Localisation updates from https://translatewiki.net.
[lhc/web/wiklou.git] / skins / Vector / collapsibleTabs.js
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 );
13
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 } );
26
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 }
34
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 );
80
81 if ( data.shifting ) {
82 return;
83 }
84
85 // if the two navigations are colliding
86 if ( $el.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
87
88 $el.trigger( 'beforeTabCollapse' );
89 // move the element to the dropdown menu
90 $.collapsibleTabs.moveToCollapsed( $el.children( data.collapsible + ':last' ) );
91 }
92
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 );
108
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;
118
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 );
143
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;
153
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;
189
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 }
199
200 leftEnd = $leftTab.offset().left + $leftTab.width();
201 rightStart = $rightTab.offset().left;
202
203 return rightStart - leftEnd;
204 }
205 };
206
207 }( jQuery ) );