home *** CD-ROM | disk | FTP | other *** search
/ PC Active 2009 March / PCA223.ISO / Software / tuneup / TU2008TrialNL.exe / jquery.accordion.js17 < prev    next >
Encoding:
Text File  |  2008-05-27  |  11.4 KB  |  365 lines

  1. /*
  2.  * Accordion 1.5 - jQuery menu widget
  3.  *
  4.  * Copyright (c) 2007 J÷rn Zaefferer, Frank Marcia
  5.  *
  6.  * http://bassistance.de/jquery-plugins/jquery-plugin-accordion/
  7.  *
  8.  * Dual licensed under the MIT and GPL licenses:
  9.  *   http://www.opensource.org/licenses/mit-license.php
  10.  *   http://www.gnu.org/licenses/gpl.html
  11.  *
  12.  * Revision: $Id: jquery.accordion.js 2880 2007-08-24 21:44:37Z joern.zaefferer $
  13.  *
  14.  */
  15.  
  16. /**
  17.  * Make the selected elements Accordion widgets.
  18.  *
  19.  * Semantic requirements:
  20.  *
  21.  * If the structure of your container is flat with unique
  22.  * tags for header and content elements, eg. a definition list
  23.  * (dl > dt + dd), you don't have to specify any options at
  24.  * all.
  25.  *
  26.  * If your structure uses the same elements for header and
  27.  * content or uses some kind of nested structure, you have to
  28.  * specify the header elements, eg. via class, see the second example.
  29.  *
  30.  * Use activate(Number) to change the active content programmatically.
  31.  *
  32.  * A change event is triggered everytime the accordion changes. Apart from
  33.  * the event object, all arguments are jQuery objects.
  34.  * Arguments: event, newHeader, oldHeader, newContent, oldContent
  35.  *
  36.  * @example jQuery('#nav').Accordion();
  37.  * @before <dl id="nav">
  38.  *   <dt>Header 1</dt>
  39.  *   <dd>Content 1</dd>
  40.  *   <dt>Header 2</dt>
  41.  *   <dd>Content 2</dd>
  42.  * </dl>
  43.  * @desc Creates an Accordion from the given definition list
  44.  *
  45.  * @example jQuery('#nav').Accordion({
  46.  *   header: '.title'
  47.  * });
  48.  * @before <div id="nav">
  49.  *  <div>
  50.  *    <div class="title">Header 1</div>
  51.  *    <div>Content 1</div>
  52.  *  </div>
  53.  *  <div>
  54.  *    <div class="title">Header 2</div>
  55.  *    <div>Content 2</div>
  56.  *  </div>
  57.  * </div>
  58.  * @desc Creates an Accordion from the given div structure
  59.  *
  60.  * @example jQuery('#nav').Accordion({
  61.  *   header: '.head',
  62.  *      navigation: true
  63.  * });
  64.  * @before <ul id="nav">
  65.  *   <li>
  66.  *     <a class="head" href="books/">Books</a>
  67.  *     <ul>
  68.  *       <li><a href="books/fantasy/">Fantasy</a></li>
  69.  *       <li><a href="books/programming/">Programming</a></li>
  70.  *     </ul>
  71.  *   </li>
  72.  *   <li>
  73.  *     <a class="head" href="movies/">Movies</a>
  74.  *     <ul>
  75.  *       <li><a href="movies/fantasy/">Fantasy</a></li>
  76.  *       <li><a href="movies/programming/">Programming</a></li>
  77.  *     </ul>
  78.  *   </li>
  79.  * </ul>
  80.  * @after <ul id="nav">
  81.  *   <li>
  82.  *     <a class="head" href="">Books</a>
  83.  *     <ul style="display: none">
  84.  *       <li><a href="books/fantasy/">Fantasy</a></li>
  85.  *       <li><a href="books/programming/">Programming</a></li>
  86.  *     </ul>
  87.  *   </li>
  88.  *   <li>
  89.  *     <a class="head" href="">Movies</a>
  90.  *     <ul>
  91.  *       <li><a class="current" href="movies/fantasy/">Fantasy</a></li>
  92.  *       <li><a href="movies/programming/">Programming</a></li>
  93.  *     </ul>
  94.  *   </li>
  95.  * </ul>
  96.  * @desc Creates an Accordion from the given navigation list, activating those accordion parts
  97.  * that match the current location.href. Assuming the user clicked on "Fantasy" in the "Movies" section,
  98.  * the accordion displayed after loading the page with the "Movies" section open and the "Fantasy" link highlighted
  99.  * with a class "current".
  100.  *
  101.  * @example jQuery('#accordion').Accordion().change(function(event, newHeader, oldHeader, newContent, oldContent) {
  102.  *   jQuery('#status').html(newHeader.text());
  103.  * });
  104.  * @desc Updates the element with id status with the text of the selected header every time the accordion changes
  105.  *
  106.  * @param Map options key/value pairs of optional settings.
  107.  * @option String|Element|jQuery|Boolean|Number active Selector for the active element. Set to false to display none at start. Default: first child
  108.  * @option String|Element|jQuery header Selector for the header element, eg. 'div.title', 'a.head'. Default: first child's tagname
  109.  * @option String|Number speed 
  110.  * @option String selectedClass Class for active header elements. Default: 'selected'
  111.  * @option Boolean alwaysOpen Whether there must be one content element open. Default: true
  112.  * @option Boolean|String animated Choose your favorite animation, or disable them (set to false). In addition to the default, "bounceslide" and "easeslide" are supported (both require the easing plugin). Default: 'slide'
  113.  * @option String event The event on which to trigger the accordion, eg. "mouseover". Default: "click"
  114.  * @option Boolean navigation If set, looks for the anchor that matches location.href and activates it. Great for href-based pseudo-state-saving. Default: false
  115.  * @option Boolean autoheight If set, the highest content part is used as height reference for all other parts. Provides more consistent animations. Default: false
  116.  *
  117.  * @type jQuery
  118.  * @see activate(Number)
  119.  * @name Accordion
  120.  * @cat Plugins/Accordion
  121.  */
  122.  
  123. /**
  124.  * Activate a content part of the Accordion programmatically.
  125.  *
  126.  * The index can be a zero-indexed number to match the position of the header to close
  127.  * or a string expression matching an element. Pass -1 to close all (only possible with alwaysOpen:false).
  128.  *
  129.  * @example jQuery('#accordion').activate(1);
  130.  * @desc Activate the second content of the Accordion contained in <div id="accordion">.
  131.  *
  132.  * @example jQuery('#accordion').activate("a:first");
  133.  * @desc Activate the first element matching the given expression.
  134.  *
  135.  * @example jQuery('#nav').activate(false);
  136.  * @desc Close all content parts of the accordion.
  137.  *
  138.  * @param String|Element|jQuery|Boolean|Number index An Integer specifying the zero-based index of the content to be
  139.  *                 activated or an expression specifying the element, or an element/jQuery object, or a boolean false to close all.
  140.  *
  141.  * @type jQuery
  142.  * @name activate
  143.  * @cat Plugins/Accordion
  144.  */
  145.  
  146. (function($) {
  147.  
  148. $.Accordion = {};
  149. $.extend($.Accordion, {
  150.     defaults: {
  151.         selectedClass: "selected",
  152.         alwaysOpen: true,
  153.         animated: 'slide',
  154.         event: "click"
  155.     },
  156.     Animations: {
  157.         slide: function(settings, additions) {
  158.             settings = $.extend({
  159.                 easing: "swing",
  160.                 duration: 300
  161.             }, settings, additions);
  162.             if ( !settings.toHide.size() ) {
  163.                 settings.toShow.animate({height: "show"}, {
  164.                     duration: settings.duration,
  165.                     easing: settings.easing,
  166.                     complete: settings.finished
  167.                 });
  168.                 return;
  169.             }
  170.             var height = settings.toHide.height();
  171.             settings.toShow.css({ height: 0, overflow: 'hidden' }).show();
  172.             settings.toHide.filter(":hidden").each(settings.finished).end().filter(":visible").animate({height:"hide"},{
  173.                 step: function(n){
  174.                     settings.toShow.height(Math.ceil(height - ($.fn.stop ? n * height : n)));
  175.                 },
  176.                 duration: settings.duration,
  177.                 easing: settings.easing,
  178.                 complete: settings.finished
  179.             });
  180.         },
  181.         bounceslide: function(settings) {
  182.             this.slide(settings, {
  183.                 easing: settings.down ? "bounceout" : "swing",
  184.                 duration: settings.down ? 1000 : 200
  185.             });
  186.         },
  187.         easeslide: function(settings) {
  188.             this.slide(settings, {
  189.                 easing: "easeinout",
  190.                 duration: 700
  191.             })
  192.         }
  193.     }
  194. });
  195.  
  196. $.fn.extend({
  197.     nextUntil: function(expr) {
  198.         var match = [];
  199.     
  200.         // We need to figure out which elements to push onto the array
  201.         this.each(function(){
  202.             // Traverse through the sibling nodes
  203.             for( var i = this.nextSibling; i; i = i.nextSibling ) {
  204.                 // Make sure that we're only dealing with elements
  205.                 if ( i.nodeType != 1 ) continue;
  206.     
  207.                 // If we find a match then we need to stop
  208.                 if ( $.filter( expr, [i] ).r.length ) break;
  209.     
  210.                 // Otherwise, add it on to the stack
  211.                 match.push( i );
  212.             }
  213.         });
  214.     
  215.         return this.pushStack( match );
  216.     },
  217.     // the plugin method itself
  218.     Accordion: function(settings) {
  219.         if ( !this.length )
  220.             return this;
  221.     
  222.         // setup configuration
  223.         settings = $.extend({}, $.Accordion.defaults, {
  224.             // define context defaults
  225.             header: $(':first-child', this)[0].tagName // take first childs tagName as header
  226.         }, settings);
  227.         
  228.         if ( settings.navigation ) {
  229.             var current = this.find("a").filter(function() { return this.href == location.href; });
  230.             if ( current.length ) {
  231.                 if ( current.filter(settings.header).length ) {
  232.                     settings.active = current;
  233.                 } else {
  234.                     settings.active = current.parent().parent().prev();
  235.                     current.addClass("current");
  236.                 }
  237.             }
  238.         }
  239.         
  240.         // calculate active if not specified, using the first header
  241.         var container = this,
  242.             headers = container.find(settings.header),
  243.             active = findActive(settings.active),
  244.             running = 0;
  245.  
  246.         if ( settings.autoheight ) {
  247.             var maxHeight = 0;
  248.             headers.nextUntil(settings.header).each(function() {
  249.                 maxHeight = Math.max(maxHeight, $(this).height());
  250.             }).height(maxHeight);
  251.         }
  252.  
  253.         headers
  254.             .not(active || "")
  255.             .nextUntil(settings.header)
  256.             .hide();
  257.         active.addClass(settings.selectedClass);
  258.         
  259.         
  260.         function findActive(selector) {
  261.             return selector != undefined
  262.                 ? typeof selector == "number"
  263.                     ? headers.eq(selector)
  264.                     : headers.not(headers.not(selector))
  265.                 : selector === false
  266.                     ? $("<div>")
  267.                     : headers.eq(0)
  268.         }
  269.         
  270.         function toggle(toShow, toHide, data, clickedActive, down) {
  271.             var finished = function(cancel) {
  272.                 running = cancel ? 0 : --running;
  273.                 if ( running )
  274.                     return;
  275.                 // trigger custom change event
  276.                 container.trigger("change", data);
  277.             };
  278.             
  279.             // count elements to animate
  280.             running = toHide.size() == 0 ? toShow.size() : toHide.size();
  281.             
  282.             if ( settings.animated ) {
  283.                 if ( !settings.alwaysOpen && clickedActive ) {
  284.                     toShow.slideToggle(settings.animated);
  285.                     finished(true);
  286.                 } else {
  287.                     $.Accordion.Animations[settings.animated]({
  288.                         toShow: toShow,
  289.                         toHide: toHide,
  290.                         finished: finished,
  291.                         down: down
  292.                     });
  293.                 }
  294.             } else {
  295.                 if ( !settings.alwaysOpen && clickedActive ) {
  296.                     toShow.toggle();
  297.                 } else {
  298.                     toHide.hide();
  299.                     toShow.show();
  300.                 }
  301.                 finished(true);
  302.             }
  303.         }
  304.         
  305.         function clickHandler(event) {
  306.             // called only when using activate(false) to close all parts programmatically
  307.             if ( !event.target && !settings.alwaysOpen ) {
  308.                 active.toggleClass(settings.selectedClass);
  309.                 var toHide = active.nextUntil(settings.header);
  310.                 var toShow = active = $([]);
  311.                 toggle( toShow, toHide );
  312.                 return;
  313.             }
  314.             // get the click target
  315.             var clicked = $(event.target);
  316.             
  317.             // due to the event delegation model, we have to check if one
  318.             // of the parent elements is our actual header, and find that
  319.             if ( clicked.parents(settings.header).length )
  320.                 while ( !clicked.is(settings.header) )
  321.                     clicked = clicked.parent();
  322.             
  323.             var clickedActive = clicked[0] == active[0];
  324.             
  325.             // if animations are still active, or the active header is the target, ignore click
  326.             if(running || (settings.alwaysOpen && clickedActive) || !clicked.is(settings.header))
  327.                 return;
  328.  
  329.             // switch classes
  330.             active.toggleClass(settings.selectedClass);
  331.             if ( !clickedActive ) {
  332.                 clicked.addClass(settings.selectedClass);
  333.             }
  334.  
  335.             // find elements to show and hide
  336.             var toShow = clicked.nextUntil(settings.header),
  337.                 toHide = active.nextUntil(settings.header),
  338.                 data = [clicked, active, toShow, toHide],
  339.                 down = headers.index( active[0] ) > headers.index( clicked[0] );
  340.             
  341.             active = clickedActive ? $([]) : clicked;
  342.             toggle( toShow, toHide, data, clickedActive, down );
  343.  
  344.             return !toShow.length;
  345.         };
  346.         function activateHandler(event, index) {
  347.             // IE manages to call activateHandler on normal clicks
  348.             if ( arguments.length == 1 )
  349.                 return;
  350.             // call clickHandler with custom event
  351.             clickHandler({
  352.                 target: findActive(index)[0]
  353.             });
  354.         };
  355.  
  356.         return container
  357.             .bind(settings.event, clickHandler)
  358.             .bind("activate", activateHandler);
  359.     },
  360.     activate: function(index) {
  361.         return this.trigger('activate', [index]);
  362.     }
  363. });
  364.  
  365. })(jQuery);