/** * Defines a `Container` Class. * * Containers are activated based on the `showOn` setting for the container. * The values are normalized to functions which accept an element and return a * boolean; true means the container should be shown. * * For efficiency, we group all containers that have the same normalized * `showOn()' function together, so we can evaluate it once, regardless of how * many containers are using the same logic. In order for this to work, the * exact same function must be returned from `Container.normalizeShowOn()' when * the logic is the same. * * The list of containers is then stored on the context instance as * `context.containers', which is a hash of `showOn()' ids to an array of * containers. The `showOn()' ids are unique identifiers that are stored as * properties of the `showOn()' function (see `getShowOnId()'). This gives us * constant lookup times when grouping containers. */ define([ 'jquery', 'util/class', 'ui/scopes' ], function( $, Class, Scopes ) { 'use strict'; var uid = 0; /** * Gets the id of a normalized showOn option. If the given function has * not had its showOnId set it will receive one, the first time this * function it is passed to this function. * * @param {function} showOn The function whose id we wish to get. * @return {number} The id of the given function. */ function getShowOnId(showOn) { // Store a unique id on the showOn function. // See full explanation at top of file. if (!showOn.showOnId) { showOn.showOnId = ++uid; } return showOn.showOnId; } /** * Show or hide a set of containers. * * @param {Array.} containers The set of containers to operate * on. * @param {boolean} show Whether to show or hide the given containers. */ function toggleContainers(containers, show) { var action = show ? 'show' : 'hide', i; for (i = 0; i < containers.length; i++) { containers[i][action](); } } var scopeFns = {}; var returnTrue = function() { return true; }; /** * Normalizes a showOn option into a function. * * @param {(string|boolean|function)} showOn * @return function */ function normalizeShowOn(container, showOn) { switch ($.type(showOn)) { case 'function': return showOn; case 'object': if (showOn.scope) { if (scopeFns[showOn.scope]) { return scopeFns[showOn.scope]; } return scopeFns[showOn.scope] = function() { return Scopes.isActiveScope(showOn.scope); }; } else { throw "Invalid showOn configuration"; } default: return returnTrue; } } /** * Container class. * * @class * @base */ var Container = Class.extend({ /** * The containing (wrapper) element for this container. * * @type {jQuery} */ element: null, /** * Initialize a new container with the specified properties. * * @param {object=} settings Optional properties, and override methods. * @constructor */ _constructor: function(context, settings) { var showOn = normalizeShowOn(this, settings.showOn), key = getShowOnId(showOn), group = context.containers[key]; this.context = context; if (!group) { group = context.containers[key] = { shouldShow: showOn, containers: [] }; } group.containers.push(this); }, /** * Must be implemented by extending classes. * * @ingroup api * @{ */ /** * A container is also a component; this is part of the component API. */ show: function() {}, /** * A container is also a component; this is part of the component API. */ hide: function() {}, /** * A container is also a component; this is part of the component API. */ focus: function() {}, /** * A container is also a component; this is part of the component API. */ foreground: function() {}, /** * The container was previously hidden, and now has become visible. This * allows a container to let its children react to this. */ childVisible: function(childComponent, visible) {}, /** * The container was given focus; this method must give focus to all * children of the container. * Optional. (E.g. tab.js doesn't implement this.) */ childFocus: function(childComponent) {}, /** * The container was foregrounded; this method must foreground all children * of the container. */ childForeground: function(childComponent) {} /** * @} End of "ingroup api". */ }); // static fields $.extend( Container, { /** * Given an array of elements, show appropriate containers. * * @param {object} context * @param {string} eventType Type of the event triggered (optional) * @static */ showContainersForContext: function(context, eventType) { var group, groupKey, containerGroups; if (!context.containers) { // No containers were constructed for the given context, so // there is nothing for us to do. return; } containerGroups = context.containers; for (groupKey in containerGroups) { if (containerGroups.hasOwnProperty(groupKey)) { group = containerGroups[groupKey]; toggleContainers(group.containers, group.shouldShow(eventType)); } } } }); return Container; });