Cleanup message file documentation
[mediawiki.git] / skins / vector / collapsibleTabs.js
blobe24bea95d99c6403194a7d3f58b0c7f6a8a87c43
1 /**
2  * Collapsible tabs jQuery Plugin
3  */
4 ( function ( $ ) {
5         var rtl = $( 'html' ).attr( 'dir' ) === 'rtl';
6         $.fn.collapsibleTabs = function ( options ) {
7                 // return if the function is called on an empty jquery object
8                 if ( !this.length ) {
9                         return this;
10                 }
11                 // Merge options into the defaults
12                 var settings = $.extend( {}, $.collapsibleTabs.defaults, options );
14                 this.each( function () {
15                         var $el = $( this );
16                         // add the element to our array of collapsible managers
17                         $.collapsibleTabs.instances = ( $.collapsibleTabs.instances.length === 0 ?
18                                 $el : $.collapsibleTabs.instances.add( $el ) );
19                         // attach the settings to the elements
20                         $el.data( 'collapsibleTabsSettings', settings );
21                         // attach data to our collapsible elements
22                         $el.children( settings.collapsible ).each( function () {
23                                 $.collapsibleTabs.addData( $( this ) );
24                         } );
25                 } );
27                 // if we haven't already bound our resize handler, bind it now
28                 if ( !$.collapsibleTabs.boundEvent ) {
29                         $( window ).on( 'resize', $.debounce( 500, function () {
30                                 $.collapsibleTabs.handleResize();
31                         } ) );
32                         $.collapsibleTabs.boundEvent = true;
33                 }
35                 // call our resize handler to setup the page
36                 $.collapsibleTabs.handleResize();
37                 return this;
38         };
39         $.collapsibleTabs = {
40                 instances: [],
41                 boundEvent: null,
42                 defaults: {
43                         expandedContainer: '#p-views ul',
44                         collapsedContainer: '#p-cactions ul',
45                         collapsible: 'li.collapsible',
46                         shifting: false,
47                         expandCondition: function ( eleWidth ) {
48                                 // If there are at least eleWidth + 1 pixels of free space, expand.
49                                 // We add 1 because .width() will truncate fractional values but .offset() will not.
50                                 return $.collapsibleTabs.calculateTabDistance() >= eleWidth + 1;
51                         },
52                         collapseCondition: function () {
53                                 // If there's an overlap, collapse.
54                                 return $.collapsibleTabs.calculateTabDistance() < 0;
55                         }
56                 },
57                 addData: function ( $collapsible ) {
58                         var settings = $collapsible.parent().data( 'collapsibleTabsSettings' );
59                         if ( settings ) {
60                                 $collapsible.data( 'collapsibleTabsSettings', {
61                                         expandedContainer: settings.expandedContainer,
62                                         collapsedContainer: settings.collapsedContainer,
63                                         expandedWidth: $collapsible.width(),
64                                         prevElement: $collapsible.prev()
65                                 } );
66                         }
67                 },
68                 getSettings: function ( $collapsible ) {
69                         var settings = $collapsible.data( 'collapsibleTabsSettings' );
70                         if ( !settings ) {
71                                 $.collapsibleTabs.addData( $collapsible );
72                                 settings = $collapsible.data( 'collapsibleTabsSettings' );
73                         }
74                         return settings;
75                 },
76                 handleResize: function () {
77                         $.collapsibleTabs.instances.each( function () {
78                                 var $el = $( this ),
79                                         data = $.collapsibleTabs.getSettings( $el );
81                                 if ( data.shifting ) {
82                                         return;
83                                 }
85                                 // if the two navigations are colliding
86                                 if ( $el.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
88                                         $el.trigger( 'beforeTabCollapse' );
89                                         // move the element to the dropdown menu
90                                         $.collapsibleTabs.moveToCollapsed( $el.children( data.collapsible + ':last' ) );
91                                 }
93                                 // if there are still moveable items in the dropdown menu,
94                                 // and there is sufficient space to place them in the tab container
95                                 if ( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0 &&
96                                                 data.expandCondition( $.collapsibleTabs.getSettings( $( data.collapsedContainer ).children(
97                                                                 data.collapsible + ':first' ) ).expandedWidth ) ) {
98                                         //move the element from the dropdown to the tab
99                                         $el.trigger( 'beforeTabExpand' );
100                                         $.collapsibleTabs
101                                                 .moveToExpanded( data.collapsedContainer + ' ' + data.collapsible + ':first' );
102                                 }
103                         } );
104                 },
105                 moveToCollapsed: function ( ele ) {
106                         var outerData, expContainerSettings, target,
107                                 $moving = $( ele );
109                         outerData = $.collapsibleTabs.getSettings( $moving );
110                         if ( !outerData ) {
111                                 return;
112                         }
113                         expContainerSettings = $.collapsibleTabs.getSettings( $( outerData.expandedContainer ) );
114                         if ( !expContainerSettings ) {
115                                 return;
116                         }
117                         expContainerSettings.shifting = true;
119                         // Remove the element from where it's at and put it in the dropdown menu
120                         target = outerData.collapsedContainer;
121                         $moving.css( 'position', 'relative' )
122                                 .css( ( rtl ? 'left' : 'right' ), 0 )
123                                 .animate( { width: '1px' }, 'normal', function () {
124                                         var data, expContainerSettings;
125                                         $( this ).hide();
126                                         // add the placeholder
127                                         $( '<span class="placeholder" style="display: none;"></span>' ).insertAfter( this );
128                                         $( this ).detach().prependTo( target ).data( 'collapsibleTabsSettings', outerData );
129                                         $( this ).attr( 'style', 'display: list-item;' );
130                                         data = $.collapsibleTabs.getSettings( $( ele ) );
131                                         if ( data ) {
132                                                 expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
133                                                 if ( expContainerSettings ) {
134                                                         expContainerSettings.shifting = false;
135                                                         $.collapsibleTabs.handleResize();
136                                                 }
137                                         }
138                                 } );
139                 },
140                 moveToExpanded: function ( ele ) {
141                         var data, expContainerSettings, $target, expandedWidth,
142                                 $moving = $( ele );
144                         data = $.collapsibleTabs.getSettings( $moving );
145                         if ( !data ) {
146                                 return;
147                         }
148                         expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
149                         if ( !expContainerSettings ) {
150                                 return;
151                         }
152                         expContainerSettings.shifting = true;
154                         // grab the next appearing placeholder so we can use it for replacing
155                         $target = $( data.expandedContainer ).find( 'span.placeholder:first' );
156                         expandedWidth = data.expandedWidth;
157                         $moving.css( 'position', 'relative' ).css( ( rtl ? 'right' : 'left' ), 0 ).css( 'width', '1px' );
158                         $target.replaceWith(
159                                 $moving
160                                 .detach()
161                                 .css( 'width', '1px' )
162                                 .data( 'collapsibleTabsSettings', data )
163                                 .animate( { width: expandedWidth + 'px' }, 'normal', function () {
164                                         $( this ).attr( 'style', 'display: block;' );
165                                         var data, expContainerSettings;
166                                         data = $.collapsibleTabs.getSettings( $( this ) );
167                                         if ( data ) {
168                                                 expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
169                                                 if ( expContainerSettings ) {
170                                                         expContainerSettings.shifting = false;
171                                                         $.collapsibleTabs.handleResize();
172                                                 }
173                                         }
174                                 } )
175                         );
176                 },
177                 /**
178                  * Returns the amount of horizontal distance between the two tabs groups
179                  * (#left-navigation and #right-navigation), in pixels. If negative, this
180                  * means that the tabs overlap, and the value is the width of overlapping
181                  * parts.
182                  *
183                  * Used in default expandCondition and collapseCondition.
184                  *
185                  * @return {Numeric} distance/overlap in pixels
186                  */
187                 calculateTabDistance: function () {
188                         var $leftTab, $rightTab, leftEnd, rightStart;
190                         // In RTL, #right-navigation is actually on the left and vice versa.
191                         // Hooray for descriptive naming.
192                         if ( !rtl ) {
193                                 $leftTab = $( '#left-navigation' );
194                                 $rightTab = $( '#right-navigation' );
195                         } else {
196                                 $leftTab = $( '#right-navigation' );
197                                 $rightTab = $( '#left-navigation' );
198                         }
200                         leftEnd = $leftTab.offset().left + $leftTab.width();
201                         rightStart = $rightTab.offset().left;
203                         return rightStart - leftEnd;
204                 }
205         };
207 }( jQuery ) );