summaryrefslogtreecommitdiff
path: root/includes/js/dijit/layout/StackContainer.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dijit/layout/StackContainer.js')
-rw-r--r--includes/js/dijit/layout/StackContainer.js493
1 files changed, 493 insertions, 0 deletions
diff --git a/includes/js/dijit/layout/StackContainer.js b/includes/js/dijit/layout/StackContainer.js
new file mode 100644
index 0000000..3119972
--- /dev/null
+++ b/includes/js/dijit/layout/StackContainer.js
@@ -0,0 +1,493 @@
+if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit.layout.StackContainer"] = true;
+dojo.provide("dijit.layout.StackContainer");
+
+dojo.require("dijit._Templated");
+dojo.require("dijit.layout._LayoutWidget");
+dojo.require("dijit.form.Button");
+dojo.require("dijit.Menu");
+dojo.requireLocalization("dijit", "common", null, "zh,pt,da,tr,ru,de,sv,ja,he,fi,nb,el,ar,ROOT,pt-pt,cs,fr,es,ko,nl,zh-tw,pl,it,hu");
+
+dojo.declare(
+ "dijit.layout.StackContainer",
+ dijit.layout._LayoutWidget,
+ {
+ // summary:
+ // A container that has multiple children, but shows only
+ // one child at a time
+ //
+ // description:
+ // A container for widgets (ContentPanes, for example) That displays
+ // only one Widget at a time.
+ //
+ // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
+ //
+ // Can be base class for container, Wizard, Show, etc.
+ //
+ //
+ // doLayout: Boolean
+ // if true, change the size of my currently displayed child to match my size
+ doLayout: true,
+
+ _started: false,
+/*=====
+ // selectedChildWidget: Widget
+ // References the currently selected child widget, if any
+ //
+ selectedChildWidget: null,
+=====*/
+ postCreate: function(){
+ dijit.setWaiRole((this.containerNode || this.domNode), "tabpanel");
+ this.connect(this.domNode, "onkeypress", this._onKeyPress);
+ },
+
+ startup: function(){
+ if(this._started){ return; }
+
+ var children = this.getChildren();
+
+ // Setup each page panel
+ dojo.forEach(children, this._setupChild, this);
+
+ // Figure out which child to initially display
+ dojo.some(children, function(child){
+ if(child.selected){
+ this.selectedChildWidget = child;
+ }
+ return child.selected;
+ }, this);
+
+ var selected = this.selectedChildWidget;
+
+ // Default to the first child
+ if(!selected && children[0]){
+ selected = this.selectedChildWidget = children[0];
+ selected.selected = true;
+ }
+ if(selected){
+ this._showChild(selected);
+ }
+
+ // Now publish information about myself so any StackControllers can initialize..
+ dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
+
+ this.inherited(arguments);
+ },
+
+ _setupChild: function(/*Widget*/ page){
+ // Summary: prepare the given child
+
+ page.domNode.style.display = "none";
+
+ // since we are setting the width/height of the child elements, they need
+ // to be position:relative, or IE has problems (See bug #2033)
+ page.domNode.style.position = "relative";
+
+ return page; // dijit._Widget
+ },
+
+ addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){
+ // summary: Adds a widget to the stack
+
+ dijit._Container.prototype.addChild.apply(this, arguments);
+ child = this._setupChild(child);
+
+ if(this._started){
+ // in case the tab titles have overflowed from one line to two lines
+ this.layout();
+
+ dojo.publish(this.id+"-addChild", [child, insertIndex]);
+
+ // if this is the first child, then select it
+ if(!this.selectedChildWidget){
+ this.selectChild(child);
+ }
+ }
+ },
+
+ removeChild: function(/*Widget*/ page){
+ // summary: Removes the pane from the stack
+
+ dijit._Container.prototype.removeChild.apply(this, arguments);
+
+ // If we are being destroyed than don't run the code below (to select another page), because we are deleting
+ // every page one by one
+ if(this._beingDestroyed){ return; }
+
+ if(this._started){
+ // this will notify any tablists to remove a button; do this first because it may affect sizing
+ dojo.publish(this.id+"-removeChild", [page]);
+
+ // in case the tab titles now take up one line instead of two lines
+ this.layout();
+ }
+
+ if(this.selectedChildWidget === page){
+ this.selectedChildWidget = undefined;
+ if(this._started){
+ var children = this.getChildren();
+ if(children.length){
+ this.selectChild(children[0]);
+ }
+ }
+ }
+ },
+
+ selectChild: function(/*Widget*/ page){
+ // summary:
+ // Show the given widget (which must be one of my children)
+
+ page = dijit.byId(page);
+
+ if(this.selectedChildWidget != page){
+ // Deselect old page and select new one
+ this._transition(page, this.selectedChildWidget);
+ this.selectedChildWidget = page;
+ dojo.publish(this.id+"-selectChild", [page]);
+ }
+ },
+
+ _transition: function(/*Widget*/newWidget, /*Widget*/oldWidget){
+ if(oldWidget){
+ this._hideChild(oldWidget);
+ }
+ this._showChild(newWidget);
+
+ // Size the new widget, in case this is the first time it's being shown,
+ // or I have been resized since the last time it was shown.
+ // page must be visible for resizing to work
+ if(this.doLayout && newWidget.resize){
+ newWidget.resize(this._containerContentBox || this._contentBox);
+ }
+ },
+
+ _adjacent: function(/*Boolean*/ forward){
+ // summary: Gets the next/previous child widget in this container from the current selection
+ var children = this.getChildren();
+ var index = dojo.indexOf(children, this.selectedChildWidget);
+ index += forward ? 1 : children.length - 1;
+ return children[ index % children.length ]; // dijit._Widget
+ },
+
+ forward: function(){
+ // Summary: advance to next page
+ this.selectChild(this._adjacent(true));
+ },
+
+ back: function(){
+ // Summary: go back to previous page
+ this.selectChild(this._adjacent(false));
+ },
+
+ _onKeyPress: function(e){
+ dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
+ },
+
+ layout: function(){
+ if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
+ this.selectedChildWidget.resize(this._contentBox);
+ }
+ },
+
+ _showChild: function(/*Widget*/ page){
+ var children = this.getChildren();
+ page.isFirstChild = (page == children[0]);
+ page.isLastChild = (page == children[children.length-1]);
+ page.selected = true;
+
+ page.domNode.style.display="";
+ if(page._loadCheck){
+ page._loadCheck(); // trigger load in ContentPane
+ }
+ if(page.onShow){
+ page.onShow();
+ }
+ },
+
+ _hideChild: function(/*Widget*/ page){
+ page.selected=false;
+ page.domNode.style.display="none";
+ if(page.onHide){
+ page.onHide();
+ }
+ },
+
+ closeChild: function(/*Widget*/ page){
+ // summary:
+ // callback when user clicks the [X] to remove a page
+ // if onClose() returns true then remove and destroy the child
+ var remove = page.onClose(this, page);
+ if(remove){
+ this.removeChild(page);
+ // makes sure we can clean up executeScripts in ContentPane onUnLoad
+ page.destroyRecursive();
+ }
+ },
+
+ destroy: function(){
+ this._beingDestroyed = true;
+ this.inherited(arguments);
+ }
+});
+
+dojo.declare(
+ "dijit.layout.StackController",
+ [dijit._Widget, dijit._Templated, dijit._Container],
+ {
+ // summary:
+ // Set of buttons to select a page in a page list.
+ // Monitors the specified StackContainer, and whenever a page is
+ // added, deleted, or selected, updates itself accordingly.
+
+ templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
+
+ // containerId: String
+ // the id of the page container that I point to
+ containerId: "",
+
+ // buttonWidget: String
+ // the name of the button widget to create to correspond to each page
+ buttonWidget: "dijit.layout._StackButton",
+
+ postCreate: function(){
+ dijit.setWaiRole(this.domNode, "tablist");
+
+ // TODO: change key from object to id, to get more separation from StackContainer
+ this.pane2button = {}; // mapping from panes to buttons
+ this.pane2menu = {}; // mapping from panes to close menu
+
+ this._subscriptions=[
+ dojo.subscribe(this.containerId+"-startup", this, "onStartup"),
+ dojo.subscribe(this.containerId+"-addChild", this, "onAddChild"),
+ dojo.subscribe(this.containerId+"-removeChild", this, "onRemoveChild"),
+ dojo.subscribe(this.containerId+"-selectChild", this, "onSelectChild"),
+ dojo.subscribe(this.containerId+"-containerKeyPress", this, "onContainerKeyPress")
+ ];
+ },
+
+ onStartup: function(/*Object*/ info){
+ // summary: called after StackContainer has finished initializing
+ dojo.forEach(info.children, this.onAddChild, this);
+ this.onSelectChild(info.selected);
+ },
+
+ destroy: function(){
+ for(var pane in this.pane2button){
+ this.onRemoveChild(pane);
+ }
+ dojo.forEach(this._subscriptions, dojo.unsubscribe);
+ this.inherited(arguments);
+ },
+
+ onAddChild: function(/*Widget*/ page, /*Integer?*/ insertIndex){
+ // summary:
+ // Called whenever a page is added to the container.
+ // Create button corresponding to the page.
+
+ // add a node that will be promoted to the button widget
+ var refNode = dojo.doc.createElement("span");
+ this.domNode.appendChild(refNode);
+ // create an instance of the button widget
+ var cls = dojo.getObject(this.buttonWidget);
+ var button = new cls({label: page.title, closeButton: page.closable}, refNode);
+ this.addChild(button, insertIndex);
+ this.pane2button[page] = button;
+ page.controlButton = button; // this value might be overwritten if two tabs point to same container
+
+ dojo.connect(button, "onClick", dojo.hitch(this,"onButtonClick",page));
+ if(page.closable){
+ dojo.connect(button, "onClickCloseButton", dojo.hitch(this,"onCloseButtonClick",page));
+ // add context menu onto title button
+ var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
+ var closeMenu = new dijit.Menu({targetNodeIds:[button.id], id:button.id+"_Menu"});
+ var mItem = new dijit.MenuItem({label:_nlsResources.itemClose});
+ dojo.connect(mItem, "onClick", dojo.hitch(this, "onCloseButtonClick", page));
+ closeMenu.addChild(mItem);
+ this.pane2menu[page] = closeMenu;
+ }
+ if(!this._currentChild){ // put the first child into the tab order
+ button.focusNode.setAttribute("tabIndex", "0");
+ this._currentChild = page;
+ }
+ //make sure all tabs have the same length
+ if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
+ this._rectifyRtlTabList();
+ }
+ },
+
+ onRemoveChild: function(/*Widget*/ page){
+ // summary:
+ // Called whenever a page is removed from the container.
+ // Remove the button corresponding to the page.
+ if(this._currentChild === page){ this._currentChild = null; }
+ var button = this.pane2button[page];
+ var menu = this.pane2menu[page];
+ if (menu){
+ menu.destroy();
+ }
+ if(button){
+ // TODO? if current child { reassign }
+ button.destroy();
+ }
+ this.pane2button[page] = null;
+ },
+
+ onSelectChild: function(/*Widget*/ page){
+ // summary:
+ // Called when a page has been selected in the StackContainer, either by me or by another StackController
+
+ if(!page){ return; }
+
+ if(this._currentChild){
+ var oldButton=this.pane2button[this._currentChild];
+ oldButton.setAttribute('checked', false);
+ oldButton.focusNode.setAttribute("tabIndex", "-1");
+ }
+
+ var newButton=this.pane2button[page];
+ newButton.setAttribute('checked', true);
+ this._currentChild = page;
+ newButton.focusNode.setAttribute("tabIndex", "0");
+ var container = dijit.byId(this.containerId);
+ dijit.setWaiState(container.containerNode || container.domNode, "labelledby", newButton.id);
+ },
+
+ onButtonClick: function(/*Widget*/ page){
+ // summary:
+ // Called whenever one of my child buttons is pressed in an attempt to select a page
+ var container = dijit.byId(this.containerId); // TODO: do this via topics?
+ container.selectChild(page);
+ },
+
+ onCloseButtonClick: function(/*Widget*/ page){
+ // summary:
+ // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
+ var container = dijit.byId(this.containerId);
+ container.closeChild(page);
+ var b = this.pane2button[this._currentChild];
+ if(b){
+ dijit.focus(b.focusNode || b.domNode);
+ }
+ },
+
+ // TODO: this is a bit redundant with forward, back api in StackContainer
+ adjacent: function(/*Boolean*/ forward){
+ if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
+ // find currently focused button in children array
+ var children = this.getChildren();
+ var current = dojo.indexOf(children, this.pane2button[this._currentChild]);
+ // pick next button to focus on
+ var offset = forward ? 1 : children.length - 1;
+ return children[ (current + offset) % children.length ]; // dijit._Widget
+ },
+
+ onkeypress: function(/*Event*/ e){
+ // summary:
+ // Handle keystrokes on the page list, for advancing to next/previous button
+ // and closing the current page if the page is closable.
+
+ if(this.disabled || e.altKey ){ return; }
+ var forward = null;
+ if(e.ctrlKey || !e._djpage){
+ var k = dojo.keys;
+ switch(e.keyCode){
+ case k.LEFT_ARROW:
+ case k.UP_ARROW:
+ if(!e._djpage){ forward = false; }
+ break;
+ case k.PAGE_UP:
+ if(e.ctrlKey){ forward = false; }
+ break;
+ case k.RIGHT_ARROW:
+ case k.DOWN_ARROW:
+ if(!e._djpage){ forward = true; }
+ break;
+ case k.PAGE_DOWN:
+ if(e.ctrlKey){ forward = true; }
+ break;
+ case k.DELETE:
+ if(this._currentChild.closable){
+ this.onCloseButtonClick(this._currentChild);
+ }
+ dojo.stopEvent(e);
+ break;
+ default:
+ if(e.ctrlKey){
+ if(e.keyCode == k.TAB){
+ this.adjacent(!e.shiftKey).onClick();
+ dojo.stopEvent(e);
+ }else if(e.keyChar == "w"){
+ if(this._currentChild.closable){
+ this.onCloseButtonClick(this._currentChild);
+ }
+ dojo.stopEvent(e); // avoid browser tab closing.
+ }
+ }
+ }
+ // handle page navigation
+ if(forward !== null){
+ this.adjacent(forward).onClick();
+ dojo.stopEvent(e);
+ }
+ }
+ },
+
+ onContainerKeyPress: function(/*Object*/ info){
+ info.e._djpage = info.page;
+ this.onkeypress(info.e);
+ }
+});
+
+dojo.declare("dijit.layout._StackButton",
+ dijit.form.ToggleButton,
+ {
+ // summary
+ // Internal widget used by StackContainer.
+ // The button-like or tab-like object you click to select or delete a page
+
+ tabIndex: "-1", // StackContainer buttons are not in the tab order by default
+
+ postCreate: function(/*Event*/ evt){
+ dijit.setWaiRole((this.focusNode || this.domNode), "tab");
+ this.inherited(arguments);
+ },
+
+ onClick: function(/*Event*/ evt){
+ // summary: This is for TabContainer where the tabs are <span> rather than button,
+ // so need to set focus explicitly (on some browsers)
+ dijit.focus(this.focusNode);
+
+ // ... now let StackController catch the event and tell me what to do
+ },
+
+ onClickCloseButton: function(/*Event*/ evt){
+ // summary
+ // StackContainer connects to this function; if your widget contains a close button
+ // then clicking it should call this function.
+ evt.stopPropagation();
+ }
+});
+
+// These arguments can be specified for the children of a StackContainer.
+// Since any widget can be specified as a StackContainer child, mix them
+// into the base widget class. (This is a hack, but it's effective.)
+dojo.extend(dijit._Widget, {
+ // title: String
+ // Title of this widget. Used by TabContainer to the name the tab, etc.
+ title: "",
+
+ // selected: Boolean
+ // Is this child currently selected?
+ selected: false,
+
+ // closable: Boolean
+ // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
+ closable: false, // true if user can close this tab pane
+
+ onClose: function(){
+ // summary: Callback if someone tries to close the child, child will be closed if func returns true
+ return true;
+ }
+});
+
+}