blob: a76bfef38da0aa424e3e6a12bfb2003b30186e8d [file] [log] [blame]
/*
Copyright (c) 2004-2009, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
/*
This is a compiled version of Dojo, built for deployment and not for
development. To get an editable version, please visit:
http://dojotoolkit.org
for documentation and information on getting the source.
*/
if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit._KeyNavContainer"] = true;
dojo.provide("dijit._KeyNavContainer");
dojo.declare("dijit._KeyNavContainer",
[dijit._Container],
{
// summary:
// A _Container with keyboard navigation of its children.
// description:
// To use this mixin, call connectKeyNavHandlers() in
// postCreate() and call startupKeyNavChildren() in startup().
// It provides normalized keyboard and focusing code for Container
// widgets.
/*=====
// focusedChild: [protected] Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
=====*/
// tabIndex: Integer
// Tab index of the container; same as HTML tabindex attribute.
// Note then when user tabs into the container, focus is immediately
// moved to the first item in the container.
tabIndex: "0",
_keyNavCodes: {},
connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
// summary:
// Call in postCreate() to attach the keyboard handlers
// to the container.
// preKeyCodes: dojo.keys[]
// Key codes for navigating to the previous child.
// nextKeyCodes: dojo.keys[]
// Key codes for navigating to the next child.
// tags:
// protected
var keyCodes = this._keyNavCodes = {};
var prev = dojo.hitch(this, this.focusPrev);
var next = dojo.hitch(this, this.focusNext);
dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
this.connect(this.domNode, "onfocus", "_onContainerFocus");
},
startupKeyNavChildren: function(){
// summary:
// Call in startup() to set child tabindexes to -1
// tags:
// protected
dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
},
addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
// summary:
// Add a child to our _Container
dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
this._startupChild(widget);
},
focus: function(){
// summary:
// Default focus() implementation: focus the first child.
this.focusFirstChild();
},
focusFirstChild: function(){
// summary:
// Focus the first focusable child in the container.
// tags:
// protected
this.focusChild(this._getFirstFocusableChild());
},
focusNext: function(){
// summary:
// Focus the next widget or focal node (for widgets
// with multiple focal nodes) within this container.
// tags:
// protected
if(this.focusedChild && this.focusedChild.hasNextFocalNode
&& this.focusedChild.hasNextFocalNode()){
this.focusedChild.focusNext();
return;
}
var child = this._getNextFocusableChild(this.focusedChild, 1);
if(child.getFocalNodes){
this.focusChild(child, child.getFocalNodes()[0]);
}else{
this.focusChild(child);
}
},
focusPrev: function(){
// summary:
// Focus the previous widget or focal node (for widgets
// with multiple focal nodes) within this container.
// tags:
// protected
if(this.focusedChild && this.focusedChild.hasPrevFocalNode
&& this.focusedChild.hasPrevFocalNode()){
this.focusedChild.focusPrev();
return;
}
var child = this._getNextFocusableChild(this.focusedChild, -1);
if(child.getFocalNodes){
var nodes = child.getFocalNodes();
this.focusChild(child, nodes[nodes.length-1]);
}else{
this.focusChild(child);
}
},
focusChild: function(/*Widget*/ widget, /*Node?*/ node){
// summary:
// Focus widget. Optionally focus 'node' within widget.
// tags:
// protected
if(widget){
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild);
}
this.focusedChild = widget;
if(node && widget.focusFocalNode){
widget.focusFocalNode(node);
}else{
widget.focus();
}
}
},
_startupChild: function(/*Widget*/ widget){
// summary:
// Set tabindex="-1" on focusable widgets so that we
// can focus them programmatically and by clicking.
// Connect focus and blur handlers.
// tags:
// private
if(widget.getFocalNodes){
dojo.forEach(widget.getFocalNodes(), function(node){
dojo.attr(node, "tabindex", -1);
this._connectNode(node);
}, this);
}else{
var node = widget.focusNode || widget.domNode;
if(widget.isFocusable()){
dojo.attr(node, "tabindex", -1);
}
this._connectNode(node);
}
},
_connectNode: function(/*Element*/ node){
// summary:
// Monitor focus and blur events on the node
// tags:
// private
this.connect(node, "onfocus", "_onNodeFocus");
this.connect(node, "onblur", "_onNodeBlur");
},
_onContainerFocus: function(evt){
// summary:
// Handler for when the container gets focus
// description:
// Initially the container itself has a tabIndex, but when it gets
// focus, switch focus to first child...
// tags:
// private
// Note that we can't use _onFocus() because switching focus from the
// _onFocus() handler confuses the focus.js code
// (because it causes _onFocusNode() to be called recursively)
// focus bubbles on Firefox,
// so just make sure that focus has really gone to the container
if(evt.target !== this.domNode){ return; }
this.focusFirstChild();
// and then remove the container's tabIndex,
// so that tab or shift-tab will go to the fields after/before
// the container, rather than the container itself
dojo.removeAttr(this.domNode, "tabIndex");
},
_onBlur: function(evt){
// When focus is moved away the container, and it's descendant (popup) widgets,
// then restore the container's tabIndex so that user can tab to it again.
// Note that using _onBlur() so that this doesn't happen when focus is shifted
// to one of my child widgets (typically a popup)
if(this.tabIndex){
dojo.attr(this.domNode, "tabindex", this.tabIndex);
}
// TODO: this.inherited(arguments);
},
_onContainerKeypress: function(evt){
// summary:
// When a key is pressed, if it's an arrow key etc. then
// it's handled here.
// tags:
// private
if(evt.ctrlKey || evt.altKey){ return; }
var func = this._keyNavCodes[evt.charOrCode];
if(func){
func();
dojo.stopEvent(evt);
}
},
_onNodeFocus: function(evt){
// summary:
// Handler for onfocus event on a child node
// tags:
// private
// record the child that has been focused
var widget = dijit.getEnclosingWidget(evt.target);
if(widget && widget.isFocusable()){
this.focusedChild = widget;
}
dojo.stopEvent(evt);
},
_onNodeBlur: function(evt){
// summary:
// Handler for onblur event on a child node
// tags:
// private
dojo.stopEvent(evt);
},
_onChildBlur: function(/*Widget*/ widget){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
// tags:
// protected
},
_getFirstFocusableChild: function(){
// summary:
// Returns first child that can be focused
return this._getNextFocusableChild(null, 1);
},
_getNextFocusableChild: function(child, dir){
// summary:
// Returns the next or previous focusable child, compared
// to "child"
// child: Widget
// The current widget
// dir: Integer
// * 1 = after
// * -1 = before
if(child){
child = this._getSiblingOfChild(child, dir);
}
var children = this.getChildren();
for(var i=0; i < children.length; i++){
if(!child){
child = children[(dir>0) ? 0 : (children.length-1)];
}
if(child.isFocusable()){
return child;
}
child = this._getSiblingOfChild(child, dir);
}
// no focusable child found
return null;
}
}
);
}
if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.MenuItem"] = true;
dojo.provide("dijit.MenuItem");
dojo.declare("dijit.MenuItem",
[dijit._Widget, dijit._Templated, dijit._Contained],
{
// summary:
// A line item in a Menu Widget
// Make 3 columns
// icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
templateString:"<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon\" dojoAttachPoint=\"iconNode\">\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\">\n\t\t<div dojoAttachPoint=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\">\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n",
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
label: { node: "containerNode", type: "innerHTML" },
iconClass: { node: "iconNode", type: "class" }
}),
// label: String
// Menu text
label: '',
// iconClass: String
// Class to apply to DOMNode to make it display an icon.
iconClass: "",
// accelKey: String
// Text for the accelerator (shortcut) key combination.
// Note that although Menu can display accelerator keys there
// is no infrastructure to actually catch and execute these
// accelerators.
accelKey: "",
// disabled: Boolean
// If true, the menu item is disabled.
// If false, the menu item is enabled.
disabled: false,
_fillContent: function(/*DomNode*/ source){
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
if(source && !("label" in this.params)){
this.attr('label', source.innerHTML);
}
},
postCreate: function(){
dojo.setSelectable(this.domNode, false);
dojo.attr(this.containerNode, "id", this.id+"_text");
dijit.setWaiState(this.domNode, "labelledby", this.id+"_text");
},
_onHover: function(){
// summary:
// Handler when mouse is moved onto menu item
// tags:
// protected
dojo.addClass(this.domNode, 'dijitMenuItemHover');
this.getParent().onItemHover(this);
},
_onUnhover: function(){
// summary:
// Handler when mouse is moved off of menu item,
// possibly to a child menu, or maybe to a sibling
// menuitem or somewhere else entirely.
// tags:
// protected
// if we are unhovering the currently selected item
// then unselect it
dojo.removeClass(this.domNode, 'dijitMenuItemHover');
this.getParent().onItemUnhover(this);
},
_onClick: function(evt){
// summary:
// Internal handler for click events on MenuItem.
// tags:
// private
this.getParent().onItemClick(this, evt);
dojo.stopEvent(evt);
},
onClick: function(/*Event*/ evt){
// summary:
// User defined function to handle clicks
// tags:
// callback
},
focus: function(){
// summary:
// Focus on this MenuItem
try{
dijit.focus(this.focusNode);
}catch(e){
// this throws on IE (at least) in some scenarios
}
},
_onFocus: function(){
// summary:
// This is called by the focus manager when focus
// goes to this MenuItem or a child menu.
// tags:
// protected
this._setSelected(true);
// TODO: this.inherited(arguments);
},
_setSelected: function(selected){
// summary:
// Indicate that this node is the currently selected one
// tags:
// private
/***
* TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
* Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
* That's not supposed to happen, but the problem is:
* In order to allow dijit.popup's getTopPopup() work,a sub menu's popupParent
* points to the parent Menu, bypassing the parent MenuItem... thus the
* MenuItem is not in the chain of active widgets and gets a premature call to
* _onBlur()
*/
dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use attr('label', ...) instead.
// tags:
// deprecated
dojo.deprecated("dijit.MenuItem.setLabel() is deprecated. Use attr('label', ...) instead.", "", "2.0");
this.attr("label", content);
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use attr('disabled', bool) instead.
// tags:
// deprecated
dojo.deprecated("dijit.Menu.setDisabled() is deprecated. Use attr('disabled', bool) instead.", "", "2.0");
this.attr('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ value){
// summary:
// Hook for attr('disabled', ...) to work.
// Enable or disable this menu item.
this.disabled = value;
dojo[value ? "addClass" : "removeClass"](this.domNode, 'dijitMenuItemDisabled');
dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
},
_setAccelKeyAttr: function(/*String*/ value){
// summary:
// Hook for attr('accelKey', ...) to work.
// Set accelKey on this menu item.
this.accelKey=value;
this.accelKeyNode.style.display=value?"":"none";
this.accelKeyNode.innerHTML=value;
//have to use colSpan to make it work in IE
dojo.attr(this.containerNode,'colSpan',value?"1":"2");
}
});
}
if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.PopupMenuItem"] = true;
dojo.provide("dijit.PopupMenuItem");
dojo.declare("dijit.PopupMenuItem",
dijit.MenuItem,
{
_fillContent: function(){
// summary:
// When Menu is declared in markup, this code gets the menu label and
// the popup widget from the srcNodeRef.
// description:
// srcNodeRefinnerHTML contains both the menu item text and a popup widget
// The first part holds the menu item text and the second part is the popup
// example:
// | <div dojoType="dijit.PopupMenuItem">
// | <span>pick me</span>
// | <popup> ... </popup>
// | </div>
// tags:
// protected
if(this.srcNodeRef){
var nodes = dojo.query("*", this.srcNodeRef);
dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
// we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
// land now. move it to dojo.doc.body.
if(!this.popup){
var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
this.popup = dijit.byNode(node);
}
dojo.body().appendChild(this.popup.domNode);
this.popup.domNode.style.display="none";
if(this.arrowWrapper){
dojo.style(this.arrowWrapper, "visibility", "");
}
dijit.setWaiState(this.focusNode, "haspopup", "true");
},
destroyDescendants: function(){
if(this.popup){
this.popup.destroyRecursive();
delete this.popup;
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.CheckedMenuItem"] = true;
dojo.provide("dijit.CheckedMenuItem");
dojo.declare("dijit.CheckedMenuItem",
dijit.MenuItem,
{
// summary:
// A checkbox-like menu item for toggling on and off
templateString:"<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" dojoAttachPoint=\"iconNode\">\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\">\n\t</td>\n</tr>\n",
// checked: Boolean
// Our checked state
checked: false,
_setCheckedAttr: function(/*Boolean*/ checked){
// summary:
// Hook so attr('checked', bool) works.
// Sets the class and state for the check box.
dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
dijit.setWaiState(this.domNode, "checked", checked);
this.checked = checked;
},
onChange: function(/*Boolean*/ checked){
// summary:
// User defined function to handle check/uncheck events
// tags:
// callback
},
_onClick: function(/*Event*/ e){
// summary:
// Clicking this item just toggles its state
// tags:
// private
if(!this.disabled){
this.attr("checked", !this.checked);
this.onChange(this.checked);
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.MenuSeparator"] = true;
dojo.provide("dijit.MenuSeparator");
dojo.declare("dijit.MenuSeparator",
[dijit._Widget, dijit._Templated, dijit._Contained],
{
// summary:
// A line between two menu items
templateString:"<tr class=\"dijitMenuSeparator\">\n\t<td colspan=\"4\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n",
postCreate: function(){
dojo.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// Override to always return false
// tags:
// protected
return false; // Boolean
}
});
}
if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.Menu"] = true;
dojo.provide("dijit.Menu");
dojo.declare("dijit._MenuBase",
[dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
{
// summary:
// Base class for Menu and MenuBar
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// number of milliseconds before hovering (without clicking) causes the popup to automatically open.
popupDelay: 500,
startup: function(){
if(this._started){ return; }
dojo.forEach(this.getChildren(), function(child){ child.startup(); });
this.startupKeyNavChildren();
this.inherited(arguments);
},
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ closeAll){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.focusedChild._onClick(evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) tabbing into it
// 3) opening it from a parent menu (which automatically focuses it)
if(this.isActive){
this.focusChild(item);
if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
}
}
},
_onChildBlur: function(item){
// summary:
// Called when a child MenuItem becomes inactive because focus
// has been removed from the MenuItem *and* it's descendant menus.
// tags:
// private
item._setSelected(false);
// Close all popups that are open and descendants of this menu
dijit.popup.close(item.popup);
this._stopPopupTimer();
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this.isActive){
this._stopPopupTimer();
}
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
clearTimeout(this.hover_timer);
this.hover_timer = null;
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top=this; top.parentMenu; top=top.parentMenu);
return top;
},
onItemClick: function(/*Widget*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
if(item.disabled){ return false; }
this.focusChild(item);
if(item.popup){
if(!this.is_open){
this._openPopup();
}
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item.onClick(evt);
}
},
_openPopup: function(){
// summary:
// Open the popup to the side of/underneath the current menu item
// tags:
// protected
this._stopPopupTimer();
var from_item = this.focusedChild;
var popup = from_item.popup;
if(popup.isShowingNow){ return; }
popup.parentMenu = this;
var self = this;
dijit.popup.open({
parent: this,
popup: popup,
around: from_item.domNode,
orient: this._orient || (this.isLeftToRight() ? {'TR': 'TL', 'TL': 'TR'} : {'TL': 'TR', 'TR': 'TL'}),
onCancel: function(){
// called when the child menu is canceled
dijit.popup.close(popup);
from_item.focus(); // put focus back on my node
self.currentPopup = null;
},
onExecute: dojo.hitch(this, "_onDescendantExecute")
});
this.currentPopup = popup;
if(popup.focus){
// If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
// if the cursor happens to collide with the popup, it will generate an onmouseover event
// even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
// our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
setTimeout(dojo.hitch(popup, "focus"), 0);
}
},
onOpen: function(/*Event*/ e){
// summary:
// Callback when this menu is opened.
// This is called by the popup manager as notification that the menu
// was opened.
// tags:
// private
this.isShowingNow = true;
},
onClose: function(){
// summary:
// Callback when this menu is closed.
// This is called by the popup manager as notification that the menu
// was closed.
// tags:
// private
this._stopPopupTimer();
this.parentMenu = null;
this.isShowingNow = false;
this.currentPopup = null;
if(this.focusedChild){
this._onChildBlur(this.focusedChild);
this.focusedChild = null;
}
},
_onFocus: function(){
// summary:
// Called when this Menu gets focus from:
// 1) clicking it
// 2) tabbing into it
// 3) being opened by a parent menu.
// This is not called just from mouse hover.
// tags:
// protected
this.isActive = true;
dojo.addClass(this.domNode, "dijitMenuActive");
dojo.removeClass(this.domNode, "dijitMenuPassive");
this.inherited(arguments);
},
_onBlur: function(){
// summary:
// Called when focus is moved away from this Menu and it's submenus.
// tags:
// protected
this.isActive = false;
dojo.removeClass(this.domNode, "dijitMenuActive");
dojo.addClass(this.domNode, "dijitMenuPassive");
// If user blurs/clicks away from a MenuBar (or always visible Menu), then close all popped up submenus etc.
this.onClose();
this.inherited(arguments);
},
_onDescendantExecute: function(){
// summary:
// Called when submenu is clicked. Close hierarchy of menus.
// tags:
// private
this.onClose();
}
});
dojo.declare("dijit.Menu",
dijit._MenuBase,
{
// summary
// A context menu you can assign to multiple elements
// TODO: most of the code in here is just for context menu (right-click menu)
// support. In retrospect that should have been a separate class (dijit.ContextMenu).
// Split them for 2.0
constructor: function(){
this._bindings = [];
},
templateString:"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" waiRole=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n",
// targetNodeIds: [const] String[]
// Array of dom node ids of nodes to attach to.
// Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
targetNodeIds: [],
// contextMenuForWindow: [const] Boolean
// If true, right clicking anywhere on the window will cause this context menu to open.
// If false, must specify targetNodeIds.
contextMenuForWindow: false,
// leftClickToOpen: [const] Boolean
// If true, menu will open on left click instead of right click, similiar to a file menu.
leftClickToOpen: false,
// _contextMenuWithMouse: [private] Boolean
// Used to record mouse and keyboard events to determine if a context
// menu is being opened with the keyboard or the mouse.
_contextMenuWithMouse: false,
postCreate: function(){
if(this.contextMenuForWindow){
this.bindDomNode(dojo.body());
}else{
dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
}
var k = dojo.keys, l = this.isLeftToRight();
this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// Handle keyboard based menu navigation.
// tags:
// protected
if(evt.ctrlKey || evt.altKey){ return; }
switch(evt.charOrCode){
case this._openSubMenuKey:
this._moveToPopup(evt);
dojo.stopEvent(evt);
break;
case this._closeSubMenuKey:
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
dojo.stopEvent(evt);
}
break;
}
},
// thanks burstlib!
_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns the window reference of the passed iframe
// tags:
// private
var win = dijit.getDocumentWindow(dijit.Menu._iframeContentDocument(iframe_el)) ||
// Moz. TODO: is this available when defaultView isn't?
dijit.Menu._iframeContentDocument(iframe_el)['__parent__'] ||
(iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
return win; // Window
},
_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns a reference to the document object inside iframe_el
// tags:
// protected
var doc = iframe_el.contentDocument // W3
|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
|| (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
|| null;
return doc; // HTMLDocument
},
bindDomNode: function(/*String|DomNode*/ node){
// summary:
// Attach menu to given node
node = dojo.byId(node);
//TODO: this is to support context popups in Editor. Maybe this shouldn't be in dijit.Menu
var win = dijit.getDocumentWindow(node.ownerDocument);
if(node.tagName.toLowerCase()=="iframe"){
win = this._iframeContentWindow(node);
node = dojo.withGlobal(win, dojo.body);
}
// to capture these events at the top level,
// attach to document, not body
var cn = (node == dojo.body() ? dojo.doc : node);
node[this.id] = this._bindings.push([
dojo.connect(cn, (this.leftClickToOpen)?"onclick":"oncontextmenu", this, "_openMyself"),
dojo.connect(cn, "onkeydown", this, "_contextKey"),
dojo.connect(cn, "onmousedown", this, "_contextMouse")
]);
},
unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
// Detach menu from given node
var node = dojo.byId(nodeName);
if(node){
var bid = node[this.id]-1, b = this._bindings[bid];
dojo.forEach(b, dojo.disconnect);
delete this._bindings[bid];
}
},
_contextKey: function(e){
// summary:
// Code to handle popping up editor using F10 key rather than mouse
// tags:
// private
this._contextMenuWithMouse = false;
if(e.keyCode == dojo.keys.F10){
dojo.stopEvent(e);
if(e.shiftKey && e.type=="keydown"){
// FF: copying the wrong property from e will cause the system
// context menu to appear in spite of stopEvent. Don't know
// exactly which properties cause this effect.
var _e = { target: e.target, pageX: e.pageX, pageY: e.pageY };
_e.preventDefault = _e.stopPropagation = function(){};
// IE: without the delay, focus work in "open" causes the system
// context menu to appear in spite of stopEvent.
window.setTimeout(dojo.hitch(this, function(){ this._openMyself(_e); }), 1);
}
}
},
_contextMouse: function(e){
// summary:
// Helper to remember when we opened the context menu with the mouse instead
// of with the keyboard
// tags:
// private
this._contextMenuWithMouse = true;
},
_openMyself: function(/*Event*/ e){
// summary:
// Internal function for opening myself when the user
// does a right-click or something similar
// tags:
// private
if(this.leftClickToOpen&&e.button>0){
return;
}
dojo.stopEvent(e);
// Get coordinates.
// if we are opening the menu with the mouse or on safari open
// the menu at the mouse cursor
// (Safari does not have a keyboard command to open the context menu
// and we don't currently have a reliable way to determine
// _contextMenuWithMouse on Safari)
var x,y;
if(dojo.isSafari || this._contextMenuWithMouse){
x=e.pageX;
y=e.pageY;
}else{
// otherwise open near e.target
var coords = dojo.coords(e.target, true);
x = coords.x + 10;
y = coords.y + 10;
}
var self=this;
var savedFocus = dijit.getFocus(this);
function closeAndRestoreFocus(){
// user has clicked on a menu or popup
dijit.focus(savedFocus);
dijit.popup.close(self);
}
dijit.popup.open({
popup: this,
x: x,
y: y,
onExecute: closeAndRestoreFocus,
onCancel: closeAndRestoreFocus,
orient: this.isLeftToRight() ? 'L' : 'R'
});
this.focus();
this._onBlur = function(){
this.inherited('_onBlur', arguments);
// Usually the parent closes the child widget but if this is a context
// menu then there is no parent
dijit.popup.close(this);
// don't try to restore focus; user has clicked another part of the screen
// and set focus there
};
},
uninitialize: function(){
dojo.forEach(this.targetNodeIds, this.unBindDomNode, this);
this.inherited(arguments);
}
}
);
// Back-compat (TODO: remove in 2.0)
}
if(!dojo._hasResource["dojox.html.metrics"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.html.metrics"] = true;
dojo.provide("dojox.html.metrics");
(function(){
var dhm = dojox.html.metrics;
// derived from Morris John's emResized measurer
dhm.getFontMeasurements = function(){
// summary
// Returns an object that has pixel equivilents of standard font size values.
var heights = {
'1em':0, '1ex':0, '100%':0, '12pt':0, '16px':0, 'xx-small':0, 'x-small':0,
'small':0, 'medium':0, 'large':0, 'x-large':0, 'xx-large':0
};
if(dojo.isIE){
// we do a font-size fix if and only if one isn't applied already.
// NOTE: If someone set the fontSize on the HTML Element, this will kill it.
dojo.doc.documentElement.style.fontSize="100%";
}
// set up the measuring node.
var div=dojo.doc.createElement("div");
var ds = div.style;
ds.position="absolute";
ds.left="-100px";
ds.top="0";
ds.width="30px";
ds.height="1000em";
ds.border="0";
ds.margin="0";
ds.padding="0";
ds.outline="0";
ds.lineHeight="1";
ds.overflow="hidden";
dojo.body().appendChild(div);
// do the measurements.
for(var p in heights){
ds.fontSize = p;
heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000;
}
dojo.body().removeChild(div);
div = null;
return heights; // object
};
var fontMeasurements = null;
dhm.getCachedFontMeasurements = function(recalculate){
if(recalculate || !fontMeasurements){
fontMeasurements = dhm.getFontMeasurements();
}
return fontMeasurements;
};
var measuringNode = null, empty = {};
dhm.getTextBox = function(/* String */ text, /* Object */ style, /* String? */ className){
var m;
if(!measuringNode){
m = measuringNode = dojo.doc.createElement("div");
m.style.position = "absolute";
m.style.left = "-10000px";
m.style.top = "0";
dojo.body().appendChild(m);
}else{
m = measuringNode;
}
// reset styles
m.className = "";
m.style.border = "0";
m.style.margin = "0";
m.style.padding = "0";
m.style.outline = "0";
// set new style
if(arguments.length > 1 && style){
for(var i in style){
if(i in empty){ continue; }
m.style[i] = style[i];
}
}
// set classes
if(arguments.length > 2 && className){
m.className = className;
}
// take a measure
m.innerHTML = text;
return dojo.marginBox(m);
};
// determine the scrollbar sizes on load.
var scroll={ w:16, h:16 };
dhm.getScrollbar=function(){ return { w:scroll.w, h:scroll.h }; };
dhm._fontResizeNode = null;
dhm.initOnFontResize = function(interval){
var f = dhm._fontResizeNode = dojo.doc.createElement("iframe");
var fs = f.style;
fs.position = "absolute";
fs.width = "5em";
fs.height = "10em";
fs.top = "-10000px";
f.src = dojo.config["dojoBlankHtmlUrl"] || dojo.moduleUrl("dojo", "resources/blank.html");
dojo.body().appendChild(f);
if(dojo.isIE){
f.onreadystatechange = function(){
if(f.contentWindow.document.readyState == "complete"){
f.onresize = Function('window.parent.'+dojox._scopeName+'.html.metrics._fontresize()');
}
};
}else{
f.onload = function(){
f.contentWindow.onresize = Function('window.parent.'+dojox._scopeName+'.html.metrics._fontresize()');
};
}
dhm.initOnFontResize = function(){};
};
dhm.onFontResize = function(){};
dhm._fontresize = function(){
dhm.onFontResize();
}
dojo.addOnUnload(function(){
// destroy our font resize iframe if we have one
var f = dhm._fontResizeNode;
if(f){
if(dojo.isIE && f.onresize){
f.onresize = null;
}else if(f.contentWindow && f.contentWindow.onresize){
f.contentWindow.onresize = null;
}
dhm._fontResizeNode = null;
}
});
dojo.addOnLoad(function(){
// getScrollbar metrics node
try{
var n=dojo.doc.createElement("div");
n.style.cssText = "top:0;left:0;width:100px;height:100px;overflow:scroll;position:absolute;visibility:hidden;";
dojo.body().appendChild(n);
scroll.w = n.offsetWidth - n.clientWidth;
scroll.h = n.offsetHeight - n.clientHeight;
dojo.body().removeChild(n);
//
delete n;
}catch(e){}
// text size poll setup
if("fontSizeWatch" in dojo.config && !!dojo.config.fontSizeWatch){
dhm.initOnFontResize();
}
});
})();
}
if(!dojo._hasResource["dojox.grid.util"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.util"] = true;
dojo.provide("dojox.grid.util");
// summary: grid utility library
(function(){
var dgu = dojox.grid.util;
dgu.na = '...';
dgu.rowIndexTag = "gridRowIndex";
dgu.gridViewTag = "gridView";
dgu.fire = function(ob, ev, args){
var fn = ob && ev && ob[ev];
return fn && (args ? fn.apply(ob, args) : ob[ev]());
};
dgu.setStyleHeightPx = function(inElement, inHeight){
if(inHeight >= 0){
var s = inElement.style;
var v = inHeight + 'px';
if(inElement && s['height'] != v){
s['height'] = v;
}
}
};
dgu.mouseEvents = [ 'mouseover', 'mouseout', /*'mousemove',*/ 'mousedown', 'mouseup', 'click', 'dblclick', 'contextmenu' ];
dgu.keyEvents = [ 'keyup', 'keydown', 'keypress' ];
dgu.funnelEvents = function(inNode, inObject, inMethod, inEvents){
var evts = (inEvents ? inEvents : dgu.mouseEvents.concat(dgu.keyEvents));
for (var i=0, l=evts.length; i<l; i++){
inObject.connect(inNode, 'on' + evts[i], inMethod);
}
},
dgu.removeNode = function(inNode){
inNode = dojo.byId(inNode);
inNode && inNode.parentNode && inNode.parentNode.removeChild(inNode);
return inNode;
};
dgu.arrayCompare = function(inA, inB){
for(var i=0,l=inA.length; i<l; i++){
if(inA[i] != inB[i]){return false;}
}
return (inA.length == inB.length);
};
dgu.arrayInsert = function(inArray, inIndex, inValue){
if(inArray.length <= inIndex){
inArray[inIndex] = inValue;
}else{
inArray.splice(inIndex, 0, inValue);
}
};
dgu.arrayRemove = function(inArray, inIndex){
inArray.splice(inIndex, 1);
};
dgu.arraySwap = function(inArray, inI, inJ){
var cache = inArray[inI];
inArray[inI] = inArray[inJ];
inArray[inJ] = cache;
};
})();
}
if(!dojo._hasResource["dojox.grid._Scroller"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._Scroller"] = true;
dojo.provide("dojox.grid._Scroller");
(function(){
var indexInParent = function(inNode){
var i=0, n, p=inNode.parentNode;
while((n = p.childNodes[i++])){
if(n == inNode){
return i - 1;
}
}
return -1;
};
var cleanNode = function(inNode){
if(!inNode){
return;
}
var filter = function(inW){
return inW.domNode && dojo.isDescendant(inW.domNode, inNode, true);
}
var ws = dijit.registry.filter(filter);
for(var i=0, w; (w=ws[i]); i++){
w.destroy();
}
delete ws;
};
var getTagName = function(inNodeOrId){
var node = dojo.byId(inNodeOrId);
return (node && node.tagName ? node.tagName.toLowerCase() : '');
};
var nodeKids = function(inNode, inTag){
var result = [];
var i=0, n;
while((n = inNode.childNodes[i++])){
if(getTagName(n) == inTag){
result.push(n);
}
}
return result;
};
var divkids = function(inNode){
return nodeKids(inNode, 'div');
};
dojo.declare("dojox.grid._Scroller", null, {
constructor: function(inContentNodes){
this.setContentNodes(inContentNodes);
this.pageHeights = [];
this.pageNodes = [];
this.stack = [];
},
// specified
rowCount: 0, // total number of rows to manage
defaultRowHeight: 32, // default height of a row
keepRows: 100, // maximum number of rows that should exist at one time
contentNode: null, // node to contain pages
scrollboxNode: null, // node that controls scrolling
// calculated
defaultPageHeight: 0, // default height of a page
keepPages: 10, // maximum number of pages that should exists at one time
pageCount: 0,
windowHeight: 0,
firstVisibleRow: 0,
lastVisibleRow: 0,
averageRowHeight: 0, // the average height of a row
// private
page: 0,
pageTop: 0,
// init
init: function(inRowCount, inKeepRows, inRowsPerPage){
switch(arguments.length){
case 3: this.rowsPerPage = inRowsPerPage;
case 2: this.keepRows = inKeepRows;
case 1: this.rowCount = inRowCount;
}
this.defaultPageHeight = this.defaultRowHeight * this.rowsPerPage;
this.pageCount = this._getPageCount(this.rowCount, this.rowsPerPage);
this.setKeepInfo(this.keepRows);
this.invalidate();
if(this.scrollboxNode){
this.scrollboxNode.scrollTop = 0;
this.scroll(0);
this.scrollboxNode.onscroll = dojo.hitch(this, 'onscroll');
}
},
_getPageCount: function(rowCount, rowsPerPage){
return rowCount ? (Math.ceil(rowCount / rowsPerPage) || 1) : 0;
},
destroy: function(){
this.invalidateNodes();
delete this.contentNodes;
delete this.contentNode;
delete this.scrollboxNode;
},
setKeepInfo: function(inKeepRows){
this.keepRows = inKeepRows;
this.keepPages = !this.keepRows ? this.keepRows : Math.max(Math.ceil(this.keepRows / this.rowsPerPage), 2);
},
// nodes
setContentNodes: function(inNodes){
this.contentNodes = inNodes;
this.colCount = (this.contentNodes ? this.contentNodes.length : 0);
this.pageNodes = [];
for(var i=0; i<this.colCount; i++){
this.pageNodes[i] = [];
}
},
getDefaultNodes: function(){
return this.pageNodes[0] || [];
},
// updating
invalidate: function(){
this.invalidateNodes();
this.pageHeights = [];
this.height = (this.pageCount ? (this.pageCount - 1)* this.defaultPageHeight + this.calcLastPageHeight() : 0);
this.resize();
},
updateRowCount: function(inRowCount){
this.invalidateNodes();
this.rowCount = inRowCount;
// update page count, adjust document height
var oldPageCount = this.pageCount;
this.pageCount = this._getPageCount(this.rowCount, this.rowsPerPage);
if(this.pageCount < oldPageCount){
for(var i=oldPageCount-1; i>=this.pageCount; i--){
this.height -= this.getPageHeight(i);
delete this.pageHeights[i]
}
}else if(this.pageCount > oldPageCount){
this.height += this.defaultPageHeight * (this.pageCount - oldPageCount - 1) + this.calcLastPageHeight();
}
this.resize();
},
// implementation for page manager
pageExists: function(inPageIndex){
return Boolean(this.getDefaultPageNode(inPageIndex));
},
measurePage: function(inPageIndex){
var n = this.getDefaultPageNode(inPageIndex);
return (n&&n.innerHTML) ? n.offsetHeight : 0;
},
positionPage: function(inPageIndex, inPos){
for(var i=0; i<this.colCount; i++){
this.pageNodes[i][inPageIndex].style.top = inPos + 'px';
}
},
repositionPages: function(inPageIndex){
var nodes = this.getDefaultNodes();
var last = 0;
for(var i=0; i<this.stack.length; i++){
last = Math.max(this.stack[i], last);
}
//
var n = nodes[inPageIndex];
var y = (n ? this.getPageNodePosition(n) + this.getPageHeight(inPageIndex) : 0);
//
//
for(var p=inPageIndex+1; p<=last; p++){
n = nodes[p];
if(n){
//
if(this.getPageNodePosition(n) == y){
return;
}
//
this.positionPage(p, y);
}
y += this.getPageHeight(p);
}
},
installPage: function(inPageIndex){
for(var i=0; i<this.colCount; i++){
this.contentNodes[i].appendChild(this.pageNodes[i][inPageIndex]);
}
},
preparePage: function(inPageIndex, inReuseNode){
var p = (inReuseNode ? this.popPage() : null);
for(var i=0; i<this.colCount; i++){
var nodes = this.pageNodes[i];
var new_p = (p === null ? this.createPageNode() : this.invalidatePageNode(p, nodes));
new_p.pageIndex = inPageIndex;
new_p.id = (this._pageIdPrefix || "") + 'page-' + inPageIndex;
nodes[inPageIndex] = new_p;
}
},
// rendering implementation
renderPage: function(inPageIndex){
var nodes = [];
for(var i=0; i<this.colCount; i++){
nodes[i] = this.pageNodes[i][inPageIndex];
}
for(var i=0, j=inPageIndex*this.rowsPerPage; (i<this.rowsPerPage)&&(j<this.rowCount); i++, j++){
this.renderRow(j, nodes);
}
},
removePage: function(inPageIndex){
for(var i=0, j=inPageIndex*this.rowsPerPage; i<this.rowsPerPage; i++, j++){
this.removeRow(j);
}
},
destroyPage: function(inPageIndex){
for(var i=0; i<this.colCount; i++){
var n = this.invalidatePageNode(inPageIndex, this.pageNodes[i]);
if(n){
dojo.destroy(n);
}
}
},
pacify: function(inShouldPacify){
},
// pacification
pacifying: false,
pacifyTicks: 200,
setPacifying: function(inPacifying){
if(this.pacifying != inPacifying){
this.pacifying = inPacifying;
this.pacify(this.pacifying);
}
},
startPacify: function(){
this.startPacifyTicks = new Date().getTime();
},
doPacify: function(){
var result = (new Date().getTime() - this.startPacifyTicks) > this.pacifyTicks;
this.setPacifying(true);
this.startPacify();
return result;
},
endPacify: function(){
this.setPacifying(false);
},
// default sizing implementation
resize: function(){
if(this.scrollboxNode){
this.windowHeight = this.scrollboxNode.clientHeight;
}
for(var i=0; i<this.colCount; i++){
dojox.grid.util.setStyleHeightPx(this.contentNodes[i], this.height);
}
// Calculate the average row height and update the defaults (row and page).
this.needPage(this.page, this.pageTop);
var rowsOnPage = (this.page < this.pageCount - 1) ? this.rowsPerPage : ((this.rowCount % this.rowsPerPage) || this.rowsPerPage);
var pageHeight = this.getPageHeight(this.page);
this.averageRowHeight = (pageHeight > 0 && rowsOnPage > 0) ? (pageHeight / rowsOnPage) : 0;
},
calcLastPageHeight: function(){
if(!this.pageCount){
return 0;
}
var lastPage = this.pageCount - 1;
var lastPageHeight = ((this.rowCount % this.rowsPerPage)||(this.rowsPerPage)) * this.defaultRowHeight;
this.pageHeights[lastPage] = lastPageHeight;
return lastPageHeight;
},
updateContentHeight: function(inDh){
this.height += inDh;
this.resize();
},
updatePageHeight: function(inPageIndex){
if(this.pageExists(inPageIndex)){
var oh = this.getPageHeight(inPageIndex);
var h = (this.measurePage(inPageIndex))||(oh);
this.pageHeights[inPageIndex] = h;
if((h)&&(oh != h)){
this.updateContentHeight(h - oh)
this.repositionPages(inPageIndex);
}
}
},
rowHeightChanged: function(inRowIndex){
this.updatePageHeight(Math.floor(inRowIndex / this.rowsPerPage));
},
// scroller core
invalidateNodes: function(){
while(this.stack.length){
this.destroyPage(this.popPage());
}
},
createPageNode: function(){
var p = document.createElement('div');
p.style.position = 'absolute';
//p.style.width = '100%';
p.style[dojo._isBodyLtr() ? "left" : "right"] = '0';
return p;
},
getPageHeight: function(inPageIndex){
var ph = this.pageHeights[inPageIndex];
return (ph !== undefined ? ph : this.defaultPageHeight);
},
// FIXME: this is not a stack, it's a FIFO list
pushPage: function(inPageIndex){
return this.stack.push(inPageIndex);
},
popPage: function(){
return this.stack.shift();
},
findPage: function(inTop){
var i = 0, h = 0;
for(var ph = 0; i<this.pageCount; i++, h += ph){
ph = this.getPageHeight(i);
if(h + ph >= inTop){
break;
}
}
this.page = i;
this.pageTop = h;
},
buildPage: function(inPageIndex, inReuseNode, inPos){
this.preparePage(inPageIndex, inReuseNode);
this.positionPage(inPageIndex, inPos);
// order of operations is key below
this.installPage(inPageIndex);
this.renderPage(inPageIndex);
// order of operations is key above
this.pushPage(inPageIndex);
},
needPage: function(inPageIndex, inPos){
var h = this.getPageHeight(inPageIndex), oh = h;
if(!this.pageExists(inPageIndex)){
this.buildPage(inPageIndex, this.keepPages&&(this.stack.length >= this.keepPages), inPos);
h = this.measurePage(inPageIndex) || h;
this.pageHeights[inPageIndex] = h;
if(h && (oh != h)){
this.updateContentHeight(h - oh)
}
}else{
this.positionPage(inPageIndex, inPos);
}
return h;
},
onscroll: function(){
this.scroll(this.scrollboxNode.scrollTop);
},
scroll: function(inTop){
this.grid.scrollTop = inTop;
if(this.colCount){
this.startPacify();
this.findPage(inTop);
var h = this.height;
var b = this.getScrollBottom(inTop);
for(var p=this.page, y=this.pageTop; (p<this.pageCount)&&((b<0)||(y<b)); p++){
y += this.needPage(p, y);
}
this.firstVisibleRow = this.getFirstVisibleRow(this.page, this.pageTop, inTop);
this.lastVisibleRow = this.getLastVisibleRow(p - 1, y, b);
// indicates some page size has been updated
if(h != this.height){
this.repositionPages(p-1);
}
this.endPacify();
}
},
getScrollBottom: function(inTop){
return (this.windowHeight >= 0 ? inTop + this.windowHeight : -1);
},
// events
processNodeEvent: function(e, inNode){
var t = e.target;
while(t && (t != inNode) && t.parentNode && (t.parentNode.parentNode != inNode)){
t = t.parentNode;
}
if(!t || !t.parentNode || (t.parentNode.parentNode != inNode)){
return false;
}
var page = t.parentNode;
e.topRowIndex = page.pageIndex * this.rowsPerPage;
e.rowIndex = e.topRowIndex + indexInParent(t);
e.rowTarget = t;
return true;
},
processEvent: function(e){
return this.processNodeEvent(e, this.contentNode);
},
// virtual rendering interface
renderRow: function(inRowIndex, inPageNode){
},
removeRow: function(inRowIndex){
},
// page node operations
getDefaultPageNode: function(inPageIndex){
return this.getDefaultNodes()[inPageIndex];
},
positionPageNode: function(inNode, inPos){
},
getPageNodePosition: function(inNode){
return inNode.offsetTop;
},
invalidatePageNode: function(inPageIndex, inNodes){
var p = inNodes[inPageIndex];
if(p){
delete inNodes[inPageIndex];
this.removePage(inPageIndex, p);
cleanNode(p);
p.innerHTML = '';
}
return p;
},
// scroll control
getPageRow: function(inPage){
return inPage * this.rowsPerPage;
},
getLastPageRow: function(inPage){
return Math.min(this.rowCount, this.getPageRow(inPage + 1)) - 1;
},
getFirstVisibleRow: function(inPage, inPageTop, inScrollTop){
if(!this.pageExists(inPage)){
return 0;
}
var row = this.getPageRow(inPage);
var nodes = this.getDefaultNodes();
var rows = divkids(nodes[inPage]);
for(var i=0,l=rows.length; i<l && inPageTop<inScrollTop; i++, row++){
inPageTop += rows[i].offsetHeight;
}
return (row ? row - 1 : row);
},
getLastVisibleRow: function(inPage, inBottom, inScrollBottom){
if(!this.pageExists(inPage)){
return 0;
}
var nodes = this.getDefaultNodes();
var row = this.getLastPageRow(inPage);
var rows = divkids(nodes[inPage]);
for(var i=rows.length-1; i>=0 && inBottom>inScrollBottom; i--, row--){
inBottom -= rows[i].offsetHeight;
}
return row + 1;
},
findTopRow: function(inScrollTop){
var nodes = this.getDefaultNodes();
var rows = divkids(nodes[this.page]);
for(var i=0,l=rows.length,t=this.pageTop,h; i<l; i++){
h = rows[i].offsetHeight;
t += h;
if(t >= inScrollTop){
this.offset = h - (t - inScrollTop);
return i + this.page * this.rowsPerPage;
}
}
return -1;
},
findScrollTop: function(inRow){
var rowPage = Math.floor(inRow / this.rowsPerPage);
var t = 0;
for(var i=0; i<rowPage; i++){
t += this.getPageHeight(i);
}
this.pageTop = t;
this.needPage(rowPage, this.pageTop);
var nodes = this.getDefaultNodes();
var rows = divkids(nodes[rowPage]);
var r = inRow - this.rowsPerPage * rowPage;
for(var i=0,l=rows.length; i<l && i<r; i++){
t += rows[i].offsetHeight;
}
return t;
},
dummy: 0
});
})();
}
if(!dojo._hasResource["dojox.grid.cells._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.cells._base"] = true;
dojo.provide("dojox.grid.cells._base");
(function(){
var focusSelectNode = function(inNode){
try{
dojox.grid.util.fire(inNode, "focus");
dojox.grid.util.fire(inNode, "select");
}catch(e){// IE sux bad
}
};
var whenIdle = function(/*inContext, inMethod, args ...*/){
setTimeout(dojo.hitch.apply(dojo, arguments), 0);
};
var dgc = dojox.grid.cells;
dojo.declare("dojox.grid.cells._Base", null, {
// summary:
// Respresents a grid cell and contains information about column options and methods
// for retrieving cell related information.
// Each column in a grid layout has a cell object and most events and many methods
// provide access to these objects.
styles: '',
classes: '',
editable: false,
alwaysEditing: false,
formatter: null,
defaultValue: '...',
value: null,
hidden: false,
noresize: false,
//private
_valueProp: "value",
_formatPending: false,
constructor: function(inProps){
this._props = inProps || {};
dojo.mixin(this, inProps);
},
// data source
format: function(inRowIndex, inItem){
// summary:
// provides the html for a given grid cell.
// inRowIndex: int
// grid row index
// returns: html for a given grid cell
var f, i=this.grid.edit.info, d=this.get ? this.get(inRowIndex, inItem) : (this.value || this.defaultValue);
if(this.editable && (this.alwaysEditing || (i.rowIndex==inRowIndex && i.cell==this))){
return this.formatEditing(d, inRowIndex);
}else{
var v = (d != this.defaultValue && (f = this.formatter)) ? f.call(this, d, inRowIndex) : d;
return (typeof v == "undefined" ? this.defaultValue : v);
}
},
formatEditing: function(inDatum, inRowIndex){
// summary:
// formats the cell for editing
// inDatum: anything
// cell data to edit
// inRowIndex: int
// grid row index
// returns: string of html to place in grid cell
},
// utility
getNode: function(inRowIndex){
// summary:
// gets the dom node for a given grid cell.
// inRowIndex: int
// grid row index
// returns: dom node for a given grid cell
return this.view.getCellNode(inRowIndex, this.index);
},
getHeaderNode: function(){
return this.view.getHeaderCellNode(this.index);
},
getEditNode: function(inRowIndex){
return (this.getNode(inRowIndex) || 0).firstChild || 0;
},
canResize: function(){
var uw = this.unitWidth;
return uw && (uw=='auto');
},
isFlex: function(){
var uw = this.unitWidth;
return uw && dojo.isString(uw) && (uw=='auto' || uw.slice(-1)=='%');
},
// edit support
applyEdit: function(inValue, inRowIndex){
this.grid.edit.applyCellEdit(inValue, this, inRowIndex);
},
cancelEdit: function(inRowIndex){
this.grid.doCancelEdit(inRowIndex);
},
_onEditBlur: function(inRowIndex){
if(this.grid.edit.isEditCell(inRowIndex, this.index)){
//
this.grid.edit.apply();
}
},
registerOnBlur: function(inNode, inRowIndex){
if(this.commitOnBlur){
dojo.connect(inNode, "onblur", function(e){
// hack: if editor still thinks this editor is current some ms after it blurs, assume we've focused away from grid
setTimeout(dojo.hitch(this, "_onEditBlur", inRowIndex), 250);
});
}
},
//protected
needFormatNode: function(inDatum, inRowIndex){
this._formatPending = true;
whenIdle(this, "_formatNode", inDatum, inRowIndex);
},
cancelFormatNode: function(){
this._formatPending = false;
},
//private
_formatNode: function(inDatum, inRowIndex){
if(this._formatPending){
this._formatPending = false;
// make cell selectable
dojo.setSelectable(this.grid.domNode, true);
this.formatNode(this.getEditNode(inRowIndex), inDatum, inRowIndex);
}
},
//protected
formatNode: function(inNode, inDatum, inRowIndex){
// summary:
// format the editing dom node. Use when editor is a widget.
// inNode: dom node
// dom node for the editor
// inDatum: anything
// cell data to edit
// inRowIndex: int
// grid row index
if(dojo.isIE){
// IE sux bad
whenIdle(this, "focus", inRowIndex, inNode);
}else{
this.focus(inRowIndex, inNode);
}
},
dispatchEvent: function(m, e){
if(m in this){
return this[m](e);
}
},
//public
getValue: function(inRowIndex){
// summary:
// returns value entered into editor
// inRowIndex: int
// grid row index
// returns:
// value of editor
return this.getEditNode(inRowIndex)[this._valueProp];
},
setValue: function(inRowIndex, inValue){
// summary:
// set the value of the grid editor
// inRowIndex: int
// grid row index
// inValue: anything
// value of editor
var n = this.getEditNode(inRowIndex);
if(n){
n[this._valueProp] = inValue
};
},
focus: function(inRowIndex, inNode){
// summary:
// focus the grid editor
// inRowIndex: int
// grid row index
// inNode: dom node
// editor node
focusSelectNode(inNode || this.getEditNode(inRowIndex));
},
save: function(inRowIndex){
// summary:
// save editor state
// inRowIndex: int
// grid row index
this.value = this.value || this.getValue(inRowIndex);
//
},
restore: function(inRowIndex){
// summary:
// restore editor state
// inRowIndex: int
// grid row index
this.setValue(inRowIndex, this.value);
//
},
//protected
_finish: function(inRowIndex){
// summary:
// called when editing is completed to clean up editor
// inRowIndex: int
// grid row index
dojo.setSelectable(this.grid.domNode, false);
this.cancelFormatNode();
},
//public
apply: function(inRowIndex){
// summary:
// apply edit from cell editor
// inRowIndex: int
// grid row index
this.applyEdit(this.getValue(inRowIndex), inRowIndex);
this._finish(inRowIndex);
},
cancel: function(inRowIndex){
// summary:
// cancel cell edit
// inRowIndex: int
// grid row index
this.cancelEdit(inRowIndex);
this._finish(inRowIndex);
}
});
dgc._Base.markupFactory = function(node, cellDef){
var d = dojo;
var formatter = d.trim(d.attr(node, "formatter")||"");
if(formatter){
cellDef.formatter = dojo.getObject(formatter);
}
var get = d.trim(d.attr(node, "get")||"");
if(get){
cellDef.get = dojo.getObject(get);
}
var getBoolAttr = function(attr){
var value = d.trim(d.attr(node, attr)||"");
return value ? !(value.toLowerCase()=="false") : undefined;
}
cellDef.sortDesc = getBoolAttr("sortDesc");
cellDef.editable = getBoolAttr("editable");
cellDef.alwaysEditing = getBoolAttr("alwaysEditing");
cellDef.noresize = getBoolAttr("noresize");
var value = d.trim(d.attr(node, "loadingText")||d.attr(node, "defaultValue")||"");
if(value){
cellDef.defaultValue = value;
}
var getStrAttr = function(attr){
return d.trim(d.attr(node, attr)||"")||undefined;
};
cellDef.styles = getStrAttr("styles");
cellDef.headerStyles = getStrAttr("headerStyles");
cellDef.cellStyles = getStrAttr("cellStyles");
cellDef.classes = getStrAttr("classes");
cellDef.headerClasses = getStrAttr("headerClasses");
cellDef.cellClasses = getStrAttr("cellClasses");
}
dojo.declare("dojox.grid.cells.Cell", dgc._Base, {
// summary
// grid cell that provides a standard text input box upon editing
constructor: function(){
this.keyFilter = this.keyFilter;
},
// keyFilter: RegExp
// optional regex for disallowing keypresses
keyFilter: null,
formatEditing: function(inDatum, inRowIndex){
this.needFormatNode(inDatum, inRowIndex);
return '<input class="dojoxGridInput" type="text" value="' + inDatum + '">';
},
formatNode: function(inNode, inDatum, inRowIndex){
this.inherited(arguments);
// FIXME: feels too specific for this interface
this.registerOnBlur(inNode, inRowIndex);
},
doKey: function(e){
if(this.keyFilter){
var key = String.fromCharCode(e.charCode);
if(key.search(this.keyFilter) == -1){
dojo.stopEvent(e);
}
}
},
_finish: function(inRowIndex){
this.inherited(arguments);
var n = this.getEditNode(inRowIndex);
try{
dojox.grid.util.fire(n, "blur");
}catch(e){}
}
});
dgc.Cell.markupFactory = function(node, cellDef){
dgc._Base.markupFactory(node, cellDef);
var d = dojo;
var keyFilter = d.trim(d.attr(node, "keyFilter")||"");
if(keyFilter){
cellDef.keyFilter = new RegExp(keyFilter);
}
}
dojo.declare("dojox.grid.cells.RowIndex", dgc.Cell, {
name: 'Row',
postscript: function(){
this.editable = false;
},
get: function(inRowIndex){
return inRowIndex + 1;
}
});
dgc.RowIndex.markupFactory = function(node, cellDef){
dgc.Cell.markupFactory(node, cellDef);
}
dojo.declare("dojox.grid.cells.Select", dgc.Cell, {
// summary:
// grid cell that provides a standard select for editing
// options: Array
// text of each item
options: null,
// values: Array
// value for each item
values: null,
// returnIndex: Integer
// editor returns only the index of the selected option and not the value
returnIndex: -1,
constructor: function(inCell){
this.values = this.values || this.options;
},
formatEditing: function(inDatum, inRowIndex){
this.needFormatNode(inDatum, inRowIndex);
var h = [ '<select class="dojoxGridSelect">' ];
for (var i=0, o, v; ((o=this.options[i]) !== undefined)&&((v=this.values[i]) !== undefined); i++){
h.push("<option", (inDatum==v ? ' selected' : ''), ' value="' + v + '"', ">", o, "</option>");
}
h.push('</select>');
return h.join('');
},
getValue: function(inRowIndex){
var n = this.getEditNode(inRowIndex);
if(n){
var i = n.selectedIndex, o = n.options[i];
return this.returnIndex > -1 ? i : o.value || o.innerHTML;
}
}
});
dgc.Select.markupFactory = function(node, cell){
dgc.Cell.markupFactory(node, cell);
var d=dojo;
var options = d.trim(d.attr(node, "options")||"");
if(options){
var o = options.split(',');
if(o[0] != options){
cell.options = o;
}
}
var values = d.trim(d.attr(node, "values")||"");
if(values){
var v = values.split(',');
if(v[0] != values){
cell.values = v;
}
}
}
dojo.declare("dojox.grid.cells.AlwaysEdit", dgc.Cell, {
// summary:
// grid cell that is always in an editable state, regardless of grid editing state
alwaysEditing: true,
_formatNode: function(inDatum, inRowIndex){
this.formatNode(this.getEditNode(inRowIndex), inDatum, inRowIndex);
},
applyStaticValue: function(inRowIndex){
var e = this.grid.edit;
e.applyCellEdit(this.getValue(inRowIndex), this, inRowIndex);
e.start(this, inRowIndex, true);
}
});
dgc.AlwaysEdit.markupFactory = function(node, cell){
dgc.Cell.markupFactory(node, cell);
}
dojo.declare("dojox.grid.cells.Bool", dgc.AlwaysEdit, {
// summary:
// grid cell that provides a standard checkbox that is always on for editing
_valueProp: "checked",
formatEditing: function(inDatum, inRowIndex){
return '<input class="dojoxGridInput" type="checkbox"' + (inDatum ? ' checked="checked"' : '') + ' style="width: auto" />';
},
doclick: function(e){
if(e.target.tagName == 'INPUT'){
this.applyStaticValue(e.rowIndex);
}
}
});
dgc.Bool.markupFactory = function(node, cell){
dgc.AlwaysEdit.markupFactory(node, cell);
}
})();
}
if(!dojo._hasResource["dojox.grid.cells"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.cells"] = true;
dojo.provide("dojox.grid.cells");
}
if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.common"] = true;
dojo.provide("dojo.dnd.common");
dojo.dnd._isMac = navigator.appVersion.indexOf("Macintosh") >= 0;
dojo.dnd._copyKey = dojo.dnd._isMac ? "metaKey" : "ctrlKey";
dojo.dnd.getCopyKeyState = function(e) {
// summary: abstracts away the difference between selection on Mac and PC,
// and returns the state of the "copy" key to be pressed.
// e: Event: mouse event
return e[dojo.dnd._copyKey]; // Boolean
};
dojo.dnd._uniqueId = 0;
dojo.dnd.getUniqueId = function(){
// summary: returns a unique string for use with any DOM element
var id;
do{
id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
}while(dojo.byId(id));
return id;
};
dojo.dnd._empty = {};
dojo.dnd.isFormElement = function(/*Event*/ e){
// summary: returns true, if user clicked on a form element
var t = e.target;
if(t.nodeType == 3 /*TEXT_NODE*/){
t = t.parentNode;
}
return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
};
// doesn't take into account when multiple buttons are pressed
dojo.dnd._lmb = dojo.isIE ? 1 : 0; // left mouse button
dojo.dnd._isLmbPressed = dojo.isIE ?
function(e){ return e.button & 1; } : // intentional bit-and
function(e){ return e.button === 0; };
}
if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.autoscroll"] = true;
dojo.provide("dojo.dnd.autoscroll");
dojo.dnd.getViewport = function(){
// summary: returns a viewport size (visible part of the window)
// FIXME: need more docs!!
var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
if(dojo.isMozilla){
return {w: dd.clientWidth, h: w.innerHeight}; // Object
}else if(!dojo.isOpera && w.innerWidth){
return {w: w.innerWidth, h: w.innerHeight}; // Object
}else if (!dojo.isOpera && dd && dd.clientWidth){
return {w: dd.clientWidth, h: dd.clientHeight}; // Object
}else if (b.clientWidth){
return {w: b.clientWidth, h: b.clientHeight}; // Object
}
return null; // Object
};
dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
dojo.dnd.V_AUTOSCROLL_VALUE = 16;
dojo.dnd.H_AUTOSCROLL_VALUE = 16;
dojo.dnd.autoScroll = function(e){
// summary:
// a handler for onmousemove event, which scrolls the window, if
// necesary
// e: Event:
// onmousemove event
// FIXME: needs more docs!
var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
}else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
dx = dojo.dnd.H_AUTOSCROLL_VALUE;
}
if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
}else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
dy = dojo.dnd.V_AUTOSCROLL_VALUE;
}
window.scrollBy(dx, dy);
};
dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
dojo.dnd.autoScrollNodes = function(e){
// summary:
// a handler for onmousemove event, which scrolls the first avaialble
// Dom element, it falls back to dojo.dnd.autoScroll()
// e: Event:
// onmousemove event
// FIXME: needs more docs!
for(var n = e.target; n;){
if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
var s = dojo.getComputedStyle(n);
if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
var b = dojo._getContentBox(n, s), t = dojo._abs(n, true);
//
var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
if(dojo.isWebKit || dojo.isOpera){
// FIXME: this code should not be here, it should be taken into account
// either by the event fixing code, or the dojo._abs()
// FIXME: this code doesn't work on Opera 9.5 Beta
rx += dojo.body().scrollLeft, ry += dojo.body().scrollTop;
}
if(rx > 0 && rx < b.w){
if(rx < w){
dx = -w;
}else if(rx > b.w - w){
dx = w;
}
}
//
if(ry > 0 && ry < b.h){
if(ry < h){
dy = -h;
}else if(ry > b.h - h){
dy = h;
}
}
var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
n.scrollLeft = n.scrollLeft + dx;
n.scrollTop = n.scrollTop + dy;
if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
}
}
try{
n = n.parentNode;
}catch(x){
n = null;
}
}
dojo.dnd.autoScroll(e);
};
}
if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Mover"] = true;
dojo.provide("dojo.dnd.Mover");
dojo.declare("dojo.dnd.Mover", null, {
constructor: function(node, e, host){
// summary: an object, which makes a node follow the mouse,
// used as a default mover, and as a base class for custom movers
// node: Node: a node (or node's id) to be moved
// e: Event: a mouse event, which started the move;
// only pageX and pageY properties are used
// host: Object?: object which implements the functionality of the move,
// and defines proper events (onMoveStart and onMoveStop)
this.node = dojo.byId(node);
this.marginBox = {l: e.pageX, t: e.pageY};
this.mouseButton = e.button;
var h = this.host = host, d = node.ownerDocument,
firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
this.events = [
dojo.connect(d, "onmousemove", this, "onMouseMove"),
dojo.connect(d, "onmouseup", this, "onMouseUp"),
// cancel text selection and text dragging
dojo.connect(d, "ondragstart", dojo.stopEvent),
dojo.connect(d.body, "onselectstart", dojo.stopEvent),
firstEvent
];
// notify that the move has started
if(h && h.onMoveStart){
h.onMoveStart(this);
}
},
// mouse event processors
onMouseMove: function(e){
// summary: event processor for onmousemove
// e: Event: mouse event
dojo.dnd.autoScroll(e);
var m = this.marginBox;
this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY});
dojo.stopEvent(e);
},
onMouseUp: function(e){
if(dojo.isWebKit && dojo.dnd._isMac && this.mouseButton == 2 ?
e.button == 0 : this.mouseButton == e.button){
this.destroy();
}
dojo.stopEvent(e);
},
// utilities
onFirstMove: function(){
// summary: makes the node absolute; it is meant to be called only once
var s = this.node.style, l, t, h = this.host;
switch(s.position){
case "relative":
case "absolute":
// assume that left and top values are in pixels already
l = Math.round(parseFloat(s.left));
t = Math.round(parseFloat(s.top));
break;
default:
s.position = "absolute"; // enforcing the absolute mode
var m = dojo.marginBox(this.node);
// event.pageX/pageY (which we used to generate the initial
// margin box) includes padding and margin set on the body.
// However, setting the node's position to absolute and then
// doing dojo.marginBox on it *doesn't* take that additional
// space into account - so we need to subtract the combined
// padding and margin. We use getComputedStyle and
// _getMarginBox/_getContentBox to avoid the extra lookup of
// the computed style.
var b = dojo.doc.body;
var bs = dojo.getComputedStyle(b);
var bm = dojo._getMarginBox(b, bs);
var bc = dojo._getContentBox(b, bs);
l = m.l - (bc.l - bm.l);
t = m.t - (bc.t - bm.t);
break;
}
this.marginBox.l = l - this.marginBox.l;
this.marginBox.t = t - this.marginBox.t;
if(h && h.onFirstMove){
h.onFirstMove(this);
}
dojo.disconnect(this.events.pop());
},
destroy: function(){
// summary: stops the move, deletes all references, so the object can be garbage-collected
dojo.forEach(this.events, dojo.disconnect);
// undo global settings
var h = this.host;
if(h && h.onMoveStop){
h.onMoveStop(this);
}
// destroy objects
this.events = this.node = this.host = null;
}
});
}
if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Moveable"] = true;
dojo.provide("dojo.dnd.Moveable");
dojo.declare("dojo.dnd.Moveable", null, {
// object attributes (for markup)
handle: "",
delay: 0,
skip: false,
constructor: function(node, params){
// summary: an object, which makes a node moveable
// node: Node: a node (or node's id) to be moved
// params: Object: an optional object with additional parameters;
// following parameters are recognized:
// handle: Node: a node (or node's id), which is used as a mouse handle
// if omitted, the node itself is used as a handle
// delay: Number: delay move by this number of pixels
// skip: Boolean: skip move of form elements
// mover: Object: a constructor of custom Mover
this.node = dojo.byId(node);
if(!params){ params = {}; }
this.handle = params.handle ? dojo.byId(params.handle) : null;
if(!this.handle){ this.handle = this.node; }
this.delay = params.delay > 0 ? params.delay : 0;
this.skip = params.skip;
this.mover = params.mover ? params.mover : dojo.dnd.Mover;
this.events = [
dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
// cancel text selection and text dragging
dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
];
},
// markup methods
markupFactory: function(params, node){
return new dojo.dnd.Moveable(node, params);
},
// methods
destroy: function(){
// summary: stops watching for possible move, deletes all references, so the object can be garbage-collected
dojo.forEach(this.events, dojo.disconnect);
this.events = this.node = this.handle = null;
},
// mouse event processors
onMouseDown: function(e){
// summary: event processor for onmousedown, creates a Mover for the node
// e: Event: mouse event
if(this.skip && dojo.dnd.isFormElement(e)){ return; }
if(this.delay){
this.events.push(
dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
dojo.connect(this.handle, "onmouseup", this, "onMouseUp")
);
this._lastX = e.pageX;
this._lastY = e.pageY;
}else{
this.onDragDetected(e);
}
dojo.stopEvent(e);
},
onMouseMove: function(e){
// summary: event processor for onmousemove, used only for delayed drags
// e: Event: mouse event
if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
this.onMouseUp(e);
this.onDragDetected(e);
}
dojo.stopEvent(e);
},
onMouseUp: function(e){
// summary: event processor for onmouseup, used only for delayed drags
// e: Event: mouse event
for(var i = 0; i < 2; ++i){
dojo.disconnect(this.events.pop());
}
dojo.stopEvent(e);
},
onSelectStart: function(e){
// summary: event processor for onselectevent and ondragevent
// e: Event: mouse event
if(!this.skip || !dojo.dnd.isFormElement(e)){
dojo.stopEvent(e);
}
},
// local events
onDragDetected: function(/* Event */ e){
// summary: called when the drag is detected,
// responsible for creation of the mover
new this.mover(this.node, e, this);
},
onMoveStart: function(/* dojo.dnd.Mover */ mover){
// summary: called before every move operation
dojo.publish("/dnd/move/start", [mover]);
dojo.addClass(dojo.body(), "dojoMove");
dojo.addClass(this.node, "dojoMoveItem");
},
onMoveStop: function(/* dojo.dnd.Mover */ mover){
// summary: called after every move operation
dojo.publish("/dnd/move/stop", [mover]);
dojo.removeClass(dojo.body(), "dojoMove");
dojo.removeClass(this.node, "dojoMoveItem");
},
onFirstMove: function(/* dojo.dnd.Mover */ mover){
// summary: called during the very first move notification,
// can be used to initialize coordinates, can be overwritten.
// default implementation does nothing
},
onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary: called during every move notification,
// should actually move the node, can be overwritten.
this.onMoving(mover, leftTop);
var s = mover.node.style;
s.left = leftTop.l + "px";
s.top = leftTop.t + "px";
this.onMoved(mover, leftTop);
},
onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary: called before every incremental move,
// can be overwritten.
// default implementation does nothing
},
onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
// summary: called after every incremental move,
// can be overwritten.
// default implementation does nothing
}
});
}
if(!dojo._hasResource["dojox.grid._Builder"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._Builder"] = true;
dojo.provide("dojox.grid._Builder");
(function(){
var dg = dojox.grid;
var getTdIndex = function(td){
return td.cellIndex >=0 ? td.cellIndex : dojo.indexOf(td.parentNode.cells, td);
};
var getTrIndex = function(tr){
return tr.rowIndex >=0 ? tr.rowIndex : dojo.indexOf(tr.parentNode.childNodes, tr);
};
var getTr = function(rowOwner, index){
return rowOwner && ((rowOwner.rows||0)[index] || rowOwner.childNodes[index]);
};
var findTable = function(node){
for(var n=node; n && n.tagName!='TABLE'; n=n.parentNode);
return n;
};
var ascendDom = function(inNode, inWhile){
for(var n=inNode; n && inWhile(n); n=n.parentNode);
return n;
};
var makeNotTagName = function(inTagName){
var name = inTagName.toUpperCase();
return function(node){ return node.tagName != name; };
};
var rowIndexTag = dojox.grid.util.rowIndexTag;
var gridViewTag = dojox.grid.util.gridViewTag;
// base class for generating markup for the views
dg._Builder = dojo.extend(function(view){
if(view){
this.view = view;
this.grid = view.grid;
}
},{
view: null,
// boilerplate HTML
_table: '<table class="dojoxGridRowTable" border="0" cellspacing="0" cellpadding="0" role="wairole:presentation"',
// Returns the table variable as an array - and with the view width, if specified
getTableArray: function(){
var html = [this._table];
if(this.view.viewWidth){
html.push([' style="width:', this.view.viewWidth, ';"'].join(''));
}
html.push('>');
return html;
},
// generate starting tags for a cell
generateCellMarkup: function(inCell, inMoreStyles, inMoreClasses, isHeader){
var result = [], html;
var waiPrefix = dojo.isFF<3 ? "wairole:" : "";
if(isHeader){
html = ['<th tabIndex="-1" role="', waiPrefix, 'columnheader"'];
}else{
html = ['<td tabIndex="-1" role="', waiPrefix, 'gridcell"'];
}
inCell.colSpan && html.push(' colspan="', inCell.colSpan, '"');
inCell.rowSpan && html.push(' rowspan="', inCell.rowSpan, '"');
html.push(' class="dojoxGridCell ');
inCell.classes && html.push(inCell.classes, ' ');
inMoreClasses && html.push(inMoreClasses, ' ');
// result[0] => td opener, style
result.push(html.join(''));
// SLOT: result[1] => td classes
result.push('');
html = ['" idx="', inCell.index, '" style="'];
if(inMoreStyles && inMoreStyles[inMoreStyles.length-1] != ';'){
inMoreStyles += ';';
}
html.push(inCell.styles, inMoreStyles||'', inCell.hidden?'display:none;':'');
inCell.unitWidth && html.push('width:', inCell.unitWidth, ';');
// result[2] => markup
result.push(html.join(''));
// SLOT: result[3] => td style
result.push('');
html = [ '"' ];
inCell.attrs && html.push(" ", inCell.attrs);
html.push('>');
// result[4] => td postfix
result.push(html.join(''));
// SLOT: result[5] => content
result.push('');
// result[6] => td closes
result.push('</td>');
return result; // Array
},
// cell finding
isCellNode: function(inNode){
return Boolean(inNode && inNode!=dojo.doc && dojo.attr(inNode, "idx"));
},
getCellNodeIndex: function(inCellNode){
return inCellNode ? Number(dojo.attr(inCellNode, "idx")) : -1;
},
getCellNode: function(inRowNode, inCellIndex){
for(var i=0, row; row=getTr(inRowNode.firstChild, i); i++){
for(var j=0, cell; cell=row.cells[j]; j++){
if(this.getCellNodeIndex(cell) == inCellIndex){
return cell;
}
}
}
},
findCellTarget: function(inSourceNode, inTopNode){
var n = inSourceNode;
while(n && (!this.isCellNode(n) || (n.offsetParent && gridViewTag in n.offsetParent.parentNode && n.offsetParent.parentNode[gridViewTag] != this.view.id)) && (n!=inTopNode)){
n = n.parentNode;
}
return n!=inTopNode ? n : null
},
// event decoration
baseDecorateEvent: function(e){
e.dispatch = 'do' + e.type;
e.grid = this.grid;
e.sourceView = this.view;
e.cellNode = this.findCellTarget(e.target, e.rowNode);
e.cellIndex = this.getCellNodeIndex(e.cellNode);
e.cell = (e.cellIndex >= 0 ? this.grid.getCell(e.cellIndex) : null);
},
// event dispatch
findTarget: function(inSource, inTag){
var n = inSource;
while(n && (n!=this.domNode) && (!(inTag in n) || (gridViewTag in n && n[gridViewTag] != this.view.id))){
n = n.parentNode;
}
return (n != this.domNode) ? n : null;
},
findRowTarget: function(inSource){
return this.findTarget(inSource, rowIndexTag);
},
isIntraNodeEvent: function(e){
try{
return (e.cellNode && e.relatedTarget && dojo.isDescendant(e.relatedTarget, e.cellNode));
}catch(x){
// e.relatedTarget has permission problem in FF if it's an input: https://bugzilla.mozilla.org/show_bug.cgi?id=208427
return false;
}
},
isIntraRowEvent: function(e){
try{
var row = e.relatedTarget && this.findRowTarget(e.relatedTarget);
return !row && (e.rowIndex==-1) || row && (e.rowIndex==row.gridRowIndex);
}catch(x){
// e.relatedTarget on INPUT has permission problem in FF: https://bugzilla.mozilla.org/show_bug.cgi?id=208427
return false;
}
},
dispatchEvent: function(e){
if(e.dispatch in this){
return this[e.dispatch](e);
}
},
// dispatched event handlers
domouseover: function(e){
if(e.cellNode && (e.cellNode!=this.lastOverCellNode)){
this.lastOverCellNode = e.cellNode;
this.grid.onMouseOver(e);
}
this.grid.onMouseOverRow(e);
},
domouseout: function(e){
if(e.cellNode && (e.cellNode==this.lastOverCellNode) && !this.isIntraNodeEvent(e, this.lastOverCellNode)){
this.lastOverCellNode = null;
this.grid.onMouseOut(e);
if(!this.isIntraRowEvent(e)){
this.grid.onMouseOutRow(e);
}
}
},
domousedown: function(e){
if (e.cellNode)
this.grid.onMouseDown(e);
this.grid.onMouseDownRow(e)
}
});
// Produces html for grid data content. Owned by grid and used internally
// for rendering data. Override to implement custom rendering.
dg._ContentBuilder = dojo.extend(function(view){
dg._Builder.call(this, view);
},dg._Builder.prototype,{
update: function(){
this.prepareHtml();
},
// cache html for rendering data rows
prepareHtml: function(){
var defaultGet=this.grid.get, cells=this.view.structure.cells;
for(var j=0, row; (row=cells[j]); j++){
for(var i=0, cell; (cell=row[i]); i++){
cell.get = cell.get || (cell.value == undefined) && defaultGet;
cell.markup = this.generateCellMarkup(cell, cell.cellStyles, cell.cellClasses, false);
}
}
},
// time critical: generate html using cache and data source
generateHtml: function(inDataIndex, inRowIndex){
var
html = this.getTableArray(),
v = this.view,
cells = v.structure.cells,
item = this.grid.getItem(inRowIndex);
dojox.grid.util.fire(this.view, "onBeforeRow", [inRowIndex, cells]);
for(var j=0, row; (row=cells[j]); j++){
if(row.hidden || row.header){
continue;
}
html.push(!row.invisible ? '<tr>' : '<tr class="dojoxGridInvisible">');
for(var i=0, cell, m, cc, cs; (cell=row[i]); i++){
m = cell.markup, cc = cell.customClasses = [], cs = cell.customStyles = [];
// content (format can fill in cc and cs as side-effects)
m[5] = cell.format(inRowIndex, item);
// classes
m[1] = cc.join(' ');
// styles
m[3] = cs.join(';');
// in-place concat
html.push.apply(html, m);
}
html.push('</tr>');
}
html.push('</table>');
return html.join(''); // String
},
decorateEvent: function(e){
e.rowNode = this.findRowTarget(e.target);
if(!e.rowNode){return false};
e.rowIndex = e.rowNode[rowIndexTag];
this.baseDecorateEvent(e);
e.cell = this.grid.getCell(e.cellIndex);
return true; // Boolean
}
});
// Produces html for grid header content. Owned by grid and used internally
// for rendering data. Override to implement custom rendering.
dg._HeaderBuilder = dojo.extend(function(view){
this.moveable = null;
dg._Builder.call(this, view);
},dg._Builder.prototype,{
_skipBogusClicks: false,
overResizeWidth: 4,
minColWidth: 1,
update: function(){
if(this.tableMap){
this.tableMap.mapRows(this.view.structure.cells);
}else{
this.tableMap = new dg._TableMap(this.view.structure.cells);
}
},
generateHtml: function(inGetValue, inValue){
var html = this.getTableArray(), cells = this.view.structure.cells;
dojox.grid.util.fire(this.view, "onBeforeRow", [-1, cells]);
for(var j=0, row; (row=cells[j]); j++){
if(row.hidden){
continue;
}
html.push(!row.invisible ? '<tr>' : '<tr class="dojoxGridInvisible">');
for(var i=0, cell, markup; (cell=row[i]); i++){
cell.customClasses = [];
cell.customStyles = [];
if(this.view.simpleStructure){
if(cell.headerClasses){
if(cell.headerClasses.indexOf('dojoDndItem') == -1){
cell.headerClasses += ' dojoDndItem';
}
}else{
cell.headerClasses = 'dojoDndItem';
}
if(cell.attrs){
if(cell.attrs.indexOf("dndType='gridColumn'") == -1){
cell.attrs += " dndType='gridColumn_" + this.grid.id + "'";
}
}else{
cell.attrs = "dndType='gridColumn_" + this.grid.id + "'";
}
}
markup = this.generateCellMarkup(cell, cell.headerStyles, cell.headerClasses, true);
// content
markup[5] = (inValue != undefined ? inValue : inGetValue(cell));
// styles
markup[3] = cell.customStyles.join(';');
// classes
markup[1] = cell.customClasses.join(' '); //(cell.customClasses ? ' ' + cell.customClasses : '');
html.push(markup.join(''));
}
html.push('</tr>');
}
html.push('</table>');
return html.join('');
},
// event helpers
getCellX: function(e){
var x = e.layerX;
if(dojo.isMoz){
var n = ascendDom(e.target, makeNotTagName("th"));
x -= (n && n.offsetLeft) || 0;
var t = e.sourceView.getScrollbarWidth();
if(!dojo._isBodyLtr() && e.sourceView.headerNode.scrollLeft < t)
x -= t;
//x -= getProp(ascendDom(e.target, mkNotTagName("td")), "offsetLeft") || 0;
}
var n = ascendDom(e.target, function(){
if(!n || n == e.cellNode){
return false;
}
// Mozilla 1.8 (FF 1.5) has a bug that makes offsetLeft = -parent border width
// when parent has border, overflow: hidden, and is positioned
// handle this problem here ... not a general solution!
x += (n.offsetLeft < 0 ? 0 : n.offsetLeft);
return true;
});
return x;
},
// event decoration
decorateEvent: function(e){
this.baseDecorateEvent(e);
e.rowIndex = -1;
e.cellX = this.getCellX(e);
return true;
},
// event handlers
// resizing
prepareResize: function(e, mod){
do{
var i = getTdIndex(e.cellNode);
e.cellNode = (i ? e.cellNode.parentNode.cells[i+mod] : null);
e.cellIndex = (e.cellNode ? this.getCellNodeIndex(e.cellNode) : -1);
}while(e.cellNode && e.cellNode.style.display == "none");
return Boolean(e.cellNode);
},
canResize: function(e){
if(!e.cellNode || e.cellNode.colSpan > 1){
return false;
}
var cell = this.grid.getCell(e.cellIndex);
return !cell.noresize && !cell.canResize();
},
overLeftResizeArea: function(e){
if(dojo._isBodyLtr()){
return (e.cellIndex>0) && (e.cellX < this.overResizeWidth) && this.prepareResize(e, -1);
}
var t = e.cellNode && (e.cellX < this.overResizeWidth);
return t;
},
overRightResizeArea: function(e){
if(dojo._isBodyLtr()){
return e.cellNode && (e.cellX >= e.cellNode.offsetWidth - this.overResizeWidth);
}
return (e.cellIndex>0) && (e.cellX >= e.cellNode.offsetWidth - this.overResizeWidth) && this.prepareResize(e, -1);
},
domousemove: function(e){
//
if(!this.moveable){
var c = (this.overRightResizeArea(e) ? 'e-resize' : (this.overLeftResizeArea(e) ? 'w-resize' : ''));
if(c && !this.canResize(e)){
c = 'not-allowed';
}
if(dojo.isIE){
var t = e.sourceView.headerNode.scrollLeft;
e.sourceView.headerNode.style.cursor = c || ''; //'default';
e.sourceView.headerNode.scrollLeft = t;
}else{
e.sourceView.headerNode.style.cursor = c || ''; //'default';
}
if(c){
dojo.stopEvent(e);
}
}
},
domousedown: function(e){
if(!this.moveable){
if((this.overRightResizeArea(e) || this.overLeftResizeArea(e)) && this.canResize(e)){
this.beginColumnResize(e);
}else{
this.grid.onMouseDown(e);
this.grid.onMouseOverRow(e);
}
//else{
// this.beginMoveColumn(e);
//}
}
},
doclick: function(e) {
if(this._skipBogusClicks){
dojo.stopEvent(e);
return true;
}
},
// column resizing
beginColumnResize: function(e){
this.moverDiv = document.createElement("div");
dojo.style(this.moverDiv,{position: "absolute", left:0}); // to make DnD work with dir=rtl
dojo.body().appendChild(this.moverDiv);
var m = this.moveable = new dojo.dnd.Moveable(this.moverDiv);
var spanners = [], nodes = this.tableMap.findOverlappingNodes(e.cellNode);
for(var i=0, cell; (cell=nodes[i]); i++){
spanners.push({ node: cell, index: this.getCellNodeIndex(cell), width: cell.offsetWidth });
//
}
var view = e.sourceView;
var adj = dojo._isBodyLtr() ? 1 : -1;
var views = e.grid.views.views;
var followers = [];
for(var i=view.idx+adj, cView; (cView=views[i]); i=i+adj){
followers.push({ node: cView.headerNode, left: window.parseInt(cView.headerNode.style.left) });
}
var table = view.headerContentNode.firstChild;
var drag = {
scrollLeft: e.sourceView.headerNode.scrollLeft,
view: view,
node: e.cellNode,
index: e.cellIndex,
w: dojo.contentBox(e.cellNode).w,
vw: dojo.contentBox(view.headerNode).w,
table: table,
tw: dojo.contentBox(table).w,
spanners: spanners,
followers: followers
};
m.onMove = dojo.hitch(this, "doResizeColumn", drag);
dojo.connect(m, "onMoveStop", dojo.hitch(this, function(){
this.endResizeColumn(drag);
if(drag.node.releaseCapture){
drag.node.releaseCapture();
}
this.moveable.destroy();
delete this.moveable;
this.moveable = null;
}));
view.convertColPctToFixed();
if(e.cellNode.setCapture){
e.cellNode.setCapture();
}
m.onMouseDown(e);
},
doResizeColumn: function(inDrag, mover, leftTop){
var isLtr = dojo._isBodyLtr();
var deltaX = isLtr ? leftTop.l : -leftTop.l;
var w = inDrag.w + deltaX;
var vw = inDrag.vw + deltaX;
var tw = inDrag.tw + deltaX;
if(w >= this.minColWidth){
for(var i=0, s, sw; (s=inDrag.spanners[i]); i++){
sw = s.width + deltaX;
s.node.style.width = sw + 'px';
inDrag.view.setColWidth(s.index, sw);
//
}
for(var i=0, f, fl; (f=inDrag.followers[i]); i++){
fl = f.left + deltaX;
f.node.style.left = fl + 'px';
}
inDrag.node.style.width = w + 'px';
inDrag.view.setColWidth(inDrag.index, w);
inDrag.view.headerNode.style.width = vw + 'px';
inDrag.view.setColumnsWidth(tw);
if(!isLtr){
inDrag.view.headerNode.scrollLeft = inDrag.scrollLeft + deltaX;
}
}
if(inDrag.view.flexCells && !inDrag.view.testFlexCells()){
var t = findTable(inDrag.node);
t && (t.style.width = '');
}
},
endResizeColumn: function(inDrag){
dojo.destroy(this.moverDiv);
delete this.moverDiv;
this._skipBogusClicks = true;
var conn = dojo.connect(inDrag.view, "update", this, function(){
dojo.disconnect(conn);
this._skipBogusClicks = false;
});
setTimeout(dojo.hitch(inDrag.view, "update"), 50);
}
});
// Maps an html table into a structure parsable for information about cell row and col spanning.
// Used by HeaderBuilder.
dg._TableMap = dojo.extend(function(rows){
this.mapRows(rows);
},{
map: null,
mapRows: function(inRows){
// summary: Map table topography
//
// # of rows
var rowCount = inRows.length;
if(!rowCount){
return;
}
// map which columns and rows fill which cells
this.map = [];
for(var j=0, row; (row=inRows[j]); j++){
this.map[j] = [];
}
for(var j=0, row; (row=inRows[j]); j++){
for(var i=0, x=0, cell, colSpan, rowSpan; (cell=row[i]); i++){
while (this.map[j][x]){x++};
this.map[j][x] = { c: i, r: j };
rowSpan = cell.rowSpan || 1;
colSpan = cell.colSpan || 1;
for(var y=0; y<rowSpan; y++){
for(var s=0; s<colSpan; s++){
this.map[j+y][x+s] = this.map[j][x];
}
}
x += colSpan;
}
}
//this.dumMap();
},
dumpMap: function(){
for(var j=0, row, h=''; (row=this.map[j]); j++,h=''){
for(var i=0, cell; (cell=row[i]); i++){
h += cell.r + ',' + cell.c + ' ';
}
//
}
},
getMapCoords: function(inRow, inCol){
// summary: Find node's map coords by it's structure coords
for(var j=0, row; (row=this.map[j]); j++){
for(var i=0, cell; (cell=row[i]); i++){
if(cell.c==inCol && cell.r == inRow){
return { j: j, i: i };
}
//else{ };
}
}
return { j: -1, i: -1 };
},
getNode: function(inTable, inRow, inCol){
// summary: Find a node in inNode's table with the given structure coords
var row = inTable && inTable.rows[inRow];
return row && row.cells[inCol];
},
_findOverlappingNodes: function(inTable, inRow, inCol){
var nodes = [];
var m = this.getMapCoords(inRow, inCol);
//
var row = this.map[m.j];
for(var j=0, row; (row=this.map[j]); j++){
if(j == m.j){ continue; }
var rw = row[m.i];
//
var n = (rw?this.getNode(inTable, rw.r, rw.c):null);
if(n){ nodes.push(n); }
}
//
return nodes;
},
findOverlappingNodes: function(inNode){
return this._findOverlappingNodes(findTable(inNode), getTrIndex(inNode.parentNode), getTdIndex(inNode));
}
});
})();
}
if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Container"] = true;
dojo.provide("dojo.dnd.Container");
/*
Container states:
"" - normal state
"Over" - mouse over a container
Container item states:
"" - normal state
"Over" - mouse over a container item
*/
dojo.declare("dojo.dnd.Container", null, {
// summary: a Container object, which knows when mouse hovers over it,
// and over which element it hovers
// object attributes (for markup)
skipForm: false,
constructor: function(node, params){
// summary: a constructor of the Container
// node: Node: node or node's id to build the container on
// params: Object: a dict of parameters, recognized parameters are:
// creator: Function: a creator function, which takes a data item, and returns an object like that:
// {node: newNode, data: usedData, type: arrayOfStrings}
// skipForm: Boolean: don't start the drag operation, if clicked on form elements
// dropParent: Node: node or node's id to use as the parent node for dropped items
// (must be underneath the 'node' parameter in the DOM)
// _skipStartup: Boolean: skip startup(), which collects children, for deferred initialization
// (this is used in the markup mode)
this.node = dojo.byId(node);
if(!params){ params = {}; }
this.creator = params.creator || null;
this.skipForm = params.skipForm;
this.parent = params.dropParent && dojo.byId(params.dropParent);
// class-specific variables
this.map = {};
this.current = null;
// states
this.containerState = "";
dojo.addClass(this.node, "dojoDndContainer");
// mark up children
if(!(params && params._skipStartup)){
this.startup();
}
// set up events
this.events = [
dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
dojo.connect(this.node, "onmouseout", this, "onMouseOut"),
// cancel text selection and text dragging
dojo.connect(this.node, "ondragstart", this, "onSelectStart"),
dojo.connect(this.node, "onselectstart", this, "onSelectStart")
];
},
// object attributes (for markup)
creator: function(){}, // creator function, dummy at the moment
// abstract access to the map
getItem: function(/*String*/ key){
// summary: returns a data item by its key (id)
return this.map[key]; // Object
},
setItem: function(/*String*/ key, /*Object*/ data){
// summary: associates a data item with its key (id)
this.map[key] = data;
},
delItem: function(/*String*/ key){
// summary: removes a data item from the map by its key (id)
delete this.map[key];
},
forInItems: function(/*Function*/ f, /*Object?*/ o){
// summary: iterates over a data map skipping members, which
// are present in the empty object (IE and/or 3rd-party libraries).
o = o || dojo.global;
var m = this.map, e = dojo.dnd._empty;
for(var i in m){
if(i in e){ continue; }
f.call(o, m[i], i, this);
}
return o; // Object
},
clearItems: function(){
// summary: removes all data items from the map
this.map = {};
},
// methods
getAllNodes: function(){
// summary: returns a list (an array) of all valid child nodes
return dojo.query("> .dojoDndItem", this.parent); // NodeList
},
sync: function(){
// summary: synch up the node list with the data map
var map = {};
this.getAllNodes().forEach(function(node){
if(node.id){
var item = this.getItem(node.id);
if(item){
map[node.id] = item;
return;
}
}else{
node.id = dojo.dnd.getUniqueId();
}
var type = node.getAttribute("dndType"),
data = node.getAttribute("dndData");
map[node.id] = {
data: data || node.innerHTML,
type: type ? type.split(/\s*,\s*/) : ["text"]
};
}, this);
this.map = map;
return this; // self
},
insertNodes: function(data, before, anchor){
// summary: inserts an array of new nodes before/after an anchor node
// data: Array: a list of data items, which should be processed by the creator function
// before: Boolean: insert before the anchor, if true, and after the anchor otherwise
// anchor: Node: the anchor node to be used as a point of insertion
if(!this.parent.firstChild){
anchor = null;
}else if(before){
if(!anchor){
anchor = this.parent.firstChild;
}
}else{
if(anchor){
anchor = anchor.nextSibling;
}
}
if(anchor){
for(var i = 0; i < data.length; ++i){
var t = this._normalizedCreator(data[i]);
this.setItem(t.node.id, {data: t.data, type: t.type});
this.parent.insertBefore(t.node, anchor);
}
}else{
for(var i = 0; i < data.length; ++i){
var t = this._normalizedCreator(data[i]);
this.setItem(t.node.id, {data: t.data, type: t.type});
this.parent.appendChild(t.node);
}
}
return this; // self
},
destroy: function(){
// summary: prepares the object to be garbage-collected
dojo.forEach(this.events, dojo.disconnect);
this.clearItems();
this.node = this.parent = this.current = null;
},
// markup methods
markupFactory: function(params, node){
params._skipStartup = true;
return new dojo.dnd.Container(node, params);
},
startup: function(){
// summary: collects valid child items and populate the map
// set up the real parent node
if(!this.parent){
// use the standard algorithm, if not assigned
this.parent = this.node;
if(this.parent.tagName.toLowerCase() == "table"){
var c = this.parent.getElementsByTagName("tbody");
if(c && c.length){ this.parent = c[0]; }
}
}
this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
// process specially marked children
this.sync();
},
// mouse events
onMouseOver: function(e){
// summary: event processor for onmouseover
// e: Event: mouse event
var n = e.relatedTarget;
while(n){
if(n == this.node){ break; }
try{
n = n.parentNode;
}catch(x){
n = null;
}
}
if(!n){
this._changeState("Container", "Over");
this.onOverEvent();
}
n = this._getChildByEvent(e);
if(this.current == n){ return; }
if(this.current){ this._removeItemClass(this.current, "Over"); }
if(n){ this._addItemClass(n, "Over"); }
this.current = n;
},
onMouseOut: function(e){
// summary: event processor for onmouseout
// e: Event: mouse event
for(var n = e.relatedTarget; n;){
if(n == this.node){ return; }
try{
n = n.parentNode;
}catch(x){
n = null;
}
}
if(this.current){
this._removeItemClass(this.current, "Over");
this.current = null;
}
this._changeState("Container", "");
this.onOutEvent();
},
onSelectStart: function(e){
// summary: event processor for onselectevent and ondragevent
// e: Event: mouse event
if(!this.skipForm || !dojo.dnd.isFormElement(e)){
dojo.stopEvent(e);
}
},
// utilities
onOverEvent: function(){
// summary: this function is called once, when mouse is over our container
},
onOutEvent: function(){
// summary: this function is called once, when mouse is out of our container
},
_changeState: function(type, newState){
// summary: changes a named state to new state value
// type: String: a name of the state to change
// newState: String: new state
var prefix = "dojoDnd" + type;
var state = type.toLowerCase() + "State";
//dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
dojo.removeClass(this.node, prefix + this[state]);
dojo.addClass(this.node, prefix + newState);
this[state] = newState;
},
_addItemClass: function(node, type){
// summary: adds a class with prefix "dojoDndItem"
// node: Node: a node
// type: String: a variable suffix for a class name
dojo.addClass(node, "dojoDndItem" + type);
},
_removeItemClass: function(node, type){
// summary: removes a class with prefix "dojoDndItem"
// node: Node: a node
// type: String: a variable suffix for a class name
dojo.removeClass(node, "dojoDndItem" + type);
},
_getChildByEvent: function(e){
// summary: gets a child, which is under the mouse at the moment, or null
// e: Event: a mouse event
var node = e.target;
if(node){
for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
}
}
return null;
},
_normalizedCreator: function(item, hint){
// summary: adds all necessary data to the output of the user-supplied creator function
var t = (this.creator || this.defaultCreator).call(this, item, hint);
if(!dojo.isArray(t.type)){ t.type = ["text"]; }
if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
dojo.addClass(t.node, "dojoDndItem");
return t;
}
});
dojo.dnd._createNode = function(tag){
// summary: returns a function, which creates an element of given tag
// (SPAN by default) and sets its innerHTML to given text
// tag: String: a tag name or empty for SPAN
if(!tag){ return dojo.dnd._createSpan; }
return function(text){ // Function
return dojo.create(tag, {innerHTML: text}); // Node
};
};
dojo.dnd._createTrTd = function(text){
// summary: creates a TR/TD structure with given text as an innerHTML of TD
// text: String: a text for TD
var tr = dojo.create("tr");
dojo.create("td", {innerHTML: text}, tr);
return tr; // Node
};
dojo.dnd._createSpan = function(text){
// summary: creates a SPAN element with given text as its innerHTML
// text: String: a text for SPAN
return dojo.create("span", {innerHTML: text}); // Node
};
// dojo.dnd._defaultCreatorNodes: Object: a dicitionary, which maps container tag names to child tag names
dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
dojo.dnd._defaultCreator = function(node){
// summary: takes a parent node, and returns an appropriate creator function
// node: Node: a container node
var tag = node.tagName.toLowerCase();
var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
return function(item, hint){ // Function
var isObj = item && dojo.isObject(item), data, type, n;
if(isObj && item.tagName && item.nodeType && item.getAttribute){
// process a DOM node
data = item.getAttribute("dndData") || item.innerHTML;
type = item.getAttribute("dndType");
type = type ? type.split(/\s*,\s*/) : ["text"];
n = item; // this node is going to be moved rather than copied
}else{
// process a DnD item object or a string
data = (isObj && item.data) ? item.data : item;
type = (isObj && item.type) ? item.type : ["text"];
n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
}
n.id = dojo.dnd.getUniqueId();
return {node: n, data: data, type: type};
};
};
}
if(!dojo._hasResource["dojo.dnd.Selector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Selector"] = true;
dojo.provide("dojo.dnd.Selector");
/*
Container item states:
"" - an item is not selected
"Selected" - an item is selected
"Anchor" - an item is selected, and is an anchor for a "shift" selection
*/
dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, {
// summary: a Selector object, which knows how to select its children
constructor: function(node, params){
// summary: a constructor of the Selector
// node: Node: node or node's id to build the selector on
// params: Object: a dict of parameters, recognized parameters are:
// singular: Boolean
// allows selection of only one element, if true
// the rest of parameters are passed to the container
// autoSync: Boolean
// autosynchronizes the source with its list of DnD nodes,
// false by default
if(!params){ params = {}; }
this.singular = params.singular;
this.autoSync = params.autoSync;
// class-specific variables
this.selection = {};
this.anchor = null;
this.simpleSelection = false;
// set up events
this.events.push(
dojo.connect(this.node, "onmousedown", this, "onMouseDown"),
dojo.connect(this.node, "onmouseup", this, "onMouseUp"));
},
// object attributes (for markup)
singular: false, // is singular property
// methods
getSelectedNodes: function(){
// summary: returns a list (an array) of selected nodes
var t = new dojo.NodeList();
var e = dojo.dnd._empty;
for(var i in this.selection){
if(i in e){ continue; }
t.push(dojo.byId(i));
}
return t; // Array
},
selectNone: function(){
// summary: unselects all items
return this._removeSelection()._removeAnchor(); // self
},
selectAll: function(){
// summary: selects all items
this.forInItems(function(data, id){
this._addItemClass(dojo.byId(id), "Selected");
this.selection[id] = 1;
}, this);
return this._removeAnchor(); // self
},
deleteSelectedNodes: function(){
// summary: deletes all selected items
var e = dojo.dnd._empty;
for(var i in this.selection){
if(i in e){ continue; }
var n = dojo.byId(i);
this.delItem(i);
dojo.destroy(n);
}
this.anchor = null;
this.selection = {};
return this; // self
},
forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
// summary: iterates over selected items,
// see dojo.dnd.Container.forInItems() for details
o = o || dojo.global;
var s = this.selection, e = dojo.dnd._empty;
for(var i in s){
if(i in e){ continue; }
f.call(o, this.getItem(i), i, this);
}
},
sync: function(){
// summary: synch up the node list with the data map
dojo.dnd.Selector.superclass.sync.call(this);
// fix the anchor
if(this.anchor){
if(!this.getItem(this.anchor.id)){
this.anchor = null;
}
}
// fix the selection
var t = [], e = dojo.dnd._empty;
for(var i in this.selection){
if(i in e){ continue; }
if(!this.getItem(i)){
t.push(i);
}
}
dojo.forEach(t, function(i){
delete this.selection[i];
}, this);
return this; // self
},
insertNodes: function(addSelected, data, before, anchor){
// summary: inserts new data items (see Container's insertNodes method for details)
// addSelected: Boolean: all new nodes will be added to selected items, if true, no selection change otherwise
// data: Array: a list of data items, which should be processed by the creator function
// before: Boolean: insert before the anchor, if true, and after the anchor otherwise
// anchor: Node: the anchor node to be used as a point of insertion
var oldCreator = this._normalizedCreator;
this._normalizedCreator = function(item, hint){
var t = oldCreator.call(this, item, hint);
if(addSelected){
if(!this.anchor){
this.anchor = t.node;
this._removeItemClass(t.node, "Selected");
this._addItemClass(this.anchor, "Anchor");
}else if(this.anchor != t.node){
this._removeItemClass(t.node, "Anchor");
this._addItemClass(t.node, "Selected");
}
this.selection[t.node.id] = 1;
}else{
this._removeItemClass(t.node, "Selected");
this._removeItemClass(t.node, "Anchor");
}
return t;
};
dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor);
this._normalizedCreator = oldCreator;
return this; // self
},
destroy: function(){
// summary: prepares the object to be garbage-collected
dojo.dnd.Selector.superclass.destroy.call(this);
this.selection = this.anchor = null;
},
// markup methods
markupFactory: function(params, node){
params._skipStartup = true;
return new dojo.dnd.Selector(node, params);
},
// mouse events
onMouseDown: function(e){
// summary: event processor for onmousedown
// e: Event: mouse event
if(this.autoSync){ this.sync(); }
if(!this.current){ return; }
if(!this.singular && !dojo.dnd.getCopyKeyState(e) && !e.shiftKey && (this.current.id in this.selection)){
this.simpleSelection = true;
if(e.button === dojo.dnd._lmb){
// accept the left button and stop the event
// for IE we don't stop event when multiple buttons are pressed
dojo.stopEvent(e);
}
return;
}
if(!this.singular && e.shiftKey){
if(!dojo.dnd.getCopyKeyState(e)){
this._removeSelection();
}
var c = this.getAllNodes();
if(c.length){
if(!this.anchor){
this.anchor = c[0];
this._addItemClass(this.anchor, "Anchor");
}
this.selection[this.anchor.id] = 1;
if(this.anchor != this.current){
var i = 0;
for(; i < c.length; ++i){
var node = c[i];
if(node == this.anchor || node == this.current){ break; }
}
for(++i; i < c.length; ++i){
var node = c[i];
if(node == this.anchor || node == this.current){ break; }
this._addItemClass(node, "Selected");
this.selection[node.id] = 1;
}
this._addItemClass(this.current, "Selected");
this.selection[this.current.id] = 1;
}
}
}else{
if(this.singular){
if(this.anchor == this.current){
if(dojo.dnd.getCopyKeyState(e)){
this.selectNone();
}
}else{
this.selectNone();
this.anchor = this.current;
this._addItemClass(this.anchor, "Anchor");
this.selection[this.current.id] = 1;
}
}else{
if(dojo.dnd.getCopyKeyState(e)){
if(this.anchor == this.current){
delete this.selection[this.anchor.id];
this._removeAnchor();
}else{
if(this.current.id in this.selection){
this._removeItemClass(this.current, "Selected");
delete this.selection[this.current.id];
}else{
if(this.anchor){
this._removeItemClass(this.anchor, "Anchor");
this._addItemClass(this.anchor, "Selected");
}
this.anchor = this.current;
this._addItemClass(this.current, "Anchor");
this.selection[this.current.id] = 1;
}
}
}else{
if(!(this.current.id in this.selection)){
this.selectNone();
this.anchor = this.current;
this._addItemClass(this.current, "Anchor");
this.selection[this.current.id] = 1;
}
}
}
}
dojo.stopEvent(e);
},
onMouseUp: function(e){
// summary: event processor for onmouseup
// e: Event: mouse event
if(!this.simpleSelection){ return; }
this.simpleSelection = false;
this.selectNone();
if(this.current){
this.anchor = this.current;
this._addItemClass(this.anchor, "Anchor");
this.selection[this.current.id] = 1;
}
},
onMouseMove: function(e){
// summary: event processor for onmousemove
// e: Event: mouse event
this.simpleSelection = false;
},
// utilities
onOverEvent: function(){
// summary: this function is called once, when mouse is over our container
this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove");
},
onOutEvent: function(){
// summary: this function is called once, when mouse is out of our container
dojo.disconnect(this.onmousemoveEvent);
delete this.onmousemoveEvent;
},
_removeSelection: function(){
// summary: unselects all items
var e = dojo.dnd._empty;
for(var i in this.selection){
if(i in e){ continue; }
var node = dojo.byId(i);
if(node){ this._removeItemClass(node, "Selected"); }
}
this.selection = {};
return this; // self
},
_removeAnchor: function(){
if(this.anchor){
this._removeItemClass(this.anchor, "Anchor");
this.anchor = null;
}
return this; // self
}
});
}
if(!dojo._hasResource["dojo.dnd.Avatar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Avatar"] = true;
dojo.provide("dojo.dnd.Avatar");
dojo.declare("dojo.dnd.Avatar", null, {
// summary: an object, which represents transferred DnD items visually
// manager: Object: a DnD manager object
constructor: function(manager){
this.manager = manager;
this.construct();
},
// methods
construct: function(){
// summary: a constructor function;
// it is separate so it can be (dynamically) overwritten in case of need
var a = dojo.create("table", {
"class": "dojoDndAvatar",
style: {
position: "absolute",
zIndex: "1999",
margin: "0px"
}
}),
b = dojo.create("tbody", null, a),
tr = dojo.create("tr", null, b),
td = dojo.create("td", {
innerHTML: this._generateText()
}, tr),
k = Math.min(5, this.manager.nodes.length), i = 0,
source = this.manager.source, node;
// we have to set the opacity on IE only after the node is live
dojo.attr(tr, {
"class": "dojoDndAvatarHeader",
style: {opacity: 0.9}
});
for(; i < k; ++i){
if(source.creator){
// create an avatar representation of the node
node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
}else{
// or just clone the node and hope it works
node = this.manager.nodes[i].cloneNode(true);
if(node.tagName.toLowerCase() == "tr"){
// insert extra table nodes
var table = dojo.create("table"),
tbody = dojo.create("tbody", null, table);
tbody.appendChild(node);
node = table;
}
}
node.id = "";
tr = dojo.create("tr", null, b);
td = dojo.create("td", null, tr);
td.appendChild(node);
dojo.attr(tr, {
"class": "dojoDndAvatarItem",
style: {opacity: (9 - i) / 10}
});
}
this.node = a;
},
destroy: function(){
// summary: a desctructor for the avatar, called to remove all references so it can be garbage-collected
dojo.destroy(this.node);
this.node = false;
},
update: function(){
// summary: updates the avatar to reflect the current DnD state
dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
// replace text
dojo.query("tr.dojoDndAvatarHeader td", this.node).forEach(function(node){
node.innerHTML = this._generateText();
}, this);
},
_generateText: function(){
// summary: generates a proper text to reflect copying or moving of items
return this.manager.nodes.length.toString();
}
});
}
if(!dojo._hasResource["dojo.dnd.Manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Manager"] = true;
dojo.provide("dojo.dnd.Manager");
dojo.declare("dojo.dnd.Manager", null, {
// summary: the manager of DnD operations (usually a singleton)
constructor: function(){
this.avatar = null;
this.source = null;
this.nodes = [];
this.copy = true;
this.target = null;
this.canDropFlag = false;
this.events = [];
},
// avatar's offset from the mouse
OFFSET_X: 16,
OFFSET_Y: 16,
// methods
overSource: function(source){
// summary: called when a source detected a mouse-over conditiion
// source: Object: the reporter
if(this.avatar){
this.target = (source && source.targetState != "Disabled") ? source : null;
this.canDropFlag = Boolean(this.target);
this.avatar.update();
}
dojo.publish("/dnd/source/over", [source]);
},
outSource: function(source){
// summary: called when a source detected a mouse-out conditiion
// source: Object: the reporter
if(this.avatar){
if(this.target == source){
this.target = null;
this.canDropFlag = false;
this.avatar.update();
dojo.publish("/dnd/source/over", [null]);
}
}else{
dojo.publish("/dnd/source/over", [null]);
}
},
startDrag: function(source, nodes, copy){
// summary: called to initiate the DnD operation
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
this.source = source;
this.nodes = nodes;
this.copy = Boolean(copy); // normalizing to true boolean
this.avatar = this.makeAvatar();
dojo.body().appendChild(this.avatar.node);
dojo.publish("/dnd/start", [source, nodes, this.copy]);
this.events = [
dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"),
dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"),
dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"),
// cancel text selection and text dragging
dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent),
dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
];
var c = "dojoDnd" + (copy ? "Copy" : "Move");
dojo.addClass(dojo.body(), c);
},
canDrop: function(flag){
// summary: called to notify if the current target can accept items
var canDropFlag = Boolean(this.target && flag);
if(this.canDropFlag != canDropFlag){
this.canDropFlag = canDropFlag;
this.avatar.update();
}
},
stopDrag: function(){
// summary: stop the DnD in progress
dojo.removeClass(dojo.body(), "dojoDndCopy");
dojo.removeClass(dojo.body(), "dojoDndMove");
dojo.forEach(this.events, dojo.disconnect);
this.events = [];
this.avatar.destroy();
this.avatar = null;
this.source = this.target = null;
this.nodes = [];
},
makeAvatar: function(){
// summary: makes the avatar, it is separate to be overwritten dynamically, if needed
return new dojo.dnd.Avatar(this);
},
updateAvatar: function(){
// summary: updates the avatar, it is separate to be overwritten dynamically, if needed
this.avatar.update();
},
// mouse event processors
onMouseMove: function(e){
// summary: event processor for onmousemove
// e: Event: mouse event
var a = this.avatar;
if(a){
dojo.dnd.autoScrollNodes(e);
//dojo.dnd.autoScroll(e);
var s = a.node.style;
s.left = (e.pageX + this.OFFSET_X) + "px";
s.top = (e.pageY + this.OFFSET_Y) + "px";
var copy = Boolean(this.source.copyState(dojo.dnd.getCopyKeyState(e)));
if(this.copy != copy){
this._setCopyStatus(copy);
}
}
},
onMouseUp: function(e){
// summary: event processor for onmouseup
// e: Event: mouse event
if(this.avatar){
if(this.target && this.canDropFlag){
var copy = Boolean(this.source.copyState(dojo.dnd.getCopyKeyState(e))),
params = [this.source, this.nodes, copy, this.target];
dojo.publish("/dnd/drop/before", params);
dojo.publish("/dnd/drop", params);
}else{
dojo.publish("/dnd/cancel");
}
this.stopDrag();
}
},
// keyboard event processors
onKeyDown: function(e){
// summary: event processor for onkeydown:
// watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
// e: Event: keyboard event
if(this.avatar){
switch(e.keyCode){
case dojo.keys.CTRL:
var copy = Boolean(this.source.copyState(true));
if(this.copy != copy){
this._setCopyStatus(copy);
}
break;
case dojo.keys.ESCAPE:
dojo.publish("/dnd/cancel");
this.stopDrag();
break;
}
}
},
onKeyUp: function(e){
// summary: event processor for onkeyup, watching for CTRL for copy/move status
// e: Event: keyboard event
if(this.avatar && e.keyCode == dojo.keys.CTRL){
var copy = Boolean(this.source.copyState(false));
if(this.copy != copy){
this._setCopyStatus(copy);
}
}
},
// utilities
_setCopyStatus: function(copy){
// summary: changes the copy status
// copy: Boolean: the copy status
this.copy = copy;
this.source._markDndStatus(this.copy);
this.updateAvatar();
dojo.removeClass(dojo.body(), "dojoDnd" + (this.copy ? "Move" : "Copy"));
dojo.addClass(dojo.body(), "dojoDnd" + (this.copy ? "Copy" : "Move"));
}
});
// summary: the manager singleton variable, can be overwritten, if needed
dojo.dnd._manager = null;
dojo.dnd.manager = function(){
// summary: returns the current DnD manager, creates one if it is not created yet
if(!dojo.dnd._manager){
dojo.dnd._manager = new dojo.dnd.Manager();
}
return dojo.dnd._manager; // Object
};
}
if(!dojo._hasResource["dojo.dnd.Source"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.dnd.Source"] = true;
dojo.provide("dojo.dnd.Source");
/*
Container property:
"Horizontal"- if this is the horizontal container
Source states:
"" - normal state
"Moved" - this source is being moved
"Copied" - this source is being copied
Target states:
"" - normal state
"Disabled" - the target cannot accept an avatar
Target anchor state:
"" - item is not selected
"Before" - insert point is before the anchor
"After" - insert point is after the anchor
*/
/*=====
dojo.dnd.__SourceArgs = function(){
// summary:
// a dict of parameters for DnD Source configuration. Note that any
// property on Source elements may be configured, but this is the
// short-list
// isSource: Boolean?
// can be used as a DnD source. Defaults to true.
// accept: Array?
// list of accepted types (text strings) for a target; defaults to
// ["text"]
// autoSync: Boolean
// if true refreshes the node list on every operation; false by default
// copyOnly: Boolean?
// copy items, if true, use a state of Ctrl key otherwise,
// see selfCopy and selfAccept for more details
// delay: Number
// the move delay in pixels before detecting a drag; 0 by default
// horizontal: Boolean?
// a horizontal container, if true, vertical otherwise or when omitted
// selfCopy: Boolean?
// copy items by default when dropping on itself,
// false by default, works only if copyOnly is true
// selfAccept: Boolean?
// accept its own items when copyOnly is true,
// true by default, works only if copyOnly is true
// withHandles: Boolean?
// allows dragging only by handles, false by default
this.isSource = isSource;
this.accept = accept;
this.autoSync = autoSync;
this.copyOnly = copyOnly;
this.delay = delay;
this.horizontal = horizontal;
this.selfCopy = selfCopy;
this.selfAccept = selfAccept;
this.withHandles = withHandles;
}
=====*/
dojo.declare("dojo.dnd.Source", dojo.dnd.Selector, {
// summary: a Source object, which can be used as a DnD source, or a DnD target
// object attributes (for markup)
isSource: true,
horizontal: false,
copyOnly: false,
selfCopy: false,
selfAccept: true,
skipForm: false,
withHandles: false,
autoSync: false,
delay: 0, // pixels
accept: ["text"],
constructor: function(/*DOMNode|String*/node, /*dojo.dnd.__SourceArgs?*/params){
// summary:
// a constructor of the Source
// node:
// node or node's id to build the source on
// params:
// any property of this class may be configured via the params
// object which is mixed-in to the `dojo.dnd.Source` instance
dojo.mixin(this, dojo.mixin({}, params));
var type = this.accept;
if(type.length){
this.accept = {};
for(var i = 0; i < type.length; ++i){
this.accept[type[i]] = 1;
}
}
// class-specific variables
this.isDragging = false;
this.mouseDown = false;
this.targetAnchor = null;
this.targetBox = null;
this.before = true;
this._lastX = 0;
this._lastY = 0;
// states
this.sourceState = "";
if(this.isSource){
dojo.addClass(this.node, "dojoDndSource");
}
this.targetState = "";
if(this.accept){
dojo.addClass(this.node, "dojoDndTarget");
}
if(this.horizontal){
dojo.addClass(this.node, "dojoDndHorizontal");
}
// set up events
this.topics = [
dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
dojo.subscribe("/dnd/start", this, "onDndStart"),
dojo.subscribe("/dnd/drop", this, "onDndDrop"),
dojo.subscribe("/dnd/cancel", this, "onDndCancel")
];
},
// methods
checkAcceptance: function(source, nodes){
// summary: checks, if the target can accept nodes from this source
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
if(this == source){
return !this.copyOnly || this.selfAccept;
}
for(var i = 0; i < nodes.length; ++i){
var type = source.getItem(nodes[i].id).type;
// type instanceof Array
var flag = false;
for(var j = 0; j < type.length; ++j){
if(type[j] in this.accept){
flag = true;
break;
}
}
if(!flag){
return false; // Boolean
}
}
return true; // Boolean
},
copyState: function(keyPressed, self){
// summary: Returns true, if we need to copy items, false to move.
// It is separated to be overwritten dynamically, if needed.
// keyPressed: Boolean: the "copy" was pressed
// self: Boolean?: optional flag, which means that we are about to drop on itself
if(keyPressed){ return true; }
if(arguments.length < 2){
self = this == dojo.dnd.manager().target;
}
if(self){
if(this.copyOnly){
return this.selfCopy;
}
}else{
return this.copyOnly;
}
return false; // Boolean
},
destroy: function(){
// summary: prepares the object to be garbage-collected
dojo.dnd.Source.superclass.destroy.call(this);
dojo.forEach(this.topics, dojo.unsubscribe);
this.targetAnchor = null;
},
// markup methods
markupFactory: function(params, node){
params._skipStartup = true;
return new dojo.dnd.Source(node, params);
},
// mouse event processors
onMouseMove: function(e){
// summary: event processor for onmousemove
// e: Event: mouse event
if(this.isDragging && this.targetState == "Disabled"){ return; }
dojo.dnd.Source.superclass.onMouseMove.call(this, e);
var m = dojo.dnd.manager();
if(this.isDragging){
// calculate before/after
var before = false;
if(this.current){
if(!this.targetBox || this.targetAnchor != this.current){
this.targetBox = {
xy: dojo.coords(this.current, true),
w: this.current.offsetWidth,
h: this.current.offsetHeight
};
}
if(this.horizontal){
before = (e.pageX - this.targetBox.xy.x) < (this.targetBox.w / 2);
}else{
before = (e.pageY - this.targetBox.xy.y) < (this.targetBox.h / 2);
}
}
if(this.current != this.targetAnchor || before != this.before){
this._markTargetAnchor(before);
m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
}
}else{
if(this.mouseDown && this.isSource &&
(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay)){
var nodes = this.getSelectedNodes();
if(nodes.length){
m.startDrag(this, nodes, this.copyState(dojo.dnd.getCopyKeyState(e), true));
}
}
}
},
onMouseDown: function(e){
// summary: event processor for onmousedown
// e: Event: mouse event
if(!this.mouseDown && this._legalMouseDown(e) && (!this.skipForm || !dojo.dnd.isFormElement(e))){
this.mouseDown = true;
this._lastX = e.pageX;
this._lastY = e.pageY;
dojo.dnd.Source.superclass.onMouseDown.call(this, e);
}
},
onMouseUp: function(e){
// summary: event processor for onmouseup
// e: Event: mouse event
if(this.mouseDown){
this.mouseDown = false;
dojo.dnd.Source.superclass.onMouseUp.call(this, e);
}
},
// topic event processors
onDndSourceOver: function(source){
// summary: topic event processor for /dnd/source/over, called when detected a current source
// source: Object: the source which has the mouse over it
if(this != source){
this.mouseDown = false;
if(this.targetAnchor){
this._unmarkTargetAnchor();
}
}else if(this.isDragging){
var m = dojo.dnd.manager();
m.canDrop(this.targetState != "Disabled" && (!this.current || m.source != this || !(this.current.id in this.selection)));
}
},
onDndStart: function(source, nodes, copy){
// summary: topic event processor for /dnd/start, called to initiate the DnD operation
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
if(this.autoSync){ this.sync(); }
if(this.isSource){
this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
}
var accepted = this.accept && this.checkAcceptance(source, nodes);
this._changeState("Target", accepted ? "" : "Disabled");
if(this == source){
dojo.dnd.manager().overSource(this);
}
this.isDragging = true;
},
onDndDrop: function(source, nodes, copy, target){
// summary: topic event processor for /dnd/drop, called to finish the DnD operation
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
// target: Object: the target which accepts items
if(this == target){
// this one is for us => move nodes!
this.onDrop(source, nodes, copy);
}
this.onDndCancel();
},
onDndCancel: function(){
// summary: topic event processor for /dnd/cancel, called to cancel the DnD operation
if(this.targetAnchor){
this._unmarkTargetAnchor();
this.targetAnchor = null;
}
this.before = true;
this.isDragging = false;
this.mouseDown = false;
this._changeState("Source", "");
this._changeState("Target", "");
},
// local events
onDrop: function(source, nodes, copy){
// summary: called only on the current target, when drop is performed
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
if(this != source){
this.onDropExternal(source, nodes, copy);
}else{
this.onDropInternal(nodes, copy);
}
},
onDropExternal: function(source, nodes, copy){
// summary: called only on the current target, when drop is performed
// from an external source
// source: Object: the source which provides items
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
var oldCreator = this._normalizedCreator;
// transferring nodes from the source to the target
if(this.creator){
// use defined creator
this._normalizedCreator = function(node, hint){
return oldCreator.call(this, source.getItem(node.id).data, hint);
};
}else{
// we have no creator defined => move/clone nodes
if(copy){
// clone nodes
this._normalizedCreator = function(node, hint){
var t = source.getItem(node.id);
var n = node.cloneNode(true);
n.id = dojo.dnd.getUniqueId();
return {node: n, data: t.data, type: t.type};
};
}else{
// move nodes
this._normalizedCreator = function(node, hint){
var t = source.getItem(node.id);
source.delItem(node.id);
return {node: node, data: t.data, type: t.type};
};
}
}
this.selectNone();
if(!copy && !this.creator){
source.selectNone();
}
this.insertNodes(true, nodes, this.before, this.current);
if(!copy && this.creator){
source.deleteSelectedNodes();
}
this._normalizedCreator = oldCreator;
},
onDropInternal: function(nodes, copy){
// summary: called only on the current target, when drop is performed
// from the same target/source
// nodes: Array: the list of transferred items
// copy: Boolean: copy items, if true, move items otherwise
var oldCreator = this._normalizedCreator;
// transferring nodes within the single source
if(this.current && this.current.id in this.selection){
// do nothing
return;
}
if(copy){
if(this.creator){
// create new copies of data items
this._normalizedCreator = function(node, hint){
return oldCreator.call(this, this.getItem(node.id).data, hint);
};
}else{
// clone nodes
this._normalizedCreator = function(node, hint){
var t = this.getItem(node.id);
var n = node.cloneNode(true);
n.id = dojo.dnd.getUniqueId();
return {node: n, data: t.data, type: t.type};
};
}
}else{
// move nodes
if(!this.current){
// do nothing
return;
}
this._normalizedCreator = function(node, hint){
var t = this.getItem(node.id);
return {node: node, data: t.data, type: t.type};
};
}
this._removeSelection();
this.insertNodes(true, nodes, this.before, this.current);
this._normalizedCreator = oldCreator;
},
onDraggingOver: function(){
// summary: called during the active DnD operation, when items
// are dragged over this target, and it is not disabled
},
onDraggingOut: function(){
// summary: called during the active DnD operation, when items
// are dragged away from this target, and it is not disabled
},
// utilities
onOverEvent: function(){
// summary: this function is called once, when mouse is over our container
dojo.dnd.Source.superclass.onOverEvent.call(this);
dojo.dnd.manager().overSource(this);
if(this.isDragging && this.targetState != "Disabled"){
this.onDraggingOver();
}
},
onOutEvent: function(){
// summary: this function is called once, when mouse is out of our container
dojo.dnd.Source.superclass.onOutEvent.call(this);
dojo.dnd.manager().outSource(this);
if(this.isDragging && this.targetState != "Disabled"){
this.onDraggingOut();
}
},
_markTargetAnchor: function(before){
// summary: assigns a class to the current target anchor based on "before" status
// before: Boolean: insert before, if true, after otherwise
if(this.current == this.targetAnchor && this.before == before){ return; }
if(this.targetAnchor){
this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
}
this.targetAnchor = this.current;
this.targetBox = null;
this.before = before;
if(this.targetAnchor){
this._addItemClass(this.targetAnchor, this.before ? "Before" : "After");
}
},
_unmarkTargetAnchor: function(){
// summary: removes a class of the current target anchor based on "before" status
if(!this.targetAnchor){ return; }
this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
this.targetAnchor = null;
this.targetBox = null;
this.before = true;
},
_markDndStatus: function(copy){
// summary: changes source's state based on "copy" status
this._changeState("Source", copy ? "Copied" : "Moved");
},
_legalMouseDown: function(e){
// summary: checks if user clicked on "approved" items
// e: Event: mouse event
// accept only the left mouse button
if(!dojo.dnd._isLmbPressed(e)){ return false; }
if(!this.withHandles){ return true; }
// check for handles
for(var node = e.target; node && node !== this.node; node = node.parentNode){
if(dojo.hasClass(node, "dojoDndHandle")){ return true; }
if(dojo.hasClass(node, "dojoDndItem")){ break; }
}
return false; // Boolean
}
});
dojo.declare("dojo.dnd.Target", dojo.dnd.Source, {
// summary: a Target object, which can be used as a DnD target
constructor: function(node, params){
// summary: a constructor of the Target --- see the Source constructor for details
this.isSource = false;
dojo.removeClass(this.node, "dojoDndSource");
},
// markup methods
markupFactory: function(params, node){
params._skipStartup = true;
return new dojo.dnd.Target(node, params);
}
});
dojo.declare("dojo.dnd.AutoSource", dojo.dnd.Source, {
// summary: a source, which syncs its DnD nodes by default
constructor: function(node, params){
// summary: a constructor of the AutoSource --- see the Source constructor for details
this.autoSync = true;
},
// markup methods
markupFactory: function(params, node){
params._skipStartup = true;
return new dojo.dnd.AutoSource(node, params);
}
});
}
if(!dojo._hasResource["dojox.grid._View"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._View"] = true;
dojo.provide("dojox.grid._View");
(function(){
// private
var getStyleText = function(inNode, inStyleText){
return inNode.style.cssText == undefined ? inNode.getAttribute("style") : inNode.style.cssText;
};
// public
dojo.declare('dojox.grid._View', [dijit._Widget, dijit._Templated], {
// summary:
// A collection of grid columns. A grid is comprised of a set of views that stack horizontally.
// Grid creates views automatically based on grid's layout structure.
// Users should typically not need to access individual views directly.
//
// defaultWidth: String
// Default width of the view
defaultWidth: "18em",
// viewWidth: String
// Width for the view, in valid css unit
viewWidth: "",
templateString:"<div class=\"dojoxGridView\" role=\"presentation\">\n\t<div class=\"dojoxGridHeader\" dojoAttachPoint=\"headerNode\" role=\"presentation\">\n\t\t<div dojoAttachPoint=\"headerNodeContainer\" style=\"width:9000em\" role=\"presentation\">\n\t\t\t<div dojoAttachPoint=\"headerContentNode\" role=\"presentation\"></div>\n\t\t</div>\n\t</div>\n\t<input type=\"checkbox\" class=\"dojoxGridHiddenFocus\" dojoAttachPoint=\"hiddenFocusNode\" />\n\t<input type=\"checkbox\" class=\"dojoxGridHiddenFocus\" />\n\t<div class=\"dojoxGridScrollbox\" dojoAttachPoint=\"scrollboxNode\" role=\"presentation\">\n\t\t<div class=\"dojoxGridContent\" dojoAttachPoint=\"contentNode\" hidefocus=\"hidefocus\" role=\"presentation\"></div>\n\t</div>\n</div>\n",
themeable: false,
classTag: 'dojoxGrid',
marginBottom: 0,
rowPad: 2,
// _togglingColumn: int
// Width of the column being toggled (-1 for none)
_togglingColumn: -1,
postMixInProperties: function(){
this.rowNodes = [];
},
postCreate: function(){
this.connect(this.scrollboxNode,"onscroll","doscroll");
dojox.grid.util.funnelEvents(this.contentNode, this, "doContentEvent", [ 'mouseover', 'mouseout', 'click', 'dblclick', 'contextmenu', 'mousedown' ]);
dojox.grid.util.funnelEvents(this.headerNode, this, "doHeaderEvent", [ 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'click', 'contextmenu' ]);
this.content = new dojox.grid._ContentBuilder(this);
this.header = new dojox.grid._HeaderBuilder(this);
//BiDi: in RTL case, style width='9000em' causes scrolling problem in head node
if(!dojo._isBodyLtr()){
this.headerNodeContainer.style.width = "";
}
},
destroy: function(){
dojo.destroy(this.headerNode);
delete this.headerNode;
dojo.forEach(this.rowNodes, dojo.destroy);
this.rowNodes = [];
if(this.source){
this.source.destroy();
}
this.inherited(arguments);
},
// focus
focus: function(){
if(dojo.isWebKit || dojo.isOpera){
this.hiddenFocusNode.focus();
}else{
this.scrollboxNode.focus();
}
},
setStructure: function(inStructure){
var vs = (this.structure = inStructure);
// FIXME: similar logic is duplicated in layout
if(vs.width && !isNaN(vs.width)){
this.viewWidth = vs.width + 'em';
}else{
this.viewWidth = vs.width || (vs.noscroll ? 'auto' : this.viewWidth); //|| this.defaultWidth;
}
this.onBeforeRow = vs.onBeforeRow;
this.onAfterRow = vs.onAfterRow;
this.noscroll = vs.noscroll;
if(this.noscroll){
this.scrollboxNode.style.overflow = "hidden";
}
this.simpleStructure = Boolean(vs.cells.length == 1);
// bookkeeping
this.testFlexCells();
// accomodate new structure
this.updateStructure();
},
testFlexCells: function(){
// FIXME: cheater, this function does double duty as initializer and tester
this.flexCells = false;
for(var j=0, row; (row=this.structure.cells[j]); j++){
for(var i=0, cell; (cell=row[i]); i++){
cell.view = this;
this.flexCells = this.flexCells || cell.isFlex();
}
}
return this.flexCells;
},
updateStructure: function(){
// header builder needs to update table map
this.header.update();
// content builder needs to update markup cache
this.content.update();
},
getScrollbarWidth: function(){
var hasScrollSpace = this.hasVScrollbar();
var overflow = dojo.style(this.scrollboxNode, "overflow");
if(this.noscroll || !overflow || overflow == "hidden"){
hasScrollSpace = false;
}else if(overflow == "scroll"){
hasScrollSpace = true;
}
return (hasScrollSpace ? dojox.html.metrics.getScrollbar().w : 0); // Integer
},
getColumnsWidth: function(){
return this.headerContentNode.firstChild.offsetWidth; // Integer
},
setColumnsWidth: function(width){
this.headerContentNode.firstChild.style.width = width + 'px';
if(this.viewWidth){
this.viewWidth = width + 'px';
}
},
getWidth: function(){
return this.viewWidth || (this.getColumnsWidth()+this.getScrollbarWidth()) +'px'; // String
},
getContentWidth: function(){
return Math.max(0, dojo._getContentBox(this.domNode).w - this.getScrollbarWidth()) + 'px'; // String
},
render: function(){
this.scrollboxNode.style.height = '';
this.renderHeader();
if(this._togglingColumn >= 0){
this.setColumnsWidth(this.getColumnsWidth() - this._togglingColumn);
this._togglingColumn = -1;
}
var cells = this.grid.layout.cells;
var getSibling = dojo.hitch(this, function(node, before){
var inc = before?-1:1;
var idx = this.header.getCellNodeIndex(node) + inc;
var cell = cells[idx];
while(cell && cell.getHeaderNode() && cell.getHeaderNode().style.display == "none"){
idx += inc;
cell = cells[idx];
}
if(cell){
return cell.getHeaderNode();
}
return null;
});
if(this.grid.columnReordering && this.simpleStructure){
if(this.source){
this.source.destroy();
}
this.source = new dojo.dnd.Source(this.headerContentNode.firstChild.rows[0], {
horizontal: true,
accept: [ "gridColumn_" + this.grid.id ],
viewIndex: this.index,
onMouseDown: dojo.hitch(this, function(e){
this.header.decorateEvent(e);
if((this.header.overRightResizeArea(e) || this.header.overLeftResizeArea(e)) &&
this.header.canResize(e) && !this.header.moveable){
this.header.beginColumnResize(e);
}else{
if(this.grid.headerMenu){
this.grid.headerMenu.onCancel(true);
}
// IE reports a left click as 1, where everything else reports 0
if(e.button === (dojo.isIE ? 1 : 0)){
dojo.dnd.Source.prototype.onMouseDown.call(this.source, e);
}
}
}),
_markTargetAnchor: dojo.hitch(this, function(before){
var src = this.source;
if(src.current == src.targetAnchor && src.before == before){ return; }
if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
src._removeItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
}
dojo.dnd.Source.prototype._markTargetAnchor.call(src, before);
if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
src._addItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
}
}),
_unmarkTargetAnchor: dojo.hitch(this, function(){
var src = this.source;
if(!src.targetAnchor){ return; }
if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
src._removeItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
}
dojo.dnd.Source.prototype._unmarkTargetAnchor.call(src);
}),
destroy: dojo.hitch(this, function(){
dojo.disconnect(this._source_conn);
dojo.unsubscribe(this._source_sub);
dojo.dnd.Source.prototype.destroy.call(this.source);
})
});
this._source_conn = dojo.connect(this.source, "onDndDrop", this, "_onDndDrop");
this._source_sub = dojo.subscribe("/dnd/drop/before", this, "_onDndDropBefore");
this.source.startup();
}
},
_onDndDropBefore: function(source, nodes, copy){
if(dojo.dnd.manager().target !== this.source){
return;
}
this.source._targetNode = this.source.targetAnchor;
this.source._beforeTarget = this.source.before;
var views = this.grid.views.views;
var srcView = views[source.viewIndex];
var tgtView = views[this.index];
if(tgtView != srcView){
var s = srcView.convertColPctToFixed();
var t = tgtView.convertColPctToFixed();
if(s || t){
setTimeout(function(){
srcView.update();
tgtView.update();
}, 50);
}
}
},
_onDndDrop: function(source, nodes, copy){
if(dojo.dnd.manager().target !== this.source){
if(dojo.dnd.manager().source === this.source){
this._removingColumn = true;
}
return;
}
var getIdx = function(n){
return n ? dojo.attr(n, "idx") : null;
}
var w = dojo.marginBox(nodes[0]).w;
if(source.viewIndex !== this.index){
var views = this.grid.views.views;
var srcView = views[source.viewIndex];
var tgtView = views[this.index];
if(srcView.viewWidth && srcView.viewWidth != "auto"){
srcView.setColumnsWidth(srcView.getColumnsWidth() - w);
}
if(tgtView.viewWidth && tgtView.viewWidth != "auto"){
tgtView.setColumnsWidth(tgtView.getColumnsWidth());
}
}
var stn = this.source._targetNode;
var stb = this.source._beforeTarget;
var layout = this.grid.layout;
var idx = this.index;
delete this.source._targetNode;
delete this.source._beforeTarget;
window.setTimeout(function(){
layout.moveColumn(
source.viewIndex,
idx,
getIdx(nodes[0]),
getIdx(stn),
stb
);
}, 1);
},
renderHeader: function(){
this.headerContentNode.innerHTML = this.header.generateHtml(this._getHeaderContent);
if(this.flexCells){
this.contentWidth = this.getContentWidth();
this.headerContentNode.firstChild.style.width = this.contentWidth;
}
dojox.grid.util.fire(this, "onAfterRow", [-1, this.structure.cells, this.headerContentNode]);
},
// note: not called in 'view' context
_getHeaderContent: function(inCell){
var n = inCell.name || inCell.grid.getCellName(inCell);
var ret = [ '<div class="dojoxGridSortNode' ];
if(inCell.index != inCell.grid.getSortIndex()){
ret.push('">');
}else{
ret = ret.concat([ ' ',
inCell.grid.sortInfo > 0 ? 'dojoxGridSortUp' : 'dojoxGridSortDown',
'"><div class="dojoxGridArrowButtonChar">',
inCell.grid.sortInfo > 0 ? '&#9650;' : '&#9660;',
'</div><div class="dojoxGridArrowButtonNode"></div>' ]);
}
ret = ret.concat([n, '</div>']);
return ret.join('');
},
resize: function(){
this.adaptHeight();
this.adaptWidth();
},
hasHScrollbar: function(reset){
if(this._hasHScroll == undefined || reset){
if(this.noscroll){
this._hasHScroll = false;
}else{
var style = dojo.style(this.scrollboxNode, "overflow");
if(style == "hidden"){
this._hasHScroll = false;
}else if(style == "scroll"){
this._hasHScroll = true;
}else{
this._hasHScroll = (this.scrollboxNode.offsetWidth < this.contentNode.offsetWidth);
}
}
}
return this._hasHScroll; // Boolean
},
hasVScrollbar: function(reset){
if(this._hasVScroll == undefined || reset){
if(this.noscroll){
this._hasVScroll = false;
}else{
var style = dojo.style(this.scrollboxNode, "overflow");
if(style == "hidden"){
this._hasVScroll = false;
}else if(style == "scroll"){
this._hasVScroll = true;
}else{
this._hasVScroll = (this.scrollboxNode.offsetHeight < this.contentNode.offsetHeight);
}
}
}
return this._hasVScroll; // Boolean
},
convertColPctToFixed: function(){
// Fix any percentage widths to be pixel values
var hasPct = false;
var cellNodes = dojo.query("th", this.headerContentNode);
var fixedWidths = dojo.map(cellNodes, function(c){
var w = c.style.width;
if(w && w.slice(-1) == "%"){
hasPct = true;
return dojo.contentBox(c).w;
}else if(w && w.slice(-2) == "px"){
return window.parseInt(w, 10);
}
return -1;
});
if(hasPct){
dojo.forEach(this.grid.layout.cells, function(cell, idx){
if(cell.view == this){
var vIdx = cell.layoutIndex;
this.setColWidth(idx, fixedWidths[vIdx]);
cellNodes[vIdx].style.width = cell.unitWidth;
}
}, this);
return true;
}
return false;
},
adaptHeight: function(minusScroll){
if(!this.grid._autoHeight){
var h = this.domNode.clientHeight;
if(minusScroll){
h -= dojox.html.metrics.getScrollbar().h;
}
dojox.grid.util.setStyleHeightPx(this.scrollboxNode, h);
}
this.hasVScrollbar(true);
},
adaptWidth: function(){
if(this.flexCells){
// the view content width
this.contentWidth = this.getContentWidth();
this.headerContentNode.firstChild.style.width = this.contentWidth;
}
// FIXME: it should be easier to get w from this.scrollboxNode.clientWidth,
// but clientWidth seemingly does not include scrollbar width in some cases
var w = this.scrollboxNode.offsetWidth - this.getScrollbarWidth();
if(!this._removingColumn){
w = Math.max(w, this.getColumnsWidth()) + 'px';
}else{
w = Math.min(w, this.getColumnsWidth()) + 'px';
this._removingColumn = false;
}
var cn = this.contentNode;
cn.style.width = w;
this.hasHScrollbar(true);
},
setSize: function(w, h){
var ds = this.domNode.style;
var hs = this.headerNode.style;
if(w){
ds.width = w;
hs.width = w;
}
ds.height = (h >= 0 ? h + 'px' : '');
},
renderRow: function(inRowIndex){
var rowNode = this.createRowNode(inRowIndex);
this.buildRow(inRowIndex, rowNode);
this.grid.edit.restore(this, inRowIndex);
if(this._pendingUpdate){
window.clearTimeout(this._pendingUpdate);
}
this._pendingUpdate = window.setTimeout(dojo.hitch(this, function(){
window.clearTimeout(this._pendingUpdate);
delete this._pendingUpdate;
this.grid._resize();
}), 50);
return rowNode;
},
createRowNode: function(inRowIndex){
var node = document.createElement("div");
node.className = this.classTag + 'Row';
node[dojox.grid.util.gridViewTag] = this.id;
node[dojox.grid.util.rowIndexTag] = inRowIndex;
this.rowNodes[inRowIndex] = node;
return node;
},
buildRow: function(inRowIndex, inRowNode){
this.buildRowContent(inRowIndex, inRowNode);
this.styleRow(inRowIndex, inRowNode);
},
buildRowContent: function(inRowIndex, inRowNode){
inRowNode.innerHTML = this.content.generateHtml(inRowIndex, inRowIndex);
if(this.flexCells && this.contentWidth){
// FIXME: accessing firstChild here breaks encapsulation
inRowNode.firstChild.style.width = this.contentWidth;
}
dojox.grid.util.fire(this, "onAfterRow", [inRowIndex, this.structure.cells, inRowNode]);
},
rowRemoved:function(inRowIndex){
this.grid.edit.save(this, inRowIndex);
delete this.rowNodes[inRowIndex];
},
getRowNode: function(inRowIndex){
return this.rowNodes[inRowIndex];
},
getCellNode: function(inRowIndex, inCellIndex){
var row = this.getRowNode(inRowIndex);
if(row){
return this.content.getCellNode(row, inCellIndex);
}
},
getHeaderCellNode: function(inCellIndex){
if(this.headerContentNode){
return this.header.getCellNode(this.headerContentNode, inCellIndex);
}
},
// styling
styleRow: function(inRowIndex, inRowNode){
inRowNode._style = getStyleText(inRowNode);
this.styleRowNode(inRowIndex, inRowNode);
},
styleRowNode: function(inRowIndex, inRowNode){
if(inRowNode){
this.doStyleRowNode(inRowIndex, inRowNode);
}
},
doStyleRowNode: function(inRowIndex, inRowNode){
this.grid.styleRowNode(inRowIndex, inRowNode);
},
// updating
updateRow: function(inRowIndex){
var rowNode = this.getRowNode(inRowIndex);
if(rowNode){
rowNode.style.height = '';
this.buildRow(inRowIndex, rowNode);
}
return rowNode;
},
updateRowStyles: function(inRowIndex){
this.styleRowNode(inRowIndex, this.getRowNode(inRowIndex));
},
// scrolling
lastTop: 0,
firstScroll:0,
doscroll: function(inEvent){
//var s = dojo.marginBox(this.headerContentNode.firstChild);
var isLtr = dojo._isBodyLtr();
if(this.firstScroll < 2){
if((!isLtr && this.firstScroll == 1) || (isLtr && this.firstScroll == 0)){
var s = dojo.marginBox(this.headerNodeContainer);
if(dojo.isIE){
this.headerNodeContainer.style.width = s.w + this.getScrollbarWidth() + 'px';
}else if(dojo.isMoz){
//TODO currently only for FF, not sure for safari and opera
this.headerNodeContainer.style.width = s.w - this.getScrollbarWidth() + 'px';
//this.headerNodeContainer.style.width = s.w + 'px';
//set scroll to right in FF
this.scrollboxNode.scrollLeft = isLtr ?
this.scrollboxNode.clientWidth - this.scrollboxNode.scrollWidth :
this.scrollboxNode.scrollWidth - this.scrollboxNode.clientWidth;
}
}
this.firstScroll++;
}
this.headerNode.scrollLeft = this.scrollboxNode.scrollLeft;
// 'lastTop' is a semaphore to prevent feedback-loop with setScrollTop below
var top = this.scrollboxNode.scrollTop;
if(top != this.lastTop){
this.grid.scrollTo(top);
}
},
setScrollTop: function(inTop){
// 'lastTop' is a semaphore to prevent feedback-loop with doScroll above
this.lastTop = inTop;
this.scrollboxNode.scrollTop = inTop;
return this.scrollboxNode.scrollTop;
},
// event handlers (direct from DOM)
doContentEvent: function(e){
if(this.content.decorateEvent(e)){
this.grid.onContentEvent(e);
}
},
doHeaderEvent: function(e){
if(this.header.decorateEvent(e)){
this.grid.onHeaderEvent(e);
}
},
// event dispatch(from Grid)
dispatchContentEvent: function(e){
return this.content.dispatchEvent(e);
},
dispatchHeaderEvent: function(e){
return this.header.dispatchEvent(e);
},
// column resizing
setColWidth: function(inIndex, inWidth){
this.grid.setCellWidth(inIndex, inWidth + 'px');
},
update: function(){
this.content.update();
this.grid.update();
//get scroll after update or scroll left setting goes wrong on IE.
//See trac: #8040
var left = this.scrollboxNode.scrollLeft;
this.scrollboxNode.scrollLeft = left;
this.headerNode.scrollLeft = left;
}
});
dojo.declare("dojox.grid._GridAvatar", dojo.dnd.Avatar, {
construct: function(){
var dd = dojo.doc;
var a = dd.createElement("table");
a.cellPadding = a.cellSpacing = "0";
a.className = "dojoxGridDndAvatar";
a.style.position = "absolute";
a.style.zIndex = 1999;
a.style.margin = "0px"; // to avoid dojo.marginBox() problems with table's margins
var b = dd.createElement("tbody");
var tr = dd.createElement("tr");
var td = dd.createElement("td");
var img = dd.createElement("td");
tr.className = "dojoxGridDndAvatarItem";
img.className = "dojoxGridDndAvatarItemImage";
img.style.width = "16px";
var source = this.manager.source, node;
if(source.creator){
// create an avatar representation of the node
node = source._normailzedCreator(source.getItem(this.manager.nodes[0].id).data, "avatar").node;
}else{
// or just clone the node and hope it works
node = this.manager.nodes[0].cloneNode(true);
if(node.tagName.toLowerCase() == "tr"){
// insert extra table nodes
var table = dd.createElement("table"),
tbody = dd.createElement("tbody");
tbody.appendChild(node);
table.appendChild(tbody);
node = table;
}else if(node.tagName.toLowerCase() == "th"){
// insert extra table nodes
var table = dd.createElement("table"),
tbody = dd.createElement("tbody"),
r = dd.createElement("tr");
table.cellPadding = table.cellSpacing = "0";
r.appendChild(node);
tbody.appendChild(r);
table.appendChild(tbody);
node = table;
}
}
node.id = "";
td.appendChild(node);
tr.appendChild(img);
tr.appendChild(td);
dojo.style(tr, "opacity", 0.9);
b.appendChild(tr);
a.appendChild(b);
this.node = a;
var m = dojo.dnd.manager();
this.oldOffsetY = m.OFFSET_Y;
m.OFFSET_Y = 1;
},
destroy: function(){
dojo.dnd.manager().OFFSET_Y = this.oldOffsetY;
this.inherited(arguments);
}
});
var oldMakeAvatar = dojo.dnd.manager().makeAvatar;
dojo.dnd.manager().makeAvatar = function(){
var src = this.source;
if(src.viewIndex !== undefined){
return new dojox.grid._GridAvatar(this);
}
return oldMakeAvatar.call(dojo.dnd.manager());
}
})();
}
if(!dojo._hasResource["dojox.grid._RowSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._RowSelector"] = true;
dojo.provide("dojox.grid._RowSelector");
dojo.declare('dojox.grid._RowSelector', dojox.grid._View, {
// summary:
// Custom grid view. If used in a grid structure, provides a small selectable region for grid rows.
defaultWidth: "2em",
noscroll: true,
padBorderWidth: 2,
buildRendering: function(){
this.inherited('buildRendering', arguments);
this.scrollboxNode.style.overflow = "hidden";
this.headerNode.style.visibility = "hidden";
},
getWidth: function(){
return this.viewWidth || this.defaultWidth;
},
buildRowContent: function(inRowIndex, inRowNode){
var w = this.contentNode.offsetWidth - this.padBorderWidth
inRowNode.innerHTML = '<table class="dojoxGridRowbarTable" style="width:' + w + 'px;" border="0" cellspacing="0" cellpadding="0" role="'+(dojo.isFF<3 ? "wairole:" : "")+'presentation"><tr><td class="dojoxGridRowbarInner">&nbsp;</td></tr></table>';
},
renderHeader: function(){
},
resize: function(){
this.adaptHeight();
},
adaptWidth: function(){
},
// styling
doStyleRowNode: function(inRowIndex, inRowNode){
var n = [ "dojoxGridRowbar" ];
if(this.grid.rows.isOver(inRowIndex)){
n.push("dojoxGridRowbarOver");
}
if(this.grid.selection.isSelected(inRowIndex)){
n.push("dojoxGridRowbarSelected");
}
inRowNode.className = n.join(" ");
},
// event handlers
domouseover: function(e){
this.grid.onMouseOverRow(e);
},
domouseout: function(e){
if(!this.isIntraRowEvent(e)){
this.grid.onMouseOutRow(e);
}
}
});
}
if(!dojo._hasResource["dojox.grid._Layout"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._Layout"] = true;
dojo.provide("dojox.grid._Layout");
dojo.declare("dojox.grid._Layout", null, {
// summary:
// Controls grid cell layout. Owned by grid and used internally.
constructor: function(inGrid){
this.grid = inGrid;
},
// flat array of grid cells
cells: [],
// structured array of grid cells
structure: null,
// default cell width
defaultWidth: '6em',
// methods
moveColumn: function(sourceViewIndex, destViewIndex, cellIndex, targetIndex, before){
var source_cells = this.structure[sourceViewIndex].cells[0];
var dest_cells = this.structure[destViewIndex].cells[0];
var cell = null;
var cell_ri = 0;
var target_ri = 0;
for(var i=0, c; c=source_cells[i]; i++){
if(c.index == cellIndex){
cell_ri = i;
break;
}
}
cell = source_cells.splice(cell_ri, 1)[0];
cell.view = this.grid.views.views[destViewIndex];
for(i=0, c=null; c=dest_cells[i]; i++){
if(c.index == targetIndex){
target_ri = i;
break;
}
}
if(!before){
target_ri += 1;
}
dest_cells.splice(target_ri, 0, cell);
var sortedCell = this.grid.getCell(this.grid.getSortIndex());
if(sortedCell){
sortedCell._currentlySorted = this.grid.getSortAsc();
}
this.cells = [];
var cellIndex = 0;
for(var i=0, v; v=this.structure[i]; i++){
for(var j=0, cs; cs=v.cells[j]; j++){
for(var k=0, c; c=cs[k]; k++){
c.index = cellIndex;
this.cells.push(c);
if("_currentlySorted" in c){
var si = cellIndex + 1;
si *= c._currentlySorted ? 1 : -1;
this.grid.sortInfo = si;
delete c._currentlySorted;
}
cellIndex++;
}
}
}
this.grid.setupHeaderMenu();
//this.grid.renderOnIdle();
},
setColumnVisibility: function(columnIndex, visible){
var cell = this.cells[columnIndex];
if(cell.hidden == visible){
cell.hidden = !visible;
var v = cell.view, w = v.viewWidth;
if(w && w != "auto"){
v._togglingColumn = dojo.marginBox(cell.getHeaderNode()).w || 0;
}
v.update();
return true;
}else{
return false;
}
},
addCellDef: function(inRowIndex, inCellIndex, inDef){
var self = this;
var getCellWidth = function(inDef){
var w = 0;
if(inDef.colSpan > 1){
w = 0;
}else{
w = inDef.width || self._defaultCellProps.width || self.defaultWidth;
if(!isNaN(w)){
w = w + "em";
}
}
return w;
};
var props = {
grid: this.grid,
subrow: inRowIndex,
layoutIndex: inCellIndex,
index: this.cells.length
};
if(inDef && inDef instanceof dojox.grid.cells._Base){
var new_cell = dojo.clone(inDef);
props.unitWidth = getCellWidth(new_cell._props);
new_cell = dojo.mixin(new_cell, this._defaultCellProps, inDef._props, props);
return new_cell;
}
var cell_type = inDef.type || this._defaultCellProps.type || dojox.grid.cells.Cell;
props.unitWidth = getCellWidth(inDef);
return new cell_type(dojo.mixin({}, this._defaultCellProps, inDef, props));
},
addRowDef: function(inRowIndex, inDef){
var result = [];
var relSum = 0, pctSum = 0, doRel = true;
for(var i=0, def, cell; (def=inDef[i]); i++){
cell = this.addCellDef(inRowIndex, i, def);
result.push(cell);
this.cells.push(cell);
// Check and calculate the sum of all relative widths
if(doRel && cell.relWidth){
relSum += cell.relWidth;
}else if(cell.width){
var w = cell.width;
if(typeof w == "string" && w.slice(-1) == "%"){
pctSum += window.parseInt(w, 10);
}else if(w == "auto"){
// relative widths doesn't play nice with auto - since we
// don't have a way of knowing how much space the auto is
// supposed to take up.
doRel = false;
}
}
}
if(relSum && doRel){
// We have some kind of relWidths specified - so change them to %
dojo.forEach(result, function(cell){
if(cell.relWidth){
cell.width = cell.unitWidth = ((cell.relWidth / relSum) * (100 - pctSum)) + "%";
}
});
}
return result;
},
addRowsDef: function(inDef){
var result = [];
if(dojo.isArray(inDef)){
if(dojo.isArray(inDef[0])){
for(var i=0, row; inDef && (row=inDef[i]); i++){
result.push(this.addRowDef(i, row));
}
}else{
result.push(this.addRowDef(0, inDef));
}
}
return result;
},
addViewDef: function(inDef){
this._defaultCellProps = inDef.defaultCell || {};
if(inDef.width && inDef.width == "auto"){
delete inDef.width;
}
return dojo.mixin({}, inDef, {cells: this.addRowsDef(inDef.rows || inDef.cells)});
},
setStructure: function(inStructure){
this.fieldIndex = 0;
this.cells = [];
var s = this.structure = [];
if(this.grid.rowSelector){
var sel = { type: dojox._scopeName + ".grid._RowSelector" };
if(dojo.isString(this.grid.rowSelector)){
var width = this.grid.rowSelector;
if(width == "false"){
sel = null;
}else if(width != "true"){
sel['width'] = width;
}
}else{
if(!this.grid.rowSelector){
sel = null;
}
}
if(sel){
s.push(this.addViewDef(sel));
}
}
var isCell = function(def){
return ("name" in def || "field" in def || "get" in def);
};
var isRowDef = function(def){
if(dojo.isArray(def)){
if(dojo.isArray(def[0]) || isCell(def[0])){
return true;
}
}
return false;
};
var isView = function(def){
return (def != null && dojo.isObject(def) &&
("cells" in def || "rows" in def || ("type" in def && !isCell(def))));
};
if(dojo.isArray(inStructure)){
var hasViews = false;
for(var i=0, st; (st=inStructure[i]); i++){
if(isView(st)){
hasViews = true;
break;
}
}
if(!hasViews){
s.push(this.addViewDef({ cells: inStructure }));
}else{
for(var i=0, st; (st=inStructure[i]); i++){
if(isRowDef(st)){
s.push(this.addViewDef({ cells: st }));
}else if(isView(st)){
s.push(this.addViewDef(st));
}
}
}
}else if(isView(inStructure)){
// it's a view object
s.push(this.addViewDef(inStructure));
}
this.cellCount = this.cells.length;
this.grid.setupHeaderMenu();
}
});
}
if(!dojo._hasResource["dojox.grid._ViewManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._ViewManager"] = true;
dojo.provide("dojox.grid._ViewManager");
dojo.declare('dojox.grid._ViewManager', null, {
// summary:
// A collection of grid views. Owned by grid and used internally for managing grid views.
// description:
// Grid creates views automatically based on grid's layout structure.
// Users should typically not need to access individual views or the views collection directly.
constructor: function(inGrid){
this.grid = inGrid;
},
defaultWidth: 200,
views: [],
// operations
resize: function(){
this.onEach("resize");
},
render: function(){
this.onEach("render");
},
// views
addView: function(inView){
inView.idx = this.views.length;
this.views.push(inView);
},
destroyViews: function(){
for(var i=0, v; v=this.views[i]; i++){
v.destroy();
}
this.views = [];
},
getContentNodes: function(){
var nodes = [];
for(var i=0, v; v=this.views[i]; i++){
nodes.push(v.contentNode);
}
return nodes;
},
forEach: function(inCallback){
for(var i=0, v; v=this.views[i]; i++){
inCallback(v, i);
}
},
onEach: function(inMethod, inArgs){
inArgs = inArgs || [];
for(var i=0, v; v=this.views[i]; i++){
if(inMethod in v){
v[inMethod].apply(v, inArgs);
}
}
},
// layout
normalizeHeaderNodeHeight: function(){
var rowNodes = [];
for(var i=0, v; (v=this.views[i]); i++){
if(v.headerContentNode.firstChild){
rowNodes.push(v.headerContentNode);
}
}
this.normalizeRowNodeHeights(rowNodes);
},
normalizeRowNodeHeights: function(inRowNodes){
var h = 0;
for(var i=0, n, o; (n=inRowNodes[i]); i++){
h = Math.max(h, dojo.marginBox(n.firstChild).h);
}
h = (h >= 0 ? h : 0);
//
//
for(var i=0, n; (n=inRowNodes[i]); i++){
dojo.marginBox(n.firstChild, {h:h});
}
//
//
//
// querying the height here seems to help scroller measure the page on IE
if(inRowNodes&&inRowNodes[0]&&inRowNodes[0].parentNode){
inRowNodes[0].parentNode.offsetHeight;
}
},
resetHeaderNodeHeight: function(){
for(var i=0, v, n; (v=this.views[i]); i++){
n = v.headerContentNode.firstChild;
if(n){
n.style.height = "";
}
}
},
renormalizeRow: function(inRowIndex){
var rowNodes = [];
for(var i=0, v, n; (v=this.views[i])&&(n=v.getRowNode(inRowIndex)); i++){
n.firstChild.style.height = '';
rowNodes.push(n);
}
this.normalizeRowNodeHeights(rowNodes);
},
getViewWidth: function(inIndex){
return this.views[inIndex].getWidth() || this.defaultWidth;
},
// must be called after view widths are properly set or height can be miscalculated
// if there are flex columns
measureHeader: function(){
// need to reset view header heights so they are properly measured.
this.resetHeaderNodeHeight();
this.forEach(function(inView){
inView.headerContentNode.style.height = '';
});
var h = 0;
// calculate maximum view header height
this.forEach(function(inView){
h = Math.max(inView.headerNode.offsetHeight, h);
});
return h;
},
measureContent: function(){
var h = 0;
this.forEach(function(inView){
h = Math.max(inView.domNode.offsetHeight, h);
});
return h;
},
findClient: function(inAutoWidth){
// try to use user defined client
var c = this.grid.elasticView || -1;
// attempt to find implicit client
if(c < 0){
for(var i=1, v; (v=this.views[i]); i++){
if(v.viewWidth){
for(i=1; (v=this.views[i]); i++){
if(!v.viewWidth){
c = i;
break;
}
}
break;
}
}
}
// client is in the middle by default
if(c < 0){
c = Math.floor(this.views.length / 2);
}
return c;
},
arrange: function(l, w){
var i, v, vw, len = this.views.length;
// find the client
var c = (w <= 0 ? len : this.findClient());
// layout views
var setPosition = function(v, l){
var ds = v.domNode.style;
var hs = v.headerNode.style;
if(!dojo._isBodyLtr()){
ds.right = l + 'px';
hs.right = l + 'px';
}else{
ds.left = l + 'px';
hs.left = l + 'px';
}
ds.top = 0 + 'px';
hs.top = 0;
}
// for views left of the client
//BiDi TODO: The left and right should not appear in BIDI environment. Should be replaced with
//leading and tailing concept.
for(i=0; (v=this.views[i])&&(i<c); i++){
// get width
vw = this.getViewWidth(i);
// process boxes
v.setSize(vw, 0);
setPosition(v, l);
if(v.headerContentNode && v.headerContentNode.firstChild){
vw = v.getColumnsWidth()+v.getScrollbarWidth();
}else{
vw = v.domNode.offsetWidth;
}
// update position
l += vw;
}
// next view (is the client, i++ == c)
i++;
// start from the right edge
var r = w;
// for views right of the client (iterated from the right)
for(var j=len-1; (v=this.views[j])&&(i<=j); j--){
// get width
vw = this.getViewWidth(j);
// set size
v.setSize(vw, 0);
// measure in pixels
vw = v.domNode.offsetWidth;
// update position
r -= vw;
// set position
setPosition(v, r);
}
if(c<len){
v = this.views[c];
// position the client box between left and right boxes
vw = Math.max(1, r-l);
// set size
v.setSize(vw + 'px', 0);
setPosition(v, l);
}
return l;
},
// rendering
renderRow: function(inRowIndex, inNodes){
var rowNodes = [];
for(var i=0, v, n, rowNode; (v=this.views[i])&&(n=inNodes[i]); i++){
rowNode = v.renderRow(inRowIndex);
n.appendChild(rowNode);
rowNodes.push(rowNode);
}
this.normalizeRowNodeHeights(rowNodes);
},
rowRemoved: function(inRowIndex){
this.onEach("rowRemoved", [ inRowIndex ]);
},
// updating
updateRow: function(inRowIndex){
for(var i=0, v; v=this.views[i]; i++){
v.updateRow(inRowIndex);
}
this.renormalizeRow(inRowIndex);
},
updateRowStyles: function(inRowIndex){
this.onEach("updateRowStyles", [ inRowIndex ]);
},
// scrolling
setScrollTop: function(inTop){
var top = inTop;
for(var i=0, v; v=this.views[i]; i++){
top = v.setScrollTop(inTop);
// Work around IE not firing scroll events that cause header offset
// issues to occur.
if(dojo.isIE && v.headerNode && v.scrollboxNode){
v.headerNode.scrollLeft = v.scrollboxNode.scrollLeft;
}
}
return top;
//this.onEach("setScrollTop", [ inTop ]);
},
getFirstScrollingView: function(){
// summary: Returns the first grid view with a scroll bar
for(var i=0, v; (v=this.views[i]); i++){
if(v.hasHScrollbar() || v.hasVScrollbar()){
return v;
}
}
}
});
}
if(!dojo._hasResource["dojox.grid._RowManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._RowManager"] = true;
dojo.provide("dojox.grid._RowManager");
(function(){
var setStyleText = function(inNode, inStyleText){
if(inNode.style.cssText == undefined){
inNode.setAttribute("style", inStyleText);
}else{
inNode.style.cssText = inStyleText;
}
};
dojo.declare("dojox.grid._RowManager", null, {
// Stores information about grid rows. Owned by grid and used internally.
constructor: function(inGrid){
this.grid = inGrid;
},
linesToEms: 2,
overRow: -2,
// styles
prepareStylingRow: function(inRowIndex, inRowNode){
return {
index: inRowIndex,
node: inRowNode,
odd: Boolean(inRowIndex&1),
selected: this.grid.selection.isSelected(inRowIndex),
over: this.isOver(inRowIndex),
customStyles: "",
customClasses: "dojoxGridRow"
}
},
styleRowNode: function(inRowIndex, inRowNode){
var row = this.prepareStylingRow(inRowIndex, inRowNode);
this.grid.onStyleRow(row);
this.applyStyles(row);
},
applyStyles: function(inRow){
var i = inRow;
i.node.className = i.customClasses;
var h = i.node.style.height;
setStyleText(i.node, i.customStyles + ';' + (i.node._style||''));
i.node.style.height = h;
},
updateStyles: function(inRowIndex){
this.grid.updateRowStyles(inRowIndex);
},
// states and events
setOverRow: function(inRowIndex){
var last = this.overRow;
this.overRow = inRowIndex;
if((last!=this.overRow)&&(last >=0)){
this.updateStyles(last);
}
this.updateStyles(this.overRow);
},
isOver: function(inRowIndex){
return (this.overRow == inRowIndex);
}
});
})();
}
if(!dojo._hasResource["dojox.grid._FocusManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._FocusManager"] = true;
dojo.provide("dojox.grid._FocusManager");
// focus management
dojo.declare("dojox.grid._FocusManager", null, {
// summary:
// Controls grid cell focus. Owned by grid and used internally for focusing.
// Note: grid cell actually receives keyboard input only when cell is being edited.
constructor: function(inGrid){
this.grid = inGrid;
this.cell = null;
this.rowIndex = -1;
this._connects = [];
this._connects.push(dojo.connect(this.grid.domNode, "onfocus", this, "doFocus"));
this._connects.push(dojo.connect(this.grid.domNode, "onblur", this, "doBlur"));
this._connects.push(dojo.connect(this.grid.lastFocusNode, "onfocus", this, "doLastNodeFocus"));
this._connects.push(dojo.connect(this.grid.lastFocusNode, "onblur", this, "doLastNodeBlur"));
this._connects.push(dojo.connect(this.grid,"_onFetchComplete", this, "_delayedCellFocus"));
this._connects.push(dojo.connect(this.grid,"postrender", this, "_delayedHeaderFocus"));
},
destroy: function(){
dojo.forEach(this._connects, dojo.disconnect);
delete this.grid;
delete this.cell;
},
_colHeadNode: null,
tabbingOut: false,
focusClass: "dojoxGridCellFocus",
focusView: null,
initFocusView: function(){
this.focusView = this.grid.views.getFirstScrollingView();
this._initColumnHeaders();
},
isFocusCell: function(inCell, inRowIndex){
// summary:
// states if the given cell is focused
// inCell: object
// grid cell object
// inRowIndex: int
// grid row index
// returns:
// true of the given grid cell is focused
return (this.cell == inCell) && (this.rowIndex == inRowIndex);
},
isLastFocusCell: function(){
return (this.rowIndex == this.grid.rowCount-1) && (this.cell.index == this.grid.layout.cellCount-1);
},
isFirstFocusCell: function(){
return (this.rowIndex == 0) && (this.cell.index == 0);
},
isNoFocusCell: function(){
return (this.rowIndex < 0) || !this.cell;
},
isNavHeader: function(){
// summary:
// states whether currently navigating among column headers.
// returns:
// true if focus is on a column header; false otherwise.
return (!!this._colHeadNode);
},
getHeaderIndex: function(){
// summary:
// if one of the column headers currently has focus, return its index.
// returns:
// index of the focused column header, or -1 if none have focus.
if(this._colHeadNode){
return dojo.indexOf(this._findHeaderCells(), this._colHeadNode);
}else{
return -1;
}
},
_focusifyCellNode: function(inBork){
var n = this.cell && this.cell.getNode(this.rowIndex);
if(n){
dojo.toggleClass(n, this.focusClass, inBork);
if(inBork){
var sl = this.scrollIntoView();
try{
if(!this.grid.edit.isEditing()){
dojox.grid.util.fire(n, "focus");
if(sl){ this.cell.view.scrollboxNode.scrollLeft = sl; }
}
}catch(e){}
}
}
},
_delayedCellFocus: function(){
if(this.isNavHeader()){
return;
}
var n = this.cell && this.cell.getNode(this.rowIndex);
if(n){
try{
if(!this.grid.edit.isEditing()){
dojo.toggleClass(n, this.focusClass, true);
dojox.grid.util.fire(n, "focus");
}
}
catch(e){}
}
},
_delayedHeaderFocus: function(){
if(this.isNavHeader()){
this.focusHeader();
//this._focusifyCellNode(false);
// may need click select??
}
},
_initColumnHeaders: function(){
this._connects.push(dojo.connect(this.grid.viewsHeaderNode, "onblur", this, "doBlurHeader"));
var headers = this._findHeaderCells();
for(var i = 0; i < headers.length; i++){
this._connects.push(dojo.connect(headers[i], "onfocus", this, "doColHeaderFocus"));
this._connects.push(dojo.connect(headers[i], "onblur", this, "doColHeaderBlur"));
}
},
_findHeaderCells: function(){
// This should be a one liner:
// dojo.query("th[tabindex=-1]", this.grid.viewsHeaderNode);
// But there is a bug in dojo.query() for IE -- see trac #7037.
var allHeads = dojo.query("th", this.grid.viewsHeaderNode);
var headers = [];
for (var i = 0; i < allHeads.length; i++){
var aHead = allHeads[i];
var hasTabIdx = dojo.hasAttr(aHead, "tabindex");
var tabindex = dojo.attr(aHead, "tabindex");
if (hasTabIdx && tabindex < 0) {
headers.push(aHead);
}
}
return headers;
},
scrollIntoView: function(){
var info = (this.cell ? this._scrollInfo(this.cell) : null);
if(!info){
return null;
}
var rt = this.grid.scroller.findScrollTop(this.rowIndex);
// place cell within horizontal view
if(info.n.offsetLeft + info.n.offsetWidth > info.sr.l + info.sr.w){
info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w;
}else if(info.n.offsetLeft < info.sr.l){
info.s.scrollLeft = info.n.offsetLeft;
}
// place cell within vertical view
if(rt + info.r.offsetHeight > info.sr.t + info.sr.h){
this.grid.setScrollTop(rt + info.r.offsetHeight - info.sr.h);
}else if(rt < info.sr.t){
this.grid.setScrollTop(rt);
}
return info.s.scrollLeft;
},
_scrollInfo: function(cell, domNode){
if(cell){
var cl = cell,
sbn = cl.view.scrollboxNode,
sbnr = {
w: sbn.clientWidth,
l: sbn.scrollLeft,
t: sbn.scrollTop,
h: sbn.clientHeight
},
rn = cl.view.getRowNode(this.rowIndex);
return {
c: cl,
s: sbn,
sr: sbnr,
n: (domNode ? domNode : cell.getNode(this.rowIndex)),
r: rn
};
}
return null;
},
_scrollHeader: function(currentIdx){
var info = null;
if(this._colHeadNode){
info = this._scrollInfo(this.grid.getCell(currentIdx), this._colHeadNode);
}
if(info){
// scroll horizontally as needed.
if(info.n.offsetLeft + info.n.offsetWidth > info.sr.l + info.sr.w){
info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w;
}else if(info.n.offsetLeft < info.sr.l){
info.s.scrollLeft = info.n.offsetLeft;
}
}
},
styleRow: function(inRow){
return;
},
setFocusIndex: function(inRowIndex, inCellIndex){
// summary:
// focuses the given grid cell
// inRowIndex: int
// grid row index
// inCellIndex: int
// grid cell index
this.setFocusCell(this.grid.getCell(inCellIndex), inRowIndex);
},
setFocusCell: function(inCell, inRowIndex){
// summary:
// focuses the given grid cell
// inCell: object
// grid cell object
// inRowIndex: int
// grid row index
if(inCell && !this.isFocusCell(inCell, inRowIndex)){
this.tabbingOut = false;
this._colHeadNode = null;
this.focusGridView();
this._focusifyCellNode(false);
this.cell = inCell;
this.rowIndex = inRowIndex;
this._focusifyCellNode(true);
}
// even if this cell isFocusCell, the document focus may need to be rejiggered
// call opera on delay to prevent keypress from altering focus
if(dojo.isOpera){
setTimeout(dojo.hitch(this.grid, 'onCellFocus', this.cell, this.rowIndex), 1);
}else{
this.grid.onCellFocus(this.cell, this.rowIndex);
}
},
next: function(){
// summary:
// focus next grid cell
var row=this.rowIndex, col=this.cell.index+1, cc=this.grid.layout.cellCount-1, rc=this.grid.rowCount-1;
if(col > cc){
col = 0;
row++;
}
if(row > rc){
col = cc;
row = rc;
}
this.setFocusIndex(row, col);
},
previous: function(){
// summary:
// focus previous grid cell
var row=(this.rowIndex || 0), col=(this.cell.index || 0) - 1;
if(col < 0){
col = this.grid.layout.cellCount-1;
row--;
}
if(row < 0){
row = 0;
col = 0;
}
this.setFocusIndex(row, col);
},
move: function(inRowDelta, inColDelta) {
// summary:
// focus grid cell or column header based on position relative to current focus
// inRowDelta: int
// vertical distance from current focus
// inColDelta: int
// horizontal distance from current focus
// Handle column headers.
if(this.isNavHeader()){
var headers = this._findHeaderCells();
var currentIdx = dojo.indexOf(headers, this._colHeadNode);
currentIdx += inColDelta;
if((currentIdx >= 0) && (currentIdx < headers.length)){
this._colHeadNode = headers[currentIdx];
this._colHeadNode.focus();
this._scrollHeader(currentIdx);
}
}else{
// Handle grid proper.
var sc = this.grid.scroller,
r = this.rowIndex,
rc = this.grid.rowCount-1,
row = Math.min(rc, Math.max(0, r+inRowDelta));
if(inRowDelta){
if(inRowDelta>0){
if(row > sc.getLastPageRow(sc.page)){
//need to load additional data, let scroller do that
this.grid.setScrollTop(this.grid.scrollTop+sc.findScrollTop(row)-sc.findScrollTop(r));
}
}else if(inRowDelta<0){
if(row <= sc.getPageRow(sc.page)){
//need to load additional data, let scroller do that
this.grid.setScrollTop(this.grid.scrollTop-sc.findScrollTop(r)-sc.findScrollTop(row));
}
}
}
var cc = this.grid.layout.cellCount-1,
i = this.cell.index,
col = Math.min(cc, Math.max(0, i+inColDelta));
this.setFocusIndex(row, col);
if(inRowDelta){
this.grid.updateRow(r);
}
}
},
previousKey: function(e){
if(!this.isNavHeader()){
this.focusHeader();
dojo.stopEvent(e);
}else if(this.grid.edit.isEditing()){
dojo.stopEvent(e);
this.previous();
}else{
this.tabOut(this.grid.domNode);
}
},
nextKey: function(e) {
var isEmpty = this.grid.rowCount == 0;
if(e.target === this.grid.domNode){
this.focusHeader();
dojo.stopEvent(e);
}else if(this.isNavHeader()){
// if tabbing from col header, then go to grid proper. If grid is empty this.grid.rowCount == 0
this._colHeadNode = null;
if(this.isNoFocusCell() && !isEmpty){
this.setFocusIndex(0, 0);
if (!this.grid.selection.isSelected(0)){
this.grid.selection.clickSelect(0, false, false);
}
}else if(this.cell && !isEmpty){
if(this.focusView && !this.focusView.rowNodes[this.rowIndex]){
// if rowNode for current index is undefined (likely as a result of a sort and because of #7304)
// scroll to that row
this.grid.scrollToRow(this.rowIndex);
}
this.focusGrid();
}else{
this.tabOut(this.grid.lastFocusNode);
}
}else if(this.grid.edit.isEditing()){
dojo.stopEvent(e);
this.next();
}else{
this.tabOut(this.grid.lastFocusNode);
}
},
tabOut: function(inFocusNode){
this.tabbingOut = true;
inFocusNode.focus();
},
focusGridView: function(){
dojox.grid.util.fire(this.focusView, "focus");
},
focusGrid: function(inSkipFocusCell){
this.focusGridView();
this._focusifyCellNode(true);
},
focusHeader: function(){
var headerNodes = this._findHeaderCells();
if(this.isNoFocusCell()){
this._colHeadNode = headerNodes[0];
}else{
this._colHeadNode = headerNodes[this.cell.index];
}
if(this._colHeadNode){
dojox.grid.util.fire(this._colHeadNode, "focus");
this._focusifyCellNode(false);
}
},
doFocus: function(e){
// trap focus only for grid dom node
if(e && e.target != e.currentTarget){
dojo.stopEvent(e);
return;
}
// do not focus for scrolling if grid is about to blur
if(!this.tabbingOut){
this.focusHeader();
}
this.tabbingOut = false;
dojo.stopEvent(e);
},
doBlur: function(e){
dojo.stopEvent(e); // FF2
},
doBlurHeader: function(e){
dojo.stopEvent(e); // FF2
},
doLastNodeFocus: function(e){
if (this.tabbingOut){
this._focusifyCellNode(false);
}else if(this.grid.rowCount >0){
if (this.isNoFocusCell()){
this.setFocusIndex(0,0);
}
this._focusifyCellNode(true);
}else {
this.focusHeader();
}
this.tabbingOut = false;
dojo.stopEvent(e); // FF2
},
doLastNodeBlur: function(e){
dojo.stopEvent(e); // FF2
},
doColHeaderFocus: function(e){
dojo.toggleClass(e.target, this.focusClass, true);
},
doColHeaderBlur: function(e){
dojo.toggleClass(e.target, this.focusClass, false);
}
});
}
if(!dojo._hasResource["dojox.grid._EditManager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._EditManager"] = true;
dojo.provide("dojox.grid._EditManager");
dojo.declare("dojox.grid._EditManager", null, {
// summary:
// Controls grid cell editing process. Owned by grid and used internally for editing.
constructor: function(inGrid){
// inGrid: dojox.Grid
// The dojox.Grid this editor should be attached to
this.grid = inGrid;
this.connections = [];
if(dojo.isIE){
this.connections.push(dojo.connect(document.body, "onfocus", dojo.hitch(this, "_boomerangFocus")));
}
},
info: {},
destroy: function(){
dojo.forEach(this.connections,dojo.disconnect);
},
cellFocus: function(inCell, inRowIndex){
// summary:
// Invoke editing when cell is focused
// inCell: cell object
// Grid cell object
// inRowIndex: Integer
// Grid row index
if(this.grid.singleClickEdit || this.isEditRow(inRowIndex)){
// if same row or quick editing, edit
this.setEditCell(inCell, inRowIndex);
}else{
// otherwise, apply any pending row edits
this.apply();
}
// if dynamic or static editing...
if(this.isEditing() || (inCell && inCell.editable && inCell.alwaysEditing)){
// let the editor focus itself as needed
this._focusEditor(inCell, inRowIndex);
}
},
rowClick: function(e){
if(this.isEditing() && !this.isEditRow(e.rowIndex)){
this.apply();
}
},
styleRow: function(inRow){
if(inRow.index == this.info.rowIndex){
inRow.customClasses += ' dojoxGridRowEditing';
}
},
dispatchEvent: function(e){
var c = e.cell, ed = (c && c["editable"]) ? c : 0;
return ed && ed.dispatchEvent(e.dispatch, e);
},
// Editing
isEditing: function(){
// summary:
// Indicates editing state of the grid.
// returns: Boolean
// True if grid is actively editing
return this.info.rowIndex !== undefined;
},
isEditCell: function(inRowIndex, inCellIndex){
// summary:
// Indicates if the given cell is being edited.
// inRowIndex: Integer
// Grid row index
// inCellIndex: Integer
// Grid cell index
// returns: Boolean
// True if given cell is being edited
return (this.info.rowIndex === inRowIndex) && (this.info.cell.index == inCellIndex);
},
isEditRow: function(inRowIndex){
// summary:
// Indicates if the given row is being edited.
// inRowIndex: Integer
// Grid row index
// returns: Boolean
// True if given row is being edited
return this.info.rowIndex === inRowIndex;
},
setEditCell: function(inCell, inRowIndex){
// summary:
// Set the given cell to be edited
// inRowIndex: Integer
// Grid row index
// inCell: Object
// Grid cell object
if(!this.isEditCell(inRowIndex, inCell.index) && this.grid.canEdit && this.grid.canEdit(inCell, inRowIndex)){
this.start(inCell, inRowIndex, this.isEditRow(inRowIndex) || inCell.editable);
}
},
_focusEditor: function(inCell, inRowIndex){
dojox.grid.util.fire(inCell, "focus", [inRowIndex]);
},
focusEditor: function(){
if(this.isEditing()){
this._focusEditor(this.info.cell, this.info.rowIndex);
}
},
// implement fix for focus boomerang effect on IE
_boomerangWindow: 500,
_shouldCatchBoomerang: function(){
return this._catchBoomerang > new Date().getTime();
},
_boomerangFocus: function(){
//
if(this._shouldCatchBoomerang()){
// make sure we don't utterly lose focus
this.grid.focus.focusGrid();
// let the editor focus itself as needed
this.focusEditor();
// only catch once
this._catchBoomerang = 0;
}
},
_doCatchBoomerang: function(){
// give ourselves a few ms to boomerang IE focus effects
if(dojo.isIE){this._catchBoomerang = new Date().getTime() + this._boomerangWindow;}
},
// end boomerang fix API
start: function(inCell, inRowIndex, inEditing){
this.grid.beginUpdate();
this.editorApply();
if(this.isEditing() && !this.isEditRow(inRowIndex)){
this.applyRowEdit();
this.grid.updateRow(inRowIndex);
}
if(inEditing){
this.info = { cell: inCell, rowIndex: inRowIndex };
this.grid.doStartEdit(inCell, inRowIndex);
this.grid.updateRow(inRowIndex);
}else{
this.info = {};
}
this.grid.endUpdate();
// make sure we don't utterly lose focus
this.grid.focus.focusGrid();
// let the editor focus itself as needed
this._focusEditor(inCell, inRowIndex);
// give ourselves a few ms to boomerang IE focus effects
this._doCatchBoomerang();
},
_editorDo: function(inMethod){
var c = this.info.cell
//c && c.editor && c.editor[inMethod](c, this.info.rowIndex);
c && c.editable && c[inMethod](this.info.rowIndex);
},
editorApply: function(){
this._editorDo("apply");
},
editorCancel: function(){
this._editorDo("cancel");
},
applyCellEdit: function(inValue, inCell, inRowIndex){
if(this.grid.canEdit(inCell, inRowIndex)){
this.grid.doApplyCellEdit(inValue, inRowIndex, inCell.field);
}
},
applyRowEdit: function(){
this.grid.doApplyEdit(this.info.rowIndex, this.info.cell.field);
},
apply: function(){
// summary:
// Apply a grid edit
if(this.isEditing()){
this.grid.beginUpdate();
this.editorApply();
this.applyRowEdit();
this.info = {};
this.grid.endUpdate();
this.grid.focus.focusGrid();
this._doCatchBoomerang();
}
},
cancel: function(){
// summary:
// Cancel a grid edit
if(this.isEditing()){
this.grid.beginUpdate();
this.editorCancel();
this.info = {};
this.grid.endUpdate();
this.grid.focus.focusGrid();
this._doCatchBoomerang();
}
},
save: function(inRowIndex, inView){
// summary:
// Save the grid editing state
// inRowIndex: Integer
// Grid row index
// inView: Object
// Grid view
var c = this.info.cell;
if(this.isEditRow(inRowIndex) && (!inView || c.view==inView) && c.editable){
c.save(c, this.info.rowIndex);
}
},
restore: function(inView, inRowIndex){
// summary:
// Restores the grid editing state
// inRowIndex: Integer
// Grid row index
// inView: Object
// Grid view
var c = this.info.cell;
if(this.isEditRow(inRowIndex) && c.view == inView && c.editable){
c.restore(c, this.info.rowIndex);
}
}
});
}
if(!dojo._hasResource['dojox.grid.Selection']){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource['dojox.grid.Selection'] = true;
dojo.provide('dojox.grid.Selection');
dojo.declare("dojox.grid.Selection", null, {
// summary:
// Manages row selection for grid. Owned by grid and used internally
// for selection. Override to implement custom selection.
constructor: function(inGrid){
this.grid = inGrid;
this.selected = [];
this.setMode(inGrid.selectionMode);
},
mode: 'extended',
selected: null,
updating: 0,
selectedIndex: -1,
setMode: function(mode){
if(this.selected.length){
this.deselectAll();
}
if(mode != 'extended' && mode != 'multiple' && mode != 'single' && mode != 'none'){
this.mode = 'extended';
}else{
this.mode = mode;
}
},
onCanSelect: function(inIndex){
return this.grid.onCanSelect(inIndex);
},
onCanDeselect: function(inIndex){
return this.grid.onCanDeselect(inIndex);
},
onSelected: function(inIndex){
},
onDeselected: function(inIndex){
},
//onSetSelected: function(inIndex, inSelect) { };
onChanging: function(){
},
onChanged: function(){
},
isSelected: function(inIndex){
if(this.mode == 'none'){
return false;
}
return this.selected[inIndex];
},
getFirstSelected: function(){
if(!this.selected.length||this.mode == 'none'){ return -1; }
for(var i=0, l=this.selected.length; i<l; i++){
if(this.selected[i]){
return i;
}
}
return -1;
},
getNextSelected: function(inPrev){
if(this.mode == 'none'){ return -1; }
for(var i=inPrev+1, l=this.selected.length; i<l; i++){
if(this.selected[i]){
return i;
}
}
return -1;
},
getSelected: function(){
var result = [];
for(var i=0, l=this.selected.length; i<l; i++){
if(this.selected[i]){
result.push(i);
}
}
return result;
},
getSelectedCount: function(){
var c = 0;
for(var i=0; i<this.selected.length; i++){
if(this.selected[i]){
c++;
}
}
return c;
},
_beginUpdate: function(){
if(this.updating == 0){
this.onChanging();
}
this.updating++;
},
_endUpdate: function(){
this.updating--;
if(this.updating == 0){
this.onChanged();
}
},
select: function(inIndex){
if(this.mode == 'none'){ return; }
if(this.mode != 'multiple'){
this.deselectAll(inIndex);
this.addToSelection(inIndex);
}else{
this.toggleSelect(inIndex);
}
},
addToSelection: function(inIndex){
if(this.mode == 'none'){ return; }
inIndex = Number(inIndex);
if(this.selected[inIndex]){
this.selectedIndex = inIndex;
}else{
if(this.onCanSelect(inIndex) !== false){
this.selectedIndex = inIndex;
this._beginUpdate();
this.selected[inIndex] = true;
//this.grid.onSelected(inIndex);
this.onSelected(inIndex);
//this.onSetSelected(inIndex, true);
this._endUpdate();
}
}
},
deselect: function(inIndex){
if(this.mode == 'none'){ return; }
inIndex = Number(inIndex);
if(this.selectedIndex == inIndex){
this.selectedIndex = -1;
}
if(this.selected[inIndex]){
if(this.onCanDeselect(inIndex) === false){
return;
}
this._beginUpdate();
delete this.selected[inIndex];
//this.grid.onDeselected(inIndex);
this.onDeselected(inIndex);
//this.onSetSelected(inIndex, false);
this._endUpdate();
}
},
setSelected: function(inIndex, inSelect){
this[(inSelect ? 'addToSelection' : 'deselect')](inIndex);
},
toggleSelect: function(inIndex){
this.setSelected(inIndex, !this.selected[inIndex])
},
_range: function(inFrom, inTo, func){
var s = (inFrom >= 0 ? inFrom : inTo), e = inTo;
if(s > e){
e = s;
s = inTo;
}
for(var i=s; i<=e; i++){
func(i);
}
},
selectRange: function(inFrom, inTo){
this._range(inFrom, inTo, dojo.hitch(this, "addToSelection"));
},
deselectRange: function(inFrom, inTo){
this._range(inFrom, inTo, dojo.hitch(this, "deselect"));
},
insert: function(inIndex){
this.selected.splice(inIndex, 0, false);
if(this.selectedIndex >= inIndex){
this.selectedIndex++;
}
},
remove: function(inIndex){
this.selected.splice(inIndex, 1);
if(this.selectedIndex >= inIndex){
this.selectedIndex--;
}
},
deselectAll: function(inExcept){
for(var i in this.selected){
if((i!=inExcept)&&(this.selected[i]===true)){
this.deselect(i);
}
}
},
clickSelect: function(inIndex, inCtrlKey, inShiftKey){
if(this.mode == 'none'){ return; }
this._beginUpdate();
if(this.mode != 'extended'){
this.select(inIndex);
}else{
var lastSelected = this.selectedIndex;
if(!inCtrlKey){
this.deselectAll(inIndex);
}
if(inShiftKey){
this.selectRange(lastSelected, inIndex);
}else if(inCtrlKey){
this.toggleSelect(inIndex);
}else{
this.addToSelection(inIndex)
}
}
this._endUpdate();
},
clickSelectEvent: function(e){
this.clickSelect(e.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
},
clear: function(){
this._beginUpdate();
this.deselectAll();
this._endUpdate();
}
});
}
if(!dojo._hasResource["dojox.grid._Events"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._Events"] = true;
dojo.provide("dojox.grid._Events");
dojo.declare("dojox.grid._Events", null, {
// summary:
// _Grid mixin that provides default implementations for grid events.
// description:
// Default synthetic events dispatched for _Grid. dojo.connect to events to
// retain default implementation or override them for custom handling.
// cellOverClass: String
// css class to apply to grid cells over which the cursor is placed.
cellOverClass: "dojoxGridCellOver",
onKeyEvent: function(e){
// summary: top level handler for Key Events
this.dispatchKeyEvent(e);
},
onContentEvent: function(e){
// summary: Top level handler for Content events
this.dispatchContentEvent(e);
},
onHeaderEvent: function(e){
// summary: Top level handler for header events
this.dispatchHeaderEvent(e);
},
onStyleRow: function(inRow){
// summary:
// Perform row styling on a given row. Called whenever row styling is updated.
//
// inRow: Object
// Object containing row state information: selected, true if the row is selcted; over:
// true of the mouse is over the row; odd: true if the row is odd. Use customClasses and
// customStyles to control row css classes and styles; both properties are strings.
//
// example: onStyleRow({ selected: true, over:true, odd:false })
var i = inRow;
i.customClasses += (i.odd?" dojoxGridRowOdd":"") + (i.selected?" dojoxGridRowSelected":"") + (i.over?" dojoxGridRowOver":"");
this.focus.styleRow(inRow);
this.edit.styleRow(inRow);
},
onKeyDown: function(e){
// summary:
// Grid key event handler. By default enter begins editing and applies edits, escape cancels an edit,
// tab, shift-tab, and arrow keys move grid cell focus.
if(e.altKey || e.metaKey){
return;
}
var dk = dojo.keys;
switch(e.keyCode){
case dk.ESCAPE:
this.edit.cancel();
break;
case dk.ENTER:
if(!this.edit.isEditing()){
var colIdx = this.focus.getHeaderIndex();
if(colIdx >= 0) {
this.setSortIndex(colIdx);
break;
}else {
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
dojo.stopEvent(e);
}
if(!e.shiftKey){
var isEditing = this.edit.isEditing();
this.edit.apply();
if(!isEditing){
this.edit.setEditCell(this.focus.cell, this.focus.rowIndex);
}
}
if (!this.edit.isEditing()){
var curView = this.focus.focusView || this.views.views[0]; //if no focusView than only one view
curView.content.decorateEvent(e);
this.onRowClick(e);
}
break;
case dk.SPACE:
if(!this.edit.isEditing()){
var colIdx = this.focus.getHeaderIndex();
if(colIdx >= 0) {
this.setSortIndex(colIdx);
break;
}else {
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
dojo.stopEvent(e);
}
break;
case dk.TAB:
this.focus[e.shiftKey ? 'previousKey' : 'nextKey'](e);
break;
case dk.LEFT_ARROW:
case dk.RIGHT_ARROW:
if(!this.edit.isEditing()){
dojo.stopEvent(e);
var offset = (e.keyCode == dk.LEFT_ARROW) ? 1 : -1;
if(dojo._isBodyLtr()){ offset *= -1; }
this.focus.move(0, offset);
}
break;
case dk.UP_ARROW:
if(!this.edit.isEditing() && this.focus.rowIndex != 0){
dojo.stopEvent(e);
this.focus.move(-1, 0);
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
break;
case dk.DOWN_ARROW:
if(!this.edit.isEditing() && this.store && this.focus.rowIndex+1 != this.rowCount){
dojo.stopEvent(e);
this.focus.move(1, 0);
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
break;
case dk.PAGE_UP:
if(!this.edit.isEditing() && this.focus.rowIndex != 0){
dojo.stopEvent(e);
if(this.focus.rowIndex != this.scroller.firstVisibleRow+1){
this.focus.move(this.scroller.firstVisibleRow-this.focus.rowIndex, 0);
}else{
this.setScrollTop(this.scroller.findScrollTop(this.focus.rowIndex-1));
this.focus.move(this.scroller.firstVisibleRow-this.scroller.lastVisibleRow+1, 0);
}
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
break;
case dk.PAGE_DOWN:
if(!this.edit.isEditing() && this.focus.rowIndex+1 != this.rowCount){
dojo.stopEvent(e);
if(this.focus.rowIndex != this.scroller.lastVisibleRow-1){
this.focus.move(this.scroller.lastVisibleRow-this.focus.rowIndex-1, 0);
}else{
this.setScrollTop(this.scroller.findScrollTop(this.focus.rowIndex+1));
this.focus.move(this.scroller.lastVisibleRow-this.scroller.firstVisibleRow-1, 0);
}
this.selection.clickSelect(this.focus.rowIndex, dojo.dnd.getCopyKeyState(e), e.shiftKey);
}
break;
}
},
onMouseOver: function(e){
// summary:
// Event fired when mouse is over the grid.
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
e.rowIndex == -1 ? this.onHeaderCellMouseOver(e) : this.onCellMouseOver(e);
},
onMouseOut: function(e){
// summary:
// Event fired when mouse moves out of the grid.
// e: Event
// Decorated event object that contains reference to grid, cell, and rowIndex
e.rowIndex == -1 ? this.onHeaderCellMouseOut(e) : this.onCellMouseOut(e);
},
onMouseDown: function(e){
// summary:
// Event fired when mouse is down inside grid.
// e: Event
// Decorated event object that contains reference to grid, cell, and rowIndex
e.rowIndex == -1 ? this.onHeaderCellMouseDown(e) : this.onCellMouseDown(e);
},
onMouseOverRow: function(e){
// summary:
// Event fired when mouse is over any row (data or header).
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
if(!this.rows.isOver(e.rowIndex)){
this.rows.setOverRow(e.rowIndex);
e.rowIndex == -1 ? this.onHeaderMouseOver(e) : this.onRowMouseOver(e);
}
},
onMouseOutRow: function(e){
// summary:
// Event fired when mouse moves out of any row (data or header).
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
if(this.rows.isOver(-1)){
this.onHeaderMouseOut(e);
}else if(!this.rows.isOver(-2)){
this.rows.setOverRow(-2);
this.onRowMouseOut(e);
}
},
onMouseDownRow: function(e){
// summary:
// Event fired when mouse is down inside grid row
// e: Event
// Decorated event object that contains reference to grid, cell, and rowIndex
if(e.rowIndex != -1)
this.onRowMouseDown(e);
},
// cell events
onCellMouseOver: function(e){
// summary:
// Event fired when mouse is over a cell.
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
if(e.cellNode){
dojo.addClass(e.cellNode, this.cellOverClass);
}
},
onCellMouseOut: function(e){
// summary:
// Event fired when mouse moves out of a cell.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
if(e.cellNode){
dojo.removeClass(e.cellNode, this.cellOverClass);
}
},
onCellMouseDown: function(e){
// summary:
// Event fired when mouse is down in a header cell.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onCellClick: function(e){
// summary:
// Event fired when a cell is clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this._click[0] = this._click[1];
this._click[1] = e;
if(!this.edit.isEditCell(e.rowIndex, e.cellIndex)){
this.focus.setFocusCell(e.cell, e.rowIndex);
}
this.onRowClick(e);
},
onCellDblClick: function(e){
// summary:
// Event fired when a cell is double-clicked.
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
if(dojo.isIE){
this.edit.setEditCell(this._click[1].cell, this._click[1].rowIndex);
}else if(this._click[0].rowIndex != this._click[1].rowIndex){
this.edit.setEditCell(this._click[0].cell, this._click[0].rowIndex);
}else{
this.edit.setEditCell(e.cell, e.rowIndex);
}
this.onRowDblClick(e);
},
onCellContextMenu: function(e){
// summary:
// Event fired when a cell context menu is accessed via mouse right click.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this.onRowContextMenu(e);
},
onCellFocus: function(inCell, inRowIndex){
// summary:
// Event fired when a cell receives focus.
// inCell: Object
// Cell object containing properties of the grid column.
// inRowIndex: Integer
// Index of the grid row
this.edit.cellFocus(inCell, inRowIndex);
},
// row events
onRowClick: function(e){
// summary:
// Event fired when a row is clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this.edit.rowClick(e);
this.selection.clickSelectEvent(e);
},
onRowDblClick: function(e){
// summary:
// Event fired when a row is double clicked.
// e: Event
// decorated event object which contains reference to grid, cell, and rowIndex
},
onRowMouseOver: function(e){
// summary:
// Event fired when mouse moves over a data row.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onRowMouseOut: function(e){
// summary:
// Event fired when mouse moves out of a data row.
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
},
onRowMouseDown: function(e){
// summary:
// Event fired when mouse is down in a row.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onRowContextMenu: function(e){
// summary:
// Event fired when a row context menu is accessed via mouse right click.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
dojo.stopEvent(e);
},
// header events
onHeaderMouseOver: function(e){
// summary:
// Event fired when mouse moves over the grid header.
// e: Event
// Decorated event object contains reference to grid, cell, and rowIndex
},
onHeaderMouseOut: function(e){
// summary:
// Event fired when mouse moves out of the grid header.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onHeaderCellMouseOver: function(e){
// summary:
// Event fired when mouse moves over a header cell.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
if(e.cellNode){
dojo.addClass(e.cellNode, this.cellOverClass);
}
},
onHeaderCellMouseOut: function(e){
// summary:
// Event fired when mouse moves out of a header cell.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
if(e.cellNode){
dojo.removeClass(e.cellNode, this.cellOverClass);
}
},
onHeaderCellMouseDown: function(e) {
// summary:
// Event fired when mouse is down in a header cell.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onHeaderClick: function(e){
// summary:
// Event fired when the grid header is clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onHeaderCellClick: function(e){
// summary:
// Event fired when a header cell is clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this.setSortIndex(e.cell.index);
this.onHeaderClick(e);
},
onHeaderDblClick: function(e){
// summary:
// Event fired when the grid header is double clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
},
onHeaderCellDblClick: function(e){
// summary:
// Event fired when a header cell is double clicked.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this.onHeaderDblClick(e);
},
onHeaderCellContextMenu: function(e){
// summary:
// Event fired when a header cell context menu is accessed via mouse right click.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
this.onHeaderContextMenu(e);
},
onHeaderContextMenu: function(e){
// summary:
// Event fired when the grid header context menu is accessed via mouse right click.
// e: Event
// Decorated event object which contains reference to grid, cell, and rowIndex
if(!this.headerMenu){
dojo.stopEvent(e);
}
},
// editing
onStartEdit: function(inCell, inRowIndex){
// summary:
// Event fired when editing is started for a given grid cell
// inCell: Object
// Cell object containing properties of the grid column.
// inRowIndex: Integer
// Index of the grid row
},
onApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
// summary:
// Event fired when editing is applied for a given grid cell
// inValue: String
// Value from cell editor
// inRowIndex: Integer
// Index of the grid row
// inFieldIndex: Integer
// Index in the grid's data store
},
onCancelEdit: function(inRowIndex){
// summary:
// Event fired when editing is cancelled for a given grid cell
// inRowIndex: Integer
// Index of the grid row
},
onApplyEdit: function(inRowIndex){
// summary:
// Event fired when editing is applied for a given grid row
// inRowIndex: Integer
// Index of the grid row
},
onCanSelect: function(inRowIndex){
// summary:
// Event to determine if a grid row may be selected
// inRowIndex: Integer
// Index of the grid row
// returns: Boolean
// true if the row can be selected
return true;
},
onCanDeselect: function(inRowIndex){
// summary:
// Event to determine if a grid row may be deselected
// inRowIndex: Integer
// Index of the grid row
// returns: Boolean
// true if the row can be deselected
return true;
},
onSelected: function(inRowIndex){
// summary:
// Event fired when a grid row is selected
// inRowIndex: Integer
// Index of the grid row
this.updateRowStyles(inRowIndex);
},
onDeselected: function(inRowIndex){
// summary:
// Event fired when a grid row is deselected
// inRowIndex: Integer
// Index of the grid row
this.updateRowStyles(inRowIndex);
},
onSelectionChanged: function(){
}
});
}
if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.i18n"] = true;
dojo.provide("dojo.i18n");
/*=====
dojo.i18n = {
// summary: Utility classes to enable loading of resources for internationalization (i18n)
};
=====*/
dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
// summary:
// Returns an Object containing the localization for a given resource
// bundle in a package, matching the specified locale.
// description:
// Returns a hash containing name/value pairs in its prototypesuch
// that values can be easily overridden. Throws an exception if the
// bundle is not found. Bundle must have already been loaded by
// `dojo.requireLocalization()` or by a build optimization step. NOTE:
// try not to call this method as part of an object property
// definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In
// some loading situations, the bundle may not be available in time
// for the object definition. Instead, call this method inside a
// function that is run after all modules load or the page loads (like
// in `dojo.addOnLoad()`), or in a widget lifecycle method.
// packageName:
// package which is associated with this resource
// bundleName:
// the base filename of the resource bundle (without the ".js" suffix)
// locale:
// the variant to load (optional). By default, the locale defined by
// the host environment: dojo.locale
locale = dojo.i18n.normalizeLocale(locale);
// look for nearest locale match
var elements = locale.split('-');
var module = [packageName,"nls",bundleName].join('.');
var bundle = dojo._loadedModules[module];
if(bundle){
var localization;
for(var i = elements.length; i > 0; i--){
var loc = elements.slice(0, i).join('_');
if(bundle[loc]){
localization = bundle[loc];
break;
}
}
if(!localization){
localization = bundle.ROOT;
}
// make a singleton prototype so that the caller won't accidentally change the values globally
if(localization){
var clazz = function(){};
clazz.prototype = localization;
return new clazz(); // Object
}
}
throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
};
dojo.i18n.normalizeLocale = function(/*String?*/locale){
// summary:
// Returns canonical form of locale, as used by Dojo.
//
// description:
// All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
// If no locale is specified, the dojo.locale is returned. dojo.locale is defined by
// the user agent's locale unless overridden by djConfig.
var result = locale ? locale.toLowerCase() : dojo.locale;
if(result == "root"){
result = "ROOT";
}
return result; // String
};
dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
// summary:
// See dojo.requireLocalization()
// description:
// Called by the bootstrap, but factored out so that it is only
// included in the build when needed.
var targetLocale = dojo.i18n.normalizeLocale(locale);
var bundlePackage = [moduleName, "nls", bundleName].join(".");
// NOTE:
// When loading these resources, the packaging does not match what is
// on disk. This is an implementation detail, as this is just a
// private data structure to hold the loaded resources. e.g.
// `tests/hello/nls/en-us/salutations.js` is loaded as the object
// `tests.hello.nls.salutations.en_us={...}` The structure on disk is
// intended to be most convenient for developers and translators, but
// in memory it is more logical and efficient to store in a different
// order. Locales cannot use dashes, since the resulting path will
// not evaluate as valid JS, so we translate them to underscores.
//Find the best-match locale to load if we have available flat locales.
var bestLocale = "";
if(availableFlatLocales){
var flatLocales = availableFlatLocales.split(",");
for(var i = 0; i < flatLocales.length; i++){
//Locale must match from start of string.
//Using ["indexOf"] so customBase builds do not see
//this as a dojo._base.array dependency.
if(targetLocale["indexOf"](flatLocales[i]) == 0){
if(flatLocales[i].length > bestLocale.length){
bestLocale = flatLocales[i];
}
}
}
if(!bestLocale){
bestLocale = "ROOT";
}
}
//See if the desired locale is already loaded.
var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
var bundle = dojo._loadedModules[bundlePackage];
var localizedBundle = null;
if(bundle){
if(dojo.config.localizationComplete && bundle._built){return;}
var jsLoc = tempLocale.replace(/-/g, '_');
var translationPackage = bundlePackage+"."+jsLoc;
localizedBundle = dojo._loadedModules[translationPackage];
}
if(!localizedBundle){
bundle = dojo["provide"](bundlePackage);
var syms = dojo._getModuleSymbols(moduleName);
var modpath = syms.concat("nls").join("/");
var parent;
dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
var jsLoc = loc.replace(/-/g, '_');
var translationPackage = bundlePackage + "." + jsLoc;
var loaded = false;
if(!dojo._loadedModules[translationPackage]){
// Mark loaded whether it's found or not, so that further load attempts will not be made
dojo["provide"](translationPackage);
var module = [modpath];
if(loc != "ROOT"){module.push(loc);}
module.push(bundleName);
var filespec = module.join("/") + '.js';
loaded = dojo._loadPath(filespec, null, function(hash){
// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
var clazz = function(){};
clazz.prototype = parent;
bundle[jsLoc] = new clazz();
for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
});
}else{
loaded = true;
}
if(loaded && bundle[jsLoc]){
parent = bundle[jsLoc];
}else{
bundle[jsLoc] = parent;
}
if(availableFlatLocales){
//Stop the locale path searching if we know the availableFlatLocales, since
//the first call to this function will load the only bundle that is needed.
return true;
}
});
}
//Save the best locale bundle as the target locale bundle when we know the
//the available bundles.
if(availableFlatLocales && targetLocale != bestLocale){
bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
}
};
(function(){
// If other locales are used, dojo.requireLocalization should load them as
// well, by default.
//
// Override dojo.requireLocalization to do load the default bundle, then
// iterate through the extraLocale list and load those translations as
// well, unless a particular locale was requested.
var extra = dojo.config.extraLocale;
if(extra){
if(!extra instanceof Array){
extra = [extra];
}
var req = dojo.i18n._requireLocalization;
dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
req(m,b,locale, availableFlatLocales);
if(locale){return;}
for(var i=0; i<extra.length; i++){
req(m,b,extra[i], availableFlatLocales);
}
};
}
})();
dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
// summary:
// A helper method to assist in searching for locale-based resources.
// Will iterate through the variants of a particular locale, either up
// or down, executing a callback function. For example, "en-us" and
// true will try "en-us" followed by "en" and finally "ROOT".
locale = dojo.i18n.normalizeLocale(locale);
var elements = locale.split('-');
var searchlist = [];
for(var i = elements.length; i > 0; i--){
searchlist.push(elements.slice(0, i).join('-'));
}
searchlist.push(false);
if(down){searchlist.reverse();}
for(var j = searchlist.length - 1; j >= 0; j--){
var loc = searchlist[j] || "ROOT";
var stop = searchFunc(loc);
if(stop){ break; }
}
};
dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
// summary:
// Load built, flattened resource bundles, if available for all
// locales used in the page. Only called by built layer files.
function preload(locale){
locale = dojo.i18n.normalizeLocale(locale);
dojo.i18n._searchLocalePath(locale, true, function(loc){
for(var i=0; i<localesGenerated.length;i++){
if(localesGenerated[i] == loc){
dojo["require"](bundlePrefix+"_"+loc);
return true; // Boolean
}
}
return false; // Boolean
});
}
preload();
var extra = dojo.config.extraLocale||[];
for(var i=0; i<extra.length; i++){
preload(extra[i]);
}
};
}
if(!dojo._hasResource["dojox.grid._Grid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid._Grid"] = true;
dojo.provide("dojox.grid._Grid");
(function(){
var jobs = {
cancel: function(inHandle){
if(inHandle){
clearTimeout(inHandle);
}
},
jobs: [],
job: function(inName, inDelay, inJob){
jobs.cancelJob(inName);
var job = function(){
delete jobs.jobs[inName];
inJob();
}
jobs.jobs[inName] = setTimeout(job, inDelay);
},
cancelJob: function(inName){
jobs.cancel(jobs.jobs[inName]);
}
};
/*=====
dojox.grid.__CellDef = function(){
// name: String?
// The text to use in the header of the grid for this cell.
// get: Function?
// function(rowIndex){} rowIndex is of type Integer. This
// function will be called when a cell requests data. Returns the
// unformatted data for the cell.
// value: String?
// If "get" is not specified, this is used as the data for the cell.
// defaultValue: String?
// If "get" and "value" aren't specified or if "get" returns an undefined
// value, this is used as the data for the cell. "formatter" is not run
// on this if "get" returns an undefined value.
// formatter: Function?
// function(data, rowIndex){} data is of type anything, rowIndex
// is of type Integer. This function will be called after the cell
// has its data but before it passes it back to the grid to render.
// Returns the formatted version of the cell's data.
// type: dojox.grid.cells._Base|Function?
// TODO
// editable: Boolean?
// Whether this cell should be editable or not.
// hidden: Boolean?
// If true, the cell will not be displayed.
// noresize: Boolean?
// If true, the cell will not be able to be resized.
// width: Integer|String?
// A CSS size. If it's an Integer, the width will be in em's.
// colSpan: Integer?
// How many columns to span this cell. Will not work in the first
// sub-row of cells.
// rowSpan: Integer?
// How many sub-rows to span this cell.
// styles: String?
// A string of styles to apply to both the header cell and main
// grid cells. Must end in a ';'.
// headerStyles: String?
// A string of styles to apply to just the header cell. Must end
// in a ';'
// cellStyles: String?
// A string of styles to apply to just the main grid cells. Must
// end in a ';'
// classes: String?
// A space separated list of classes to apply to both the header
// cell and the main grid cells.
// headerClasses: String?
// A space separated list of classes to apply to just the header
// cell.
// cellClasses: String?
// A space separated list of classes to apply to just the main
// grid cells.
// attrs: String?
// A space separated string of attribute='value' pairs to add to
// the header cell element and main grid cell elements.
this.name = name;
this.value = value;
this.get = get;
this.formatter = formatter;
this.type = type;
this.editable = editable;
this.hidden = hidden;
this.width = width;
this.colSpan = colSpan;
this.rowSpan = rowSpan;
this.styles = styles;
this.headerStyles = headerStyles;
this.cellStyles = cellStyles;
this.classes = classes;
this.headerClasses = headerClasses;
this.cellClasses = cellClasses;
this.attrs = attrs;
}
=====*/
/*=====
dojox.grid.__ViewDef = function(){
// noscroll: Boolean?
// If true, no scrollbars will be rendered without scrollbars.
// width: Integer|String?
// A CSS size. If it's an Integer, the width will be in em's. If
// "noscroll" is true, this value is ignored.
// cells: dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]]?
// The structure of the cells within this grid.
// type: String?
// A string containing the constructor of a subclass of
// dojox.grid._View. If this is not specified, dojox.grid._View
// is used.
// defaultCell: dojox.grid.__CellDef?
// A cell definition with default values for all cells in this view. If
// a property is defined in a cell definition in the "cells" array and
// this property, the cell definition's property will override this
// property's property.
// onBeforeRow: Function?
// function(rowIndex, cells){} rowIndex is of type Integer, cells
// is of type Array[dojox.grid.__CellDef[]]. This function is called
// before each row of data is rendered. Before the header is
// rendered, rowIndex will be -1. "cells" is a reference to the
// internal structure of this view's cells so any changes you make to
// it will persist between calls.
// onAfterRow: Function?
// function(rowIndex, cells, rowNode){} rowIndex is of type Integer, cells
// is of type Array[dojox.grid.__CellDef[]], rowNode is of type DOMNode.
// This function is called after each row of data is rendered. After the
// header is rendered, rowIndex will be -1. "cells" is a reference to the
// internal structure of this view's cells so any changes you make to
// it will persist between calls.
this.noscroll = noscroll;
this.width = width;
this.cells = cells;
this.type = type;
this.defaultCell = defaultCell;
this.onBeforeRow = onBeforeRow;
this.onAfterRow = onAfterRow;
}
=====*/
dojo.declare('dojox.grid._Grid',
[ dijit._Widget, dijit._Templated, dojox.grid._Events ],
{
// summary:
// A grid widget with virtual scrolling, cell editing, complex rows,
// sorting, fixed columns, sizeable columns, etc.
//
// description:
// _Grid provides the full set of grid features without any
// direct connection to a data store.
//
// The grid exposes a get function for the grid, or optionally
// individual columns, to populate cell contents.
//
// The grid is rendered based on its structure, an object describing
// column and cell layout.
//
// example:
// A quick sample:
//
// define a get function
// | function get(inRowIndex){ // called in cell context
// | return [this.index, inRowIndex].join(', ');
// | }
//
// define the grid structure:
// | var structure = [ // array of view objects
// | { cells: [// array of rows, a row is an array of cells
// | [
// | { name: "Alpha", width: 6 },
// | { name: "Beta" },
// | { name: "Gamma", get: get }]
// | ]}
// | ];
//
// | <div id="grid"
// | rowCount="100" get="get"
// | structure="structure"
// | dojoType="dojox.grid._Grid"></div>
templateString:"<div class=\"dojoxGrid\" hidefocus=\"hidefocus\" role=\"wairole:grid\" dojoAttachEvent=\"onmouseout:_mouseOut\">\n\t<div class=\"dojoxGridMasterHeader\" dojoAttachPoint=\"viewsHeaderNode\" tabindex=\"-1\"></div>\n\t<div class=\"dojoxGridMasterView\" dojoAttachPoint=\"viewsNode\"></div>\n\t<div class=\"dojoxGridMasterMessages\" style=\"display: none;\" dojoAttachPoint=\"messagesNode\"></div>\n\t<span dojoAttachPoint=\"lastFocusNode\" tabindex=\"0\"></span>\n</div>\n",
// classTag: String
// CSS class applied to the grid's domNode
classTag: 'dojoxGrid',
get: function(inRowIndex){
// summary: Default data getter.
// description:
// Provides data to display in a grid cell. Called in grid cell context.
// So this.cell.index is the column index.
// inRowIndex: Integer
// Row for which to provide data
// returns:
// Data to display for a given grid cell.
},
// settings
// rowCount: Integer
// Number of rows to display.
rowCount: 5,
// keepRows: Integer
// Number of rows to keep in the rendering cache.
keepRows: 75,
// rowsPerPage: Integer
// Number of rows to render at a time.
rowsPerPage: 25,
// autoWidth: Boolean
// If autoWidth is true, grid width is automatically set to fit the data.
autoWidth: false,
// autoHeight: Boolean|Integer
// If autoHeight is true, grid height is automatically set to fit the data.
// If it is an integer, the height will be automatically set to fit the data
// if there are fewer than that many rows - and the height will be set to show
// that many rows if there are more
autoHeight: '',
// autoRender: Boolean
// If autoRender is true, grid will render itself after initialization.
autoRender: true,
// defaultHeight: String
// default height of the grid, measured in any valid css unit.
defaultHeight: '15em',
// height: String
// explicit height of the grid, measured in any valid css unit. This will be populated (and overridden)
// if the height: css attribute exists on the source node.
height: '',
// structure: dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]]
// View layout defintion.
structure: null,
// elasticView: Integer
// Override defaults and make the indexed grid view elastic, thus filling available horizontal space.
elasticView: -1,
// singleClickEdit: boolean
// Single-click starts editing. Default is double-click
singleClickEdit: false,
// selectionMode: String
// Set the selection mode of grid's Selection. Value must be 'single', 'multiple',
// or 'extended'. Default is 'extended'.
selectionMode: 'extended',
// rowSelector: Boolean|String
// If set to true, will add a row selector view to this grid. If set to a CSS width, will add
// a row selector of that width to this grid.
rowSelector: '',
// columnReordering: Boolean
// If set to true, will add drag and drop reordering to views with one row of columns.
columnReordering: false,
// headerMenu: dijit.Menu
// If set to a dijit.Menu, will use this as a context menu for the grid headers.
headerMenu: null,
// placeholderLabel: String
// Label of placeholders to search for in the header menu to replace with column toggling
// menu items.
placeholderLabel: "GridColumns",
// selectable: Boolean
// Set to true if you want to be able to select the text within the grid.
selectable: false,
// Used to store the last two clicks, to ensure double-clicking occurs based on the intended row
_click: null,
// loadingMessage: String
// Message that shows while the grid is loading
loadingMessage: "<span class='dojoxGridLoading'>${loadingState}</span>",
// errorMessage: String
// Message that shows when the grid encounters an error loading
errorMessage: "<span class='dojoxGridError'>${errorState}</span>",
// noDataMessage: String
// Message that shows if the grid has no data - wrap it in a
// span with class 'dojoxGridNoData' if you want it to be
// styled similar to the loading and error messages
noDataMessage: "",
// private
sortInfo: 0,
themeable: true,
_placeholders: null,
// initialization
buildRendering: function(){
this.inherited(arguments);
// reset get from blank function (needed for markup parsing) to null, if not changed
if(this.get == dojox.grid._Grid.prototype.get){
this.get = null;
}
if(!this.domNode.getAttribute('tabIndex')){
this.domNode.tabIndex = "0";
}
this.createScroller();
this.createLayout();
this.createViews();
this.createManagers();
this.createSelection();
this.connect(this.selection, "onSelected", "onSelected");
this.connect(this.selection, "onDeselected", "onDeselected");
this.connect(this.selection, "onChanged", "onSelectionChanged");
dojox.html.metrics.initOnFontResize();
this.connect(dojox.html.metrics, "onFontResize", "textSizeChanged");
dojox.grid.util.funnelEvents(this.domNode, this, 'doKeyEvent', dojox.grid.util.keyEvents);
this.connect(this, "onShow", "renderOnIdle");
},
postMixInProperties: function(){
this.inherited(arguments);
var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
if(this.srcNodeRef && this.srcNodeRef.style.height){
this.height = this.srcNodeRef.style.height;
}
// Call this to update our autoheight to start out
this._setAutoHeightAttr(this.autoHeight, true);
},
postCreate: function(){
// replace stock styleChanged with one that triggers an update
this.styleChanged = this._styleChanged;
this._placeholders = [];
this._setHeaderMenuAttr(this.headerMenu);
this._setStructureAttr(this.structure);
this._click = [];
},
destroy: function(){
this.domNode.onReveal = null;
this.domNode.onSizeChange = null;
// Fixes IE domNode leak
delete this._click;
this.edit.destroy();
delete this.edit;
this.views.destroyViews();
if(this.scroller){
this.scroller.destroy();
delete this.scroller;
}
if(this.focus){
this.focus.destroy();
delete this.focus;
}
if(this.headerMenu&&this._placeholders.length){
dojo.forEach(this._placeholders, function(p){ p.unReplace(true); });
this.headerMenu.unBindDomNode(this.viewsHeaderNode);
}
this.inherited(arguments);
},
_setAutoHeightAttr: function(ah, skipRender){
// Calculate our autoheight - turn it into a boolean or an integer
if(typeof ah == "string"){
if(!ah || ah == "false"){
ah = false;
}else if (ah == "true"){
ah = true;
}else{
ah = window.parseInt(ah, 10);
if(isNaN(ah)){
ah = false;
}
// Autoheight must be at least 1, if it's a number. If it's
// less than 0, we'll take that to mean "all" rows (same as
// autoHeight=true - if it is equal to zero, we'll take that
// to mean autoHeight=false
if(ah < 0){
ah = true;
}else if (ah === 0){
ah = false;
}
}
}
this.autoHeight = ah;
if(typeof ah == "boolean"){
this._autoHeight = ah;
}else if(typeof ah == "number"){
this._autoHeight = (ah >= this.attr('rowCount'));
}else{
this._autoHeight = false;
}
if(this._started && !skipRender){
this.render();
}
},
_getRowCountAttr: function(){
return this.updating && this.invalidated && this.invalidated.rowCount != undefined ?
this.invalidated.rowCount : this.rowCount;
},
styleChanged: function(){
this.setStyledClass(this.domNode, '');
},
_styleChanged: function(){
this.styleChanged();
this.update();
},
textSizeChanged: function(){
setTimeout(dojo.hitch(this, "_textSizeChanged"), 1);
},
_textSizeChanged: function(){
if(this.domNode){
this.views.forEach(function(v){
v.content.update();
});
this.render();
}
},
sizeChange: function(){
jobs.job(this.id + 'SizeChange', 50, dojo.hitch(this, "update"));
},
renderOnIdle: function() {
setTimeout(dojo.hitch(this, "render"), 1);
},
createManagers: function(){
// summary:
// create grid managers for various tasks including rows, focus, selection, editing
// row manager
this.rows = new dojox.grid._RowManager(this);
// focus manager
this.focus = new dojox.grid._FocusManager(this);
// edit manager
this.edit = new dojox.grid._EditManager(this);
},
createSelection: function(){
// summary: Creates a new Grid selection manager.
// selection manager
this.selection = new dojox.grid.Selection(this);
},
createScroller: function(){
// summary: Creates a new virtual scroller
this.scroller = new dojox.grid._Scroller();
this.scroller.grid = this;
this.scroller._pageIdPrefix = this.id + '-';
this.scroller.renderRow = dojo.hitch(this, "renderRow");
this.scroller.removeRow = dojo.hitch(this, "rowRemoved");
},
createLayout: function(){
// summary: Creates a new Grid layout
this.layout = new dojox.grid._Layout(this);
this.connect(this.layout, "moveColumn", "onMoveColumn");
},
onMoveColumn: function(){
this.render();
this._resize();
},
// views
createViews: function(){
this.views = new dojox.grid._ViewManager(this);
this.views.createView = dojo.hitch(this, "createView");
},
createView: function(inClass, idx){
var c = dojo.getObject(inClass);
var view = new c({ grid: this, index: idx });
this.viewsNode.appendChild(view.domNode);
this.viewsHeaderNode.appendChild(view.headerNode);
this.views.addView(view);
return view;
},
buildViews: function(){
for(var i=0, vs; (vs=this.layout.structure[i]); i++){
this.createView(vs.type || dojox._scopeName + ".grid._View", i).setStructure(vs);
}
this.scroller.setContentNodes(this.views.getContentNodes());
},
_setStructureAttr: function(structure){
var s = structure;
if(s && dojo.isString(s)){
dojo.deprecated("dojox.grid._Grid.attr('structure', 'objVar')", "use dojox.grid._Grid.attr('structure', objVar) instead", "2.0");
s=dojo.getObject(s);
}
this.structure = s;
if(!s){
if(this.layout.structure){
s = this.layout.structure;
}else{
return;
}
}
this.views.destroyViews();
if(s !== this.layout.structure){
this.layout.setStructure(s);
}
this._structureChanged();
},
setStructure: function(/* dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]] */ inStructure){
// summary:
// Install a new structure and rebuild the grid.
dojo.deprecated("dojox.grid._Grid.setStructure(obj)", "use dojox.grid._Grid.attr('structure', obj) instead.", "2.0");
this._setStructureAttr(inStructure);
},
getColumnTogglingItems: function(){
// Summary: returns an array of dijit.CheckedMenuItem widgets that can be
// added to a menu for toggling columns on and off.
return dojo.map(this.layout.cells, function(cell){
if(!cell.menuItems){ cell.menuItems = []; }
var self = this;
var item = new dijit.CheckedMenuItem({
label: cell.name,
checked: !cell.hidden,
_gridCell: cell,
onChange: function(checked){
if(self.layout.setColumnVisibility(this._gridCell.index, checked)){
var items = this._gridCell.menuItems;
if(items.length > 1){
dojo.forEach(items, function(item){
if(item !== this){
item.setAttribute("checked", checked);
}
}, this);
}
var checked = dojo.filter(self.layout.cells, function(c){
if(c.menuItems.length > 1){
dojo.forEach(c.menuItems, "item.attr('disabled', false);");
}else{
c.menuItems[0].attr('disabled', false);
}
return !c.hidden;
});
if(checked.length == 1){
dojo.forEach(checked[0].menuItems, "item.attr('disabled', true);");
}
}
},
destroy: function(){
var index = dojo.indexOf(this._gridCell.menuItems, this);
this._gridCell.menuItems.splice(index, 1);
delete this._gridCell;
dijit.CheckedMenuItem.prototype.destroy.apply(this, arguments);
}
});
cell.menuItems.push(item);
return item;
}, this); // dijit.CheckedMenuItem[]
},
_setHeaderMenuAttr: function(menu){
if(this._placeholders.length){
dojo.forEach(this._placeholders, function(p){
p.unReplace(true);
});
this._placeholders = [];
}
if(this.headerMenu){
this.headerMenu.unBindDomNode(this.viewsHeaderNode);
}
this.headerMenu = menu;
if(!menu){ return; }
this.headerMenu.bindDomNode(this.viewsHeaderNode);
if(this.headerMenu.getPlaceholders){
this._placeholders = this.headerMenu.getPlaceholders(this.placeholderLabel);
}
},
setHeaderMenu: function(/* dijit.Menu */ menu){
dojo.deprecated("dojox.grid._Grid.setHeaderMenu(obj)", "use dojox.grid._Grid.attr('headerMenu', obj) instead.", "2.0");
this._setHeaderMenuAttr(menu);
},
setupHeaderMenu: function(){
if(this._placeholders && this._placeholders.length){
dojo.forEach(this._placeholders, function(p){
if(p._replaced){
p.unReplace(true);
}
p.replace(this.getColumnTogglingItems());
}, this);
}
},
_fetch: function(start){
this.setScrollTop(0);
},
getItem: function(inRowIndex){
return null;
},
showMessage: function(message){
if(message){
this.messagesNode.innerHTML = message;
this.messagesNode.style.display = "";
}else{
this.messagesNode.innerHTML = "";
this.messagesNode.style.display = "none";
}
},
_structureChanged: function() {
this.buildViews();
if(this.autoRender && this._started){
this.render();
}
},
hasLayout: function() {
return this.layout.cells.length;
},
// sizing
resize: function(changeSize, resultSize){
// summary:
// Update the grid's rendering dimensions and resize it
this._resize(changeSize, resultSize);
this.sizeChange();
},
_getPadBorder: function() {
this._padBorder = this._padBorder || dojo._getPadBorderExtents(this.domNode);
return this._padBorder;
},
_getHeaderHeight: function(){
var vns = this.viewsHeaderNode.style, t = vns.display == "none" ? 0 : this.views.measureHeader();
vns.height = t + 'px';
// header heights are reset during measuring so must be normalized after measuring.
this.views.normalizeHeaderNodeHeight();
return t;
},
_resize: function(changeSize, resultSize){
// if we have set up everything except the DOM, we cannot resize
var pn = this.domNode.parentNode;
if(!pn || pn.nodeType != 1 || !this.hasLayout() || pn.style.visibility == "hidden" || pn.style.display == "none"){
return;
}
// useful measurement
var padBorder = this._getPadBorder();
var hh = 0;
// grid height
if(this._autoHeight){
this.domNode.style.height = 'auto';
this.viewsNode.style.height = '';
}else if(typeof this.autoHeight == "number"){
var h = hh = this._getHeaderHeight();
h += (this.scroller.averageRowHeight * this.autoHeight);
this.domNode.style.height = h + "px";
}else if(this.flex > 0){
}else if(this.domNode.clientHeight <= padBorder.h){
if(pn == document.body){
this.domNode.style.height = this.defaultHeight;
}else if(this.height){
this.domNode.style.height = this.height;
}else{
this.fitTo = "parent";
}
}
// if we are given dimensions, size the grid's domNode to those dimensions
if(resultSize){
changeSize = resultSize;
}
if(changeSize){
dojo.marginBox(this.domNode, changeSize);
this.height = this.domNode.style.height;
delete this.fitTo;
}else if(this.fitTo == "parent"){
var h = dojo._getContentBox(pn).h;
dojo.marginBox(this.domNode, { h: Math.max(0, h) });
}
var h = dojo._getContentBox(this.domNode).h;
if(h == 0 && !this._autoHeight){
// We need to hide the header, since the Grid is essentially hidden.
this.viewsHeaderNode.style.display = "none";
}else{
// Otherwise, show the header and give it an appropriate height.
this.viewsHeaderNode.style.display = "block";
hh = this._getHeaderHeight();
}
// NOTE: it is essential that width be applied before height
// Header height can only be calculated properly after view widths have been set.
// This is because flex column width is naturally 0 in Firefox.
// Therefore prior to width sizing flex columns with spaces are maximally wrapped
// and calculated to be too tall.
this.adaptWidth();
this.adaptHeight(hh);
this.postresize();
},
adaptWidth: function() {
// private: sets width and position for views and update grid width if necessary
var w = this.autoWidth ? 0 : this.domNode.clientWidth || (this.domNode.offsetWidth - this._getPadBorder().w),
vw = this.views.arrange(1, w);
this.views.onEach("adaptWidth");
if (this.autoWidth)
this.domNode.style.width = vw + "px";
},
adaptHeight: function(inHeaderHeight){
// private: measures and normalizes header height, then sets view heights, and then updates scroller
// content extent
var t = inHeaderHeight || this._getHeaderHeight();
var h = (this._autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
this.views.onEach('setSize', [0, h]);
this.views.onEach('adaptHeight');
if(!this._autoHeight){
var numScroll = 0, numNoScroll = 0;
var noScrolls = dojo.filter(this.views.views, function(v){
var has = v.hasHScrollbar();
if(has){ numScroll++; }else{ numNoScroll++; }
return (!has);
});
if(numScroll > 0 && numNoScroll > 0){
dojo.forEach(noScrolls, function(v){
v.adaptHeight(true);
});
}
}
if(this.autoHeight === true || h != -1 || (typeof this.autoHeight == "number" && this.autoHeight >= this.attr('rowCount'))){
this.scroller.windowHeight = h;
}else{
this.scroller.windowHeight = Math.max(this.domNode.clientHeight - t, 0);
}
},
// startup
startup: function(){
if(this._started){return;}
this.inherited(arguments);
if(this.autoRender){
this.render();
}
},
// render
render: function(){
// summary:
// Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and
// scrolling states, see Update.
if(!this.domNode){return;}
if(!this._started){return;}
if(!this.hasLayout()) {
this.scroller.init(0, this.keepRows, this.rowsPerPage);
return;
}
//
this.update = this.defaultUpdate;
this._render();
},
_render: function(){
this.scroller.init(this.attr('rowCount'), this.keepRows, this.rowsPerPage);
this.prerender();
this.setScrollTop(0);
this.postrender();
},
prerender: function(){
// if autoHeight, make sure scroller knows not to virtualize; everything must be rendered.
this.keepRows = this._autoHeight ? 0 : this.keepRows;
this.scroller.setKeepInfo(this.keepRows);
this.views.render();
this._resize();
},
postrender: function(){
this.postresize();
this.focus.initFocusView();
// make rows unselectable
dojo.setSelectable(this.domNode, this.selectable);
},
postresize: function(){
// views are position absolute, so they do not inflate the parent
if(this._autoHeight){
var size = Math.max(this.views.measureContent()) + 'px';
this.viewsNode.style.height = size;
}
},
renderRow: function(inRowIndex, inNodes){
// summary: private, used internally to render rows
this.views.renderRow(inRowIndex, inNodes);
},
rowRemoved: function(inRowIndex){
// summary: private, used internally to remove rows
this.views.rowRemoved(inRowIndex);
},
invalidated: null,
updating: false,
beginUpdate: function(){
// summary:
// Use to make multiple changes to rows while queueing row updating.
// NOTE: not currently supporting nested begin/endUpdate calls
this.invalidated = [];
this.updating = true;
},
endUpdate: function(){
// summary:
// Use after calling beginUpdate to render any changes made to rows.
this.updating = false;
var i = this.invalidated, r;
if(i.all){
this.update();
}else if(i.rowCount != undefined){
this.updateRowCount(i.rowCount);
}else{
for(r in i){
this.updateRow(Number(r));
}
}
this.invalidated = null;
},
// update
defaultUpdate: function(){
// note: initial update calls render and subsequently this function.
if(!this.domNode){return;}
if(this.updating){
this.invalidated.all = true;
return;
}
//this.edit.saveState(inRowIndex);
var lastScrollTop = this.scrollTop;
this.prerender();
this.scroller.invalidateNodes();
this.setScrollTop(lastScrollTop);
this.postrender();
//this.edit.restoreState(inRowIndex);
},
update: function(){
// summary:
// Update the grid, retaining edit and scrolling states.
this.render();
},
updateRow: function(inRowIndex){
// summary:
// Render a single row.
// inRowIndex: Integer
// Index of the row to render
inRowIndex = Number(inRowIndex);
if(this.updating){
this.invalidated[inRowIndex]=true;
}else{
this.views.updateRow(inRowIndex);
this.scroller.rowHeightChanged(inRowIndex);
}
},
updateRows: function(startIndex, howMany){
// summary:
// Render consecutive rows at once.
// startIndex: Integer
// Index of the starting row to render
// howMany: Integer
// How many rows to update.
startIndex = Number(startIndex);
howMany = Number(howMany);
if(this.updating){
for(var i=0; i<howMany; i++){
this.invalidated[i+startIndex]=true;
}
}else{
for(var i=0; i<howMany; i++){
this.views.updateRow(i+startIndex);
}
this.scroller.rowHeightChanged(startIndex);
}
},
updateRowCount: function(inRowCount){
//summary:
// Change the number of rows.
// inRowCount: int
// Number of rows in the grid.
if(this.updating){
this.invalidated.rowCount = inRowCount;
}else{
this.rowCount = inRowCount;
this._setAutoHeightAttr(this.autoHeight, true);
if(this.layout.cells.length){
this.scroller.updateRowCount(inRowCount);
}
this._resize();
if(this.layout.cells.length){
this.setScrollTop(this.scrollTop);
}
}
},
updateRowStyles: function(inRowIndex){
// summary:
// Update the styles for a row after it's state has changed.
this.views.updateRowStyles(inRowIndex);
},
rowHeightChanged: function(inRowIndex){
// summary:
// Update grid when the height of a row has changed. Row height is handled automatically as rows
// are rendered. Use this function only to update a row's height outside the normal rendering process.
// inRowIndex: Integer
// index of the row that has changed height
this.views.renormalizeRow(inRowIndex);
this.scroller.rowHeightChanged(inRowIndex);
},
// fastScroll: Boolean
// flag modifies vertical scrolling behavior. Defaults to true but set to false for slower
// scroll performance but more immediate scrolling feedback
fastScroll: true,
delayScroll: false,
// scrollRedrawThreshold: int
// pixel distance a user must scroll vertically to trigger grid scrolling.
scrollRedrawThreshold: (dojo.isIE ? 100 : 50),
// scroll methods
scrollTo: function(inTop){
// summary:
// Vertically scroll the grid to a given pixel position
// inTop: Integer
// vertical position of the grid in pixels
if(!this.fastScroll){
this.setScrollTop(inTop);
return;
}
var delta = Math.abs(this.lastScrollTop - inTop);
this.lastScrollTop = inTop;
if(delta > this.scrollRedrawThreshold || this.delayScroll){
this.delayScroll = true;
this.scrollTop = inTop;
this.views.setScrollTop(inTop);
jobs.job('dojoxGridScroll', 200, dojo.hitch(this, "finishScrollJob"));
}else{
this.setScrollTop(inTop);
}
},
finishScrollJob: function(){
this.delayScroll = false;
this.setScrollTop(this.scrollTop);
},
setScrollTop: function(inTop){
this.scroller.scroll(this.views.setScrollTop(inTop));
},
scrollToRow: function(inRowIndex){
// summary:
// Scroll the grid to a specific row.
// inRowIndex: Integer
// grid row index
this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1);
},
// styling (private, used internally to style individual parts of a row)
styleRowNode: function(inRowIndex, inRowNode){
if(inRowNode){
this.rows.styleRowNode(inRowIndex, inRowNode);
}
},
// called when the mouse leaves the grid so we can deselect all hover rows
_mouseOut: function(e){
this.rows.setOverRow(-2);
},
// cells
getCell: function(inIndex){
// summary:
// Retrieves the cell object for a given grid column.
// inIndex: Integer
// Grid column index of cell to retrieve
// returns:
// a grid cell
return this.layout.cells[inIndex];
},
setCellWidth: function(inIndex, inUnitWidth) {
this.getCell(inIndex).unitWidth = inUnitWidth;
},
getCellName: function(inCell){
// summary: Returns the cell name of a passed cell
return "Cell " + inCell.index; // String
},
// sorting
canSort: function(inSortInfo){
// summary:
// Determines if the grid can be sorted
// inSortInfo: Integer
// Sort information, 1-based index of column on which to sort, positive for an ascending sort
// and negative for a descending sort
// returns: Boolean
// True if grid can be sorted on the given column in the given direction
},
sort: function(){
},
getSortAsc: function(inSortInfo){
// summary:
// Returns true if grid is sorted in an ascending direction.
inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
return Boolean(inSortInfo > 0); // Boolean
},
getSortIndex: function(inSortInfo){
// summary:
// Returns the index of the column on which the grid is sorted
inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
return Math.abs(inSortInfo) - 1; // Integer
},
setSortIndex: function(inIndex, inAsc){
// summary:
// Sort the grid on a column in a specified direction
// inIndex: Integer
// Column index on which to sort.
// inAsc: Boolean
// If true, sort the grid in ascending order, otherwise in descending order
var si = inIndex +1;
if(inAsc != undefined){
si *= (inAsc ? 1 : -1);
} else if(this.getSortIndex() == inIndex){
si = -this.sortInfo;
}
this.setSortInfo(si);
},
setSortInfo: function(inSortInfo){
if(this.canSort(inSortInfo)){
this.sortInfo = inSortInfo;
this.sort();
this.update();
}
},
// DOM event handler
doKeyEvent: function(e){
e.dispatch = 'do' + e.type;
this.onKeyEvent(e);
},
// event dispatch
//: protected
_dispatch: function(m, e){
if(m in this){
return this[m](e);
}
},
dispatchKeyEvent: function(e){
this._dispatch(e.dispatch, e);
},
dispatchContentEvent: function(e){
this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e);
},
dispatchHeaderEvent: function(e){
e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e);
},
dokeydown: function(e){
this.onKeyDown(e);
},
doclick: function(e){
if(e.cellNode){
this.onCellClick(e);
}else{
this.onRowClick(e);
}
},
dodblclick: function(e){
if(e.cellNode){
this.onCellDblClick(e);
}else{
this.onRowDblClick(e);
}
},
docontextmenu: function(e){
if(e.cellNode){
this.onCellContextMenu(e);
}else{
this.onRowContextMenu(e);
}
},
doheaderclick: function(e){
if(e.cellNode){
this.onHeaderCellClick(e);
}else{
this.onHeaderClick(e);
}
},
doheaderdblclick: function(e){
if(e.cellNode){
this.onHeaderCellDblClick(e);
}else{
this.onHeaderDblClick(e);
}
},
doheadercontextmenu: function(e){
if(e.cellNode){
this.onHeaderCellContextMenu(e);
}else{
this.onHeaderContextMenu(e);
}
},
// override to modify editing process
doStartEdit: function(inCell, inRowIndex){
this.onStartEdit(inCell, inRowIndex);
},
doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
},
doCancelEdit: function(inRowIndex){
this.onCancelEdit(inRowIndex);
},
doApplyEdit: function(inRowIndex){
this.onApplyEdit(inRowIndex);
},
// row editing
addRow: function(){
// summary:
// Add a row to the grid.
this.updateRowCount(this.attr('rowCount')+1);
},
removeSelectedRows: function(){
// summary:
// Remove the selected rows from the grid.
this.updateRowCount(Math.max(0, this.attr('rowCount') - this.selection.getSelected().length));
this.selection.clear();
}
});
dojox.grid._Grid.markupFactory = function(props, node, ctor, cellFunc){
var d = dojo;
var widthFromAttr = function(n){
var w = d.attr(n, "width")||"auto";
if((w != "auto")&&(w.slice(-2) != "em")&&(w.slice(-1) != "%")){
w = parseInt(w)+"px";
}
return w;
}
// if(!props.store){ }
// if a structure isn't referenced, do we have enough
// data to try to build one automatically?
if( !props.structure &&
node.nodeName.toLowerCase() == "table"){
// try to discover a structure
props.structure = d.query("> colgroup", node).map(function(cg){
var sv = d.attr(cg, "span");
var v = {
noscroll: (d.attr(cg, "noscroll") == "true") ? true : false,
__span: (!!sv ? parseInt(sv) : 1),
cells: []
};
if(d.hasAttr(cg, "width")){
v.width = widthFromAttr(cg);
}
return v; // for vendetta
});
if(!props.structure.length){
props.structure.push({
__span: Infinity,
cells: [] // catch-all view
});
}
// check to see if we're gonna have more than one view
// for each tr in our th, create a row of cells
d.query("thead > tr", node).forEach(function(tr, tr_idx){
var cellCount = 0;
var viewIdx = 0;
var lastViewIdx;
var cView = null;
d.query("> th", tr).map(function(th){
// what view will this cell go into?
// NOTE:
// to prevent extraneous iteration, we start counters over
// for each row, incrementing over the surface area of the
// structure that colgroup processing generates and
// creating cell objects for each <th> to place into those
// cell groups. There's a lot of state-keepking logic
// here, but it is what it has to be.
if(!cView){ // current view book keeping
lastViewIdx = 0;
cView = props.structure[0];
}else if(cellCount >= (lastViewIdx+cView.__span)){
viewIdx++;
// move to allocating things into the next view
lastViewIdx += cView.__span;
var lastView = cView;
cView = props.structure[viewIdx];
}
// actually define the cell from what markup hands us
var cell = {
name: d.trim(d.attr(th, "name")||th.innerHTML),
colSpan: parseInt(d.attr(th, "colspan")||1, 10),
type: d.trim(d.attr(th, "cellType")||"")
};
cellCount += cell.colSpan;
var rowSpan = d.attr(th, "rowspan");
if(rowSpan){
cell.rowSpan = rowSpan;
}
if(d.hasAttr(th, "width")){
cell.width = widthFromAttr(th);
}
if(d.hasAttr(th, "relWidth")){
cell.relWidth = window.parseInt(dojo.attr(th, "relWidth"), 10);
}
if(d.hasAttr(th, "hidden")){
cell.hidden = d.attr(th, "hidden") == "true";
}
if(cellFunc){
cellFunc(th, cell);
}
cell.type = cell.type ? dojo.getObject(cell.type) : dojox.grid.cells.Cell;
if(cell.type && cell.type.markupFactory){
cell.type.markupFactory(th, cell);
}
if(!cView.cells[tr_idx]){
cView.cells[tr_idx] = [];
}
cView.cells[tr_idx].push(cell);
});
});
}
return new ctor(props, node);
}
})();
}
if(!dojo._hasResource["dojox.grid.DataSelection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.DataSelection"] = true;
dojo.provide("dojox.grid.DataSelection");
dojo.declare("dojox.grid.DataSelection", dojox.grid.Selection, {
getFirstSelected: function(){
var idx = dojox.grid.Selection.prototype.getFirstSelected.call(this);
if(idx == -1){ return null; }
return this.grid.getItem(idx);
},
getNextSelected: function(inPrev){
var old_idx = this.grid.getItemIndex(inPrev);
var idx = dojox.grid.Selection.prototype.getNextSelected.call(this, old_idx);
if(idx == -1){ return null; }
return this.grid.getItem(idx);
},
getSelected: function(){
var result = [];
for(var i=0, l=this.selected.length; i<l; i++){
if(this.selected[i]){
result.push(this.grid.getItem(i));
}
}
return result;
},
addToSelection: function(inItemOrIndex){
if(this.mode == 'none'){ return; }
var idx = null;
if(typeof inItemOrIndex == "number" || typeof inItemOrIndex == "string"){
idx = inItemOrIndex;
}else{
idx = this.grid.getItemIndex(inItemOrIndex);
}
dojox.grid.Selection.prototype.addToSelection.call(this, idx);
},
deselect: function(inItemOrIndex){
if(this.mode == 'none'){ return; }
var idx = null;
if(typeof inItemOrIndex == "number" || typeof inItemOrIndex == "string"){
idx = inItemOrIndex;
}else{
idx = this.grid.getItemIndex(inItemOrIndex);
}
dojox.grid.Selection.prototype.deselect.call(this, idx);
},
deselectAll: function(inItemOrIndex){
var idx = null;
if(inItemOrIndex || typeof inItemOrIndex == "number"){
if(typeof inItemOrIndex == "number" || typeof inItemOrIndex == "string"){
idx = inItemOrIndex;
}else{
idx = this.grid.getItemIndex(inItemOrIndex);
}
dojox.grid.Selection.prototype.deselectAll.call(this, idx);
}else{
this.inherited(arguments);
}
}
});
}
if(!dojo._hasResource["dojox.grid.DataGrid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.DataGrid"] = true;
dojo.provide("dojox.grid.DataGrid");
/*=====
dojo.declare("dojox.grid.__DataCellDef", dojox.grid.__CellDef, {
constructor: function(){
// field: String?
// The attribute to read from the dojo.data item for the row.
// get: Function?
// function(rowIndex, item?){} rowIndex is of type Integer, item is of type
// Object. This function will be called when a cell requests data. Returns
// the unformatted data for the cell.
}
});
=====*/
/*=====
dojo.declare("dojox.grid.__DataViewDef", dojox.grid.__ViewDef, {
constructor: function(){
// cells: dojox.grid.__DataCellDef[]|Array[dojox.grid.__DataCellDef[]]?
// The structure of the cells within this grid.
// defaultCell: dojox.grid.__DataCellDef?
// A cell definition with default values for all cells in this view. If
// a property is defined in a cell definition in the "cells" array and
// this property, the cell definition's property will override this
// property's property.
}
});
=====*/
dojo.declare("dojox.grid.DataGrid", dojox.grid._Grid, {
store: null,
query: null,
queryOptions: null,
fetchText: '...',
/*=====
// structure: dojox.grid.__DataViewDef|dojox.grid.__DataViewDef[]|dojox.grid.__DataCellDef[]|Array[dojox.grid.__DataCellDef[]]
// View layout defintion.
structure: '',
=====*/
// You can specify items instead of a query, if you like. They do not need
// to be loaded - but the must be items in the store
items: null,
_store_connects: null,
_by_idty: null,
_by_idx: null,
_cache: null,
_pages: null,
_pending_requests: null,
_bop: -1,
_eop: -1,
_requests: 0,
rowCount: 0,
_isLoaded: false,
_isLoading: false,
postCreate: function(){
this._pages = [];
this._store_connects = [];
this._by_idty = {};
this._by_idx = [];
this._cache = [];
this._pending_requests = {};
this._setStore(this.store);
this.inherited(arguments);
},
createSelection: function(){
this.selection = new dojox.grid.DataSelection(this);
},
get: function(inRowIndex, inItem){
return (!inItem ? this.defaultValue : (!this.field ? this.value : this.grid.store.getValue(inItem, this.field)));
},
_onSet: function(item, attribute, oldValue, newValue){
var idx = this.getItemIndex(item);
if(idx>-1){
this.updateRow(idx);
}
},
_addItem: function(item, index, noUpdate){
var idty = this._hasIdentity ? this.store.getIdentity(item) : dojo.toJson(this.query) + ":idx:" + index + ":sort:" + dojo.toJson(this.getSortProps());
var o = { idty: idty, item: item };
this._by_idty[idty] = this._by_idx[index] = o;
if(!noUpdate){
this.updateRow(index);
}
},
_onNew: function(item, parentInfo){
var rowCount = this.attr('rowCount');
this._addingItem = true;
this.updateRowCount(rowCount+1);
this._addingItem = false;
this._addItem(item, rowCount);
this.showMessage();
},
_onDelete: function(item){
var idx = this._getItemIndex(item, true);
if(idx >= 0){
var o = this._by_idx[idx];
this._by_idx.splice(idx, 1);
delete this._by_idty[o.idty];
this.updateRowCount(this.attr('rowCount')-1);
if(this.attr('rowCount') === 0){
this.showMessage(this.noDataMessage);
}
}
},
_onRevert: function(){
this._refresh();
},
setStore: function(store, query, queryOptions){
this._setQuery(query, queryOptions);
this._setStore(store);
this._refresh(true);
},
setQuery: function(query, queryOptions){
this._setQuery(query, queryOptions);
this._refresh(true);
},
setItems: function(items){
this.items = items;
this._setStore(this.store);
this._refresh(true);
},
_setQuery: function(query, queryOptions){
this.query = query;
this.queryOptions = queryOptions || this.queryOptions;
},
_setStore: function(store){
if(this.store&&this._store_connects){
dojo.forEach(this._store_connects,function(arr){
dojo.forEach(arr, dojo.disconnect);
});
}
this.store = store;
if(this.store){
var f = this.store.getFeatures();
var h = [];
this._canEdit = !!f["dojo.data.api.Write"] && !!f["dojo.data.api.Identity"];
this._hasIdentity = !!f["dojo.data.api.Identity"];
if(!!f["dojo.data.api.Notification"] && !this.items){
h.push(this.connect(this.store, "onSet", "_onSet"));
h.push(this.connect(this.store, "onNew", "_onNew"));
h.push(this.connect(this.store, "onDelete", "_onDelete"));
}
if(this._canEdit){
h.push(this.connect(this.store, "revert", "_onRevert"));
}
this._store_connects = h;
}
},
_onFetchBegin: function(size, req){
if(this.rowCount != size){
if(req.isRender){
this.scroller.init(size, this.keepRows, this.rowsPerPage);
this.rowCount = size;
this._setAutoHeightAttr(this.autoHeight, true);
this.prerender();
}else{
this.updateRowCount(size);
}
}
},
_onFetchComplete: function(items, req){
if(items && items.length > 0){
//
dojo.forEach(items, function(item, idx){
this._addItem(item, req.start+idx, true);
}, this);
this.updateRows(req.start, items.length);
if(req.isRender){
this.setScrollTop(0);
this.postrender();
}else if(this._lastScrollTop){
this.setScrollTop(this._lastScrollTop);
}
}
delete this._lastScrollTop;
if(!this._isLoaded){
this._isLoading = false;
this._isLoaded = true;
if(!items || !items.length){
this.showMessage(this.noDataMessage);
}else{
this.showMessage();
}
}
this._pending_requests[req.start] = false;
},
_onFetchError: function(err, req){
delete this._lastScrollTop;
if(!this._isLoaded){
this._isLoading = false;
this._isLoaded = true;
this.showMessage(this.errorMessage);
}
this.onFetchError(err, req);
},
onFetchError: function(err, req){
},
_fetch: function(start, isRender){
var start = start || 0;
if(this.store && !this._pending_requests[start]){
if(!this._isLoaded && !this._isLoading){
this._isLoading = true;
this.showMessage(this.loadingMessage);
}
this._pending_requests[start] = true;
//
try{
if(this.items){
var items = this.items;
var store = this.store;
this.rowsPerPage = items.length
var req = {
start: start,
count: this.rowsPerPage,
isRender: isRender
};
this._onFetchBegin(items.length, req);
// Load them if we need to
var waitCount = 0;
dojo.forEach(items, function(i){
if(!store.isItemLoaded(i)){ waitCount++; }
});
if(waitCount === 0){
this._onFetchComplete(items, req);
}else{
var onItem = function(item){
waitCount--;
if(waitCount === 0){
this._onFetchComplete(items, req);
}
};
dojo.forEach(items, function(i){
if(!store.isItemLoaded(i)){
store.loadItem({item: i, onItem: onItem, scope: this});
}
}, this);
}
}else{
this.store.fetch({
start: start,
count: this.rowsPerPage,
query: this.query,
sort: this.getSortProps(),
queryOptions: this.queryOptions,
isRender: isRender,
onBegin: dojo.hitch(this, "_onFetchBegin"),
onComplete: dojo.hitch(this, "_onFetchComplete"),
onError: dojo.hitch(this, "_onFetchError")
});
}
}catch(e){
this._onFetchError(e);
}
}
},
_clearData: function(){
this.updateRowCount(0);
this._by_idty = {};
this._by_idx = [];
this._pages = [];
this._bop = this._eop = -1;
this._isLoaded = false;
this._isLoading = false;
},
getItem: function(idx){
var data = this._by_idx[idx];
if(!data||(data&&!data.item)){
this._preparePage(idx);
return null;
}
return data.item;
},
getItemIndex: function(item){
return this._getItemIndex(item, false);
},
_getItemIndex: function(item, isDeleted){
if(!isDeleted && !this.store.isItem(item)){
return -1;
}
var idty = this._hasIdentity ? this.store.getIdentity(item) : null;
for(var i=0, l=this._by_idx.length; i<l; i++){
var d = this._by_idx[i];
if(d && ((idty && d.idty == idty) || (d.item === item))){
return i;
}
}
return -1;
},
filter: function(query, reRender){
this.query = query;
if(reRender){
this._clearData();
}
this._fetch();
},
_getItemAttr: function(idx, attr){
var item = this.getItem(idx);
return (!item ? this.fetchText : this.store.getValue(item, attr));
},
// rendering
_render: function(){
if(this.domNode.parentNode){
this.scroller.init(this.attr('rowCount'), this.keepRows, this.rowsPerPage);
this.prerender();
this._fetch(0, true);
}
},
// paging
_requestsPending: function(inRowIndex){
return this._pending_requests[inRowIndex];
},
_rowToPage: function(inRowIndex){
return (this.rowsPerPage ? Math.floor(inRowIndex / this.rowsPerPage) : inRowIndex);
},
_pageToRow: function(inPageIndex){
return (this.rowsPerPage ? this.rowsPerPage * inPageIndex : inPageIndex);
},
_preparePage: function(inRowIndex){
if((inRowIndex < this._bop || inRowIndex >= this._eop) && !this._addingItem){
var pageIndex = this._rowToPage(inRowIndex);
this._needPage(pageIndex);
this._bop = pageIndex * this.rowsPerPage;
this._eop = this._bop + (this.rowsPerPage || this.attr('rowCount'));
}
},
_needPage: function(inPageIndex){
if(!this._pages[inPageIndex]){
this._pages[inPageIndex] = true;
this._requestPage(inPageIndex);
}
},
_requestPage: function(inPageIndex){
var row = this._pageToRow(inPageIndex);
var count = Math.min(this.rowsPerPage, this.attr('rowCount') - row);
if(count > 0){
this._requests++;
if(!this._requestsPending(row)){
setTimeout(dojo.hitch(this, "_fetch", row, false), 1);
//this.requestRows(row, count);
}
}
},
getCellName: function(inCell){
return inCell.field;
//
},
_refresh: function(isRender){
this._clearData();
this._fetch(0, isRender);
},
sort: function(){
this._lastScrollTop = this.scrollTop;
this._refresh();
},
canSort: function(){
return (!this._isLoading);
},
getSortProps: function(){
var c = this.getCell(this.getSortIndex());
if(!c){
return null;
}else{
var desc = c["sortDesc"];
var si = !(this.sortInfo>0);
if(typeof desc == "undefined"){
desc = si;
}else{
desc = si ? !desc : desc;
}
return [{ attribute: c.field, descending: desc }];
}
},
styleRowState: function(inRow){
// summary: Perform row styling
if(this.store && this.store.getState){
var states=this.store.getState(inRow.index), c='';
for(var i=0, ss=["inflight", "error", "inserting"], s; s=ss[i]; i++){
if(states[s]){
c = ' dojoxGridRow-' + s;
break;
}
}
inRow.customClasses += c;
}
},
onStyleRow: function(inRow){
this.styleRowState(inRow);
this.inherited(arguments);
},
// editing
canEdit: function(inCell, inRowIndex){
return this._canEdit;
},
_copyAttr: function(idx, attr){
var row = {};
var backstop = {};
var src = this.getItem(idx);
return this.store.getValue(src, attr);
},
doStartEdit: function(inCell, inRowIndex){
if(!this._cache[inRowIndex]){
this._cache[inRowIndex] = this._copyAttr(inRowIndex, inCell.field);
}
this.onStartEdit(inCell, inRowIndex);
},
doApplyCellEdit: function(inValue, inRowIndex, inAttrName){
this.store.fetchItemByIdentity({
identity: this._by_idx[inRowIndex].idty,
onItem: dojo.hitch(this, function(item){
var oldValue = this.store.getValue(item, inAttrName);
if(typeof oldValue == 'number'){
inValue = isNaN(inValue) ? inValue : parseFloat(inValue);
}else if(typeof oldValue == 'boolean'){
inValue = inValue == 'true' ? true : inValue == 'false' ? false : inValue;
}else if(oldValue instanceof Date){
var asDate = new Date(inValue);
inValue = isNaN(asDate.getTime()) ? inValue : asDate;
}
this.store.setValue(item, inAttrName, inValue);
this.onApplyCellEdit(inValue, inRowIndex, inAttrName);
})
});
},
doCancelEdit: function(inRowIndex){
var cache = this._cache[inRowIndex];
if(cache){
this.updateRow(inRowIndex);
delete this._cache[inRowIndex];
}
this.onCancelEdit.apply(this, arguments);
},
doApplyEdit: function(inRowIndex, inDataAttr){
var cache = this._cache[inRowIndex];
/*if(cache){
var data = this.getItem(inRowIndex);
if(this.store.getValue(data, inDataAttr) != cache){
this.update(cache, data, inRowIndex);
}
delete this._cache[inRowIndex];
}*/
this.onApplyEdit(inRowIndex);
},
removeSelectedRows: function(){
// summary:
// Remove the selected rows from the grid.
if(this._canEdit){
this.edit.apply();
var items = this.selection.getSelected();
if(items.length){
dojo.forEach(items, this.store.deleteItem, this.store);
this.selection.clear();
}
}
}
});
dojox.grid.DataGrid.markupFactory = function(props, node, ctor, cellFunc){
return dojox.grid._Grid.markupFactory(props, node, ctor, function(node, cellDef){
var field = dojo.trim(dojo.attr(node, "field")||"");
if(field){
cellDef.field = field;
}
cellDef.field = cellDef.field||cellDef.name;
if(cellFunc){
cellFunc(node, cellDef);
}
});
}
}
dojo.i18n._preloadLocalizations("dojox.grid.nls.DataGrid", ["ROOT","ar","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hu","it","it-it","ja","ja-jp","ko","ko-kr","nl","nl-nl","no","pl","pt","pt-br","pt-pt","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]);