123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- 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.declare("dijit.layout.StackContainer", dijit.layout._LayoutWidget,
- // summary
- // A container that has multiple children, but shows only
- // one child at a time (like looking at the pages in a book one by
- // one).
- //
- // Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and
- // <widgetId>-selectChild
- //
- // Can be base class for container, Wizard, Show, etc.
- {
- // 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
- 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("startup", arguments);
- this._started = true;
- },
- _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 childd
- var remove = page.onClose(this, page);
- if (remove) {
- this.removeChild(page);
- // makes sure we can clean up executeScripts in ContentPane
- // onUnLoad
- page.destroy();
- }
- },
- destroy : function() {
- this._beingDestroyed = true;
- this.inherited("destroy", 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");
- this.pane2button = {}; // mapping from panes to buttons
- 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() {
- dojo.forEach(this._subscriptions, dojo.unsubscribe);
- this.inherited("destroy", 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 = document.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));
- dojo.connect(button, "onClickCloseButton", dojo.hitch(this,
- "onCloseButtonClick", page));
- if (!this._currentChild) { // put the first child into the
- // tab order
- button.focusNode.setAttribute("tabIndex", "0");
- this._currentChild = page;
- }
- },
- onRemoveChild : function(/* Widget */page) {
- // summary:
- // Called whenever a page is removed from the container.
- // Remove the button corresponding to the page.
- if (this._currentChild === page) {
- this._currentChild = null;
- }
- var button = this.pane2button[page];
- if (button) {
- // 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.setChecked(false);
- oldButton.focusNode.setAttribute("tabIndex", "-1");
- }
- var newButton = this.pane2button[page];
- newButton.setChecked(true);
- this._currentChild = page;
- newButton.focusNode.setAttribute("tabIndex", "0");
- },
- 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) {
- // 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 = true;
- if (e.ctrlKey || !e._djpage) {
- var k = dojo.keys;
- switch (e.keyCode) {
- case k.LEFT_ARROW :
- case k.UP_ARROW :
- case k.PAGE_UP :
- forward = false;
- // fall through
- case k.RIGHT_ARROW :
- case k.DOWN_ARROW :
- case k.PAGE_DOWN :
- this.adjacent(forward).onClick();
- dojo.stopEvent(e);
- 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.
- }
- }
- }
- }
- },
- 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("postCreate", 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;
- }
- });
- }
|