Forgot to namespace the click handler. Unbinding all click handlers is indeed risky...
[lhc/web/wiklou.git] / resources / jquery / jquery.makeCollapsible.js
1 /**
2 * jQuery makeCollapsible
3 *
4 * This will enable collapsible-functionality on all passed elements.
5 * Will prevent binding twice to the same element.
6 * Initial state is expanded by default, this can be overriden by adding class
7 * "mw-collapsed" to the "mw-collapsible" element.
8 * Elements made collapsible have class "mw-made-collapsible".
9 * Except for tables and lists, the inner content is wrapped in "mw-collapsible-content".
10 *
11 * @author Krinkle <krinklemail@gmail.com>
12 *
13 * Dual license:
14 * @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
15 * @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
16 */
17
18 $.fn.makeCollapsible = function() {
19
20 return this.each(function() {
21
22 var $that = $(this).addClass( 'mw-collapsible' ), // in case $( '#myAJAXelement' ).makeCollapsible() was called
23 that = this,
24 collapsetext = $(this).attr( 'data-collapsetext' ),
25 expandtext = $(this).attr( 'data-expandtext' ),
26 toggleFunction = function( that ) {
27 var $that = $(that),
28 $collapsible = $that.closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
29
30 // It's expanded right now
31 if ( $that.hasClass( 'mw-collapsible-toggle-expanded' ) ) {
32 // Change link to "Show"
33 $that.removeClass( 'mw-collapsible-toggle-expanded' ).addClass( 'mw-collapsible-toggle-collapsed' );
34 if ( $that.find( '> a' ).size() ) {
35 $that.find( '> a' ).text( expandtext );
36 } else {
37 $that.text( expandtext );
38 }
39 // Hide the collapsible element
40 if ( $collapsible.is( 'table' ) ) {
41 // Hide all direct childing table rows of this table, except the row containing the link
42 // Slide doens't work, but fade works fine as of jQuery 1.1.3
43 // http://stackoverflow.com/questions/467336/jquery-how-to-use-slidedown-or-collapsed-function-on-a-table-row#920480
44 // Stop to prevent animations from stacking up
45 $collapsible.find( '> tbody > tr' ).not( $that.parent().parent() ).stop( true, true ).fadeOut();
46
47 } else if ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) {
48 $collapsible.find( '> li' ).not( $that.parent() ).stop( true, true ).slideUp();
49
50 } else { // <div>, <p> etc.
51 $collapsible.find( '> .mw-collapsible-content' ).slideUp();
52 }
53
54 // It's collapsed right now
55 } else {
56 // Change link to "Hide"
57 $that.removeClass( 'mw-collapsible-toggle-collapsed' ).addClass( 'mw-collapsible-toggle-expanded' );
58 if ( $that.find( '> a' ).size() ) {
59 $that.find( '> a' ).text( collapsetext );
60 } else {
61 $that.text( collapsetext );
62 }
63 // Show the collapsible element
64 if ( $collapsible.is( 'table' ) ) {
65 $collapsible.find( '> tbody > tr' ).not( $that.parent().parent() ).stop( true, true ).fadeIn();
66
67 } else if ( $collapsible.is( 'ul' ) || $collapsible.is( 'ol' ) ) {
68 $collapsible.find( '> li' ).not( $that.parent() ).stop( true, true ).slideDown();
69
70 } else { // <div>, <p> etc.
71 $collapsible.find( '> .mw-collapsible-content' ).slideDown();
72 }
73 }
74 return;
75 };
76
77 // Use custom text or default ?
78 if( !collapsetext || collapsetext == '' ){
79 collapsetext = mw.msg( 'collapsible-collapse' );
80 }
81 if ( !expandtext || expandtext == '' ){
82 expandtext = mw.msg( 'collapsible-expand' );
83 }
84
85 // Create toggle link with a space around the brackets (&nbsp;[text]&nbsp;)
86 var $toggleLink = $( '<a href="#">' ).text( collapsetext ).wrap( '<span class="mw-collapsible-toggle mw-collapsible-toggle-expanded">' ).parent().prepend( '&nbsp;[' ).append( ']&nbsp;' ).bind( 'click.mw-collapse', function(e){
87 e.preventDefault();
88 toggleFunction( this );
89 } );
90
91 // Skip if it has been enabled already.
92 if ( $that.hasClass( 'mw-made-collapsible' ) ) {
93 return;
94 } else {
95 $that.addClass( 'mw-made-collapsible' );
96 }
97
98 // Elements are treated differently
99 if ( $that.is( 'table' ) ) {
100 // The toggle-link will be in the last cell (td or th) of the first row
101 var $lastCell = $( 'tr:first th, tr:first td', that ).eq(-1),
102 $toggle = $lastCell.find( '> .mw-collapsible-toggle' );
103
104 if ( !$toggle.size() ) {
105 $lastCell.prepend( $toggleLink );
106 } else {
107 $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ){
108 e.preventDefault();
109 toggleFunction( this );
110 } );
111 }
112
113 } else if ( $that.is( 'ul' ) || $that.is( 'ol' ) ) {
114 // The toggle-link will be in the first list-item
115 var $firstItem = $( 'li:first', $that),
116 $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
117
118 if ( !$toggle.size() ) {
119 // Make sure the numeral order doesn't get messed up, reset to 1 unless value-attribute is already used
120 if ( $firstItem.attr( 'value' ) == '' ) {
121 $firstItem.attr( 'value', '1' );
122 }
123 $that.prepend( $toggleLink.wrap( '<li class="mw-collapsibile-toggle-li">' ).parent() );
124 } else {
125 $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ){
126 e.preventDefault();
127 toggleFunction( this );
128 } );
129 }
130
131 } else { // <div>, <p> etc.
132 // Is there an content-wrapper already made ?
133 // If a direct child with the class does not exists, create the wrap.
134 if ( !$that.find( '> .mw-collapsible-content' ).size() ) {
135 $that.wrapInner( '<div class="mw-collapsible-content">' );
136 }
137
138 // The toggle-link will be the first child of the element
139 var $toggle = $that.find( '> .mw-collapsible-toggle' );
140
141 if ( !$toggle.size() ) {
142 $that.prepend( $toggleLink );
143 } else {
144 $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ){
145 e.preventDefault();
146 toggleFunction( this );
147 } );
148 }
149
150 }
151 // Initial state
152 if ( $that.hasClass( 'mw-collapsed' ) ) {
153 $toggleLink.click();
154 }
155 } );
156 };