| /* |
| 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\">✓</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 ? '▲' : '▼', |
| '</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"> </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"]); |