Source: delite/Dialog.js

/** @module delite/Dialog */
define([
	"dcl/dcl",
	"./a11y",
	"./Widget"
], function (dcl, a11y, Widget) {
	/**
	 * Base class for modal dialogs, where tabbing from the last element loops to the first, and vice-versa.
	 * @mixin module:delite/Dialog
	 * @augments module:delite/Widget
	 */
	return dcl(Widget, /** @lends module:delite/Dialog# */ {
		createdCallback: function () {
			this.on("keydown", this._dialogKeyDownHandler.bind(this));
		},

		_getFocusItems: function () {
			// summary:
			//		Finds focusable items in dialog,
			//		and sets this._firstFocusItem and this._lastFocusItem
			// tags:
			//		protected

			var elems = a11y._getTabNavigable(this.containerNode);
			this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
			this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
		},

		_dialogKeyDownHandler: function (/*Event*/ evt) {
			if (evt.key === "Tab") {
				this._getFocusItems(this.domNode);
				var node = evt.target;
				if (this._firstFocusItem === this._lastFocusItem) {
					// don't move focus anywhere, but don't allow browser to move focus off of dialog either
					evt.stopPropagation();
					evt.preventDefault();
				} else if (node === this._firstFocusItem && evt.shiftKey) {
					// if we are shift-tabbing from first focusable item in dialog, send focus to last item
					this._lastFocusItem.focus();
					evt.stopPropagation();
					evt.preventDefault();
				} else if (node === this._lastFocusItem && !evt.shiftKey) {
					// if we are tabbing from last focusable item in dialog, send focus to first item
					this._firstFocusItem.focus();
					evt.stopPropagation();
					evt.preventDefault();
				}
			}
		}
	});
});