/** * @file * Javascript required for a simple collapsible div. * * Creating a collapsible div with this doesn't take too much. There are * three classes necessary: * * - ctools-collapsible-container: This is the overall container that will be * collapsible. This must be a div. * - ctools-collapsible-handle: This is the title area, and is what will be * visible when it is collapsed. This can be any block element, such as div * or h2. * - ctools-collapsible-content: This is the ocntent area and will only be * visible when expanded. This must be a div. * * Adding 'ctools-collapsible-remember' to the container class will cause the * state of the container to be stored in a cookie, and remembered from page * load to page load. This will only work if the container has a unique ID, so * very carefully add IDs to your containers. * * If the class 'ctools-no-container' is placed on the container, the container * will be the handle. The content will be found by appending '-content' to the * id of the handle. The ctools-collapsible-handle and * ctools-collapsible-content classes will not be required in that case, and no * restrictions on what of data the container is are placed. Like * ctools-collapsible-remember this requires an id to eist. * * The content will be 'open' unless the container class has 'ctools-collapsed' * as a class, which will cause the container to draw collapsed. */ (function ($) { // All CTools tools begin with this if they need to use the CTools namespace. if (!Drupal.CTools) { Drupal.CTools = {}; } /** * Object to store state. * * This object will remember the state of collapsible containers. The first * time a state is requested, it will check the cookie and set up the variable. * If a state has been changed, when the window is unloaded the state will be * saved. */ Drupal.CTools.Collapsible = { state: {}, stateLoaded: false, stateChanged: false, cookieString: 'ctools-collapsible-state=', /** * Get the current collapsed state of a container. * * If set to 1, the container is open. If set to -1, the container is * collapsed. If unset the state is unknown, and the default state should * be used. */ getState: function (id) { if (!this.stateLoaded) { this.loadCookie(); } return this.state[id]; }, /** * Set the collapsed state of a container for subsequent page loads. * * Set the state to 1 for open, -1 for collapsed. */ setState: function (id, state) { if (!this.stateLoaded) { this.loadCookie(); } this.state[id] = state; if (!this.stateChanged) { this.stateChanged = true; $(window).unload(this.unload); } }, /** * Check the cookie and load the state variable. */ loadCookie: function () { // If there is a previous instance of this cookie if (document.cookie.length > 0) { // Get the number of characters that have the list of values // from our string index. offset = document.cookie.indexOf(this.cookieString); // If its positive, there is a list! if (offset != -1) { offset += this.cookieString.length; var end = document.cookie.indexOf(';', offset); if (end == -1) { end = document.cookie.length; } // Get a list of all values that are saved on our string var cookie = unescape(document.cookie.substring(offset, end)); if (cookie != '') { var cookieList = cookie.split(','); for (var i = 0; i < cookieList.length; i++) { var info = cookieList[i].split(':'); this.state[info[0]] = info[1]; } } } } this.stateLoaded = true; }, /** * Turn the state variable into a string and store it in the cookie. */ storeCookie: function () { var cookie = ''; // Get a list of IDs, saparated by comma for (i in this.state) { if (cookie != '') { cookie += ','; } cookie += i + ':' + this.state[i]; } // Save this values on the cookie document.cookie = this.cookieString + escape(cookie) + ';path=/'; }, /** * Respond to the unload event by storing the current state. */ unload: function() { Drupal.CTools.Collapsible.storeCookie(); } }; // Set up an array for callbacks. Drupal.CTools.CollapsibleCallbacks = []; Drupal.CTools.CollapsibleCallbacksAfterToggle = []; /** * Bind collapsible behavior to a given container. */ Drupal.CTools.bindCollapsible = function () { var $container = $(this); // Allow the specification of the 'no container' class, which means the // handle and the container can be completely independent. if ($container.hasClass('ctools-no-container') && $container.attr('id')) { // In this case, the container *is* the handle and the content is found // by adding '-content' to the id. Obviously, an id is required. var handle = $container; var content = $('#' + $container.attr('id') + '-content'); } else { var handle = $container.children('.ctools-collapsible-handle'); var content = $container.children('div.ctools-collapsible-content'); } if (content.length) { // Create the toggle item and place it in front of the toggle. var toggle = $(''); handle.before(toggle); // If the remember class is set, check to see if we have a remembered // state stored. if ($container.hasClass('ctools-collapsible-remember') && $container.attr('id')) { var state = Drupal.CTools.Collapsible.getState($container.attr('id')); if (state == 1) { $container.removeClass('ctools-collapsed'); } else if (state == -1) { $container.addClass('ctools-collapsed'); } } // If we should start collapsed, do so: if ($container.hasClass('ctools-collapsed')) { toggle.toggleClass('ctools-toggle-collapsed'); content.hide(); } var afterToggle = function () { if (Drupal.CTools.CollapsibleCallbacksAfterToggle) { for (i in Drupal.CTools.CollapsibleCallbacksAfterToggle) { Drupal.CTools.CollapsibleCallbacksAfterToggle[i]($container, handle, content, toggle); } } } var clickMe = function () { if (Drupal.CTools.CollapsibleCallbacks) { for (i in Drupal.CTools.CollapsibleCallbacks) { Drupal.CTools.CollapsibleCallbacks[i]($container, handle, content, toggle); } } // If the container is a table element slideToggle does not do what // we want, so use toggle() instead. if ($container.is('table')) { content.toggle(0, afterToggle); } else { content.slideToggle(100, afterToggle); } $container.toggleClass('ctools-collapsed'); toggle.toggleClass('ctools-toggle-collapsed'); // If we're supposed to remember the state of this class, do so. if ($container.hasClass('ctools-collapsible-remember') && $container.attr('id')) { var state = toggle.hasClass('ctools-toggle-collapsed') ? -1 : 1; Drupal.CTools.Collapsible.setState($container.attr('id'), state); } return false; } // Let both the toggle and the handle be clickable. toggle.click(clickMe); handle.click(clickMe); } }; /** * Support Drupal's 'behaviors' system for binding. */ Drupal.behaviors.CToolsCollapsible = { attach: function(context) { $('.ctools-collapsible-container', context).once('ctools-collapsible', Drupal.CTools.bindCollapsible); } } })(jQuery);