Merge "Several tweaks to the install.php script"
[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 /**
40 * Returns the amount of horizontal distance between the two tabs groups
41 * (#left-navigation and #right-navigation), in pixels. If negative, this
42 * means that the tabs overlap, and the value is the width of overlapping
43 * parts.
44 *
45 * Used in default expandCondition and collapseCondition.
46 *
47 * @return {Numeric} distance/overlap in pixels
48 */
49 function calculateTabDistance() {
50 var $leftTab, $rightTab, leftEnd, rightStart;
51
52 // In RTL, #right-navigation is actually on the left and vice versa.
53 // Hooray for descriptive naming.
54 if ( !rtl ) {
55 $leftTab = $( '#left-navigation' );
56 $rightTab = $( '#right-navigation' );
57 } else {
58 $leftTab = $( '#right-navigation' );
59 $rightTab = $( '#left-navigation' );
60 }
61
62 leftEnd = $leftTab.offset().left + $leftTab.width();
63 rightStart = $rightTab.offset().left;
64
65 return rightStart - leftEnd;
66 }
67 $.collapsibleTabs = {
68 instances: [],
69 boundEvent: null,
70 defaults: {
71 expandedContainer: '#p-views ul',
72 collapsedContainer: '#p-cactions ul',
73 collapsible: 'li.collapsible',
74 shifting: false,
75 expandCondition: function ( eleWidth ) {
76 // If there are at least eleWidth + 1 pixels of free space, expand.
77 // We add 1 because .width() will truncate fractional values
78 // but .offset() will not.
79 return calculateTabDistance() >= (eleWidth + 1);
80 },
81 collapseCondition: function () {
82 // If there's an overlap, collapse.
83 return calculateTabDistance() < 0;
84 }
85 },
86 addData: function ( $collapsible ) {
87 var $settings = $collapsible.parent().data( 'collapsibleTabsSettings' );
88 if ( $settings ) {
89 $collapsible.data( 'collapsibleTabsSettings', {
90 expandedContainer: $settings.expandedContainer,
91 collapsedContainer: $settings.collapsedContainer,
92 expandedWidth: $collapsible.width(),
93 prevElement: $collapsible.prev()
94 } );
95 }
96 },
97 getSettings: function ( $collapsible ) {
98 var $settings = $collapsible.data( 'collapsibleTabsSettings' );
99 if ( !$settings ) {
100 $.collapsibleTabs.addData( $collapsible );
101 $settings = $collapsible.data( 'collapsibleTabsSettings' );
102 }
103 return $settings;
104 },
105 handleResize: function () {
106 $.collapsibleTabs.instances.each( function () {
107 var $el = $( this ),
108 data = $.collapsibleTabs.getSettings( $el );
109
110 if ( data.shifting ) {
111 return;
112 }
113
114 // if the two navigations are colliding
115 if ( $el.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
116
117 $el.trigger( 'beforeTabCollapse' );
118 // move the element to the dropdown menu
119 $.collapsibleTabs.moveToCollapsed( $el.children( data.collapsible + ':last' ) );
120 }
121
122 // if there are still moveable items in the dropdown menu,
123 // and there is sufficient space to place them in the tab container
124 if ( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0 &&
125 data.expandCondition( $.collapsibleTabs.getSettings( $( data.collapsedContainer ).children(
126 data.collapsible + ':first' ) ).expandedWidth ) ) {
127 //move the element from the dropdown to the tab
128 $el.trigger( 'beforeTabExpand' );
129 $.collapsibleTabs
130 .moveToExpanded( data.collapsedContainer + ' ' + data.collapsible + ':first' );
131 }
132 } );
133 },
134 moveToCollapsed: function ( ele ) {
135 var outerData, expContainerSettings, target,
136 $moving = $( ele );
137
138 outerData = $.collapsibleTabs.getSettings( $moving );
139 if ( !outerData ) {
140 return;
141 }
142 expContainerSettings = $.collapsibleTabs.getSettings( $( outerData.expandedContainer ) );
143 if ( !expContainerSettings ) {
144 return;
145 }
146 expContainerSettings.shifting = true;
147
148 // Remove the element from where it's at and put it in the dropdown menu
149 target = outerData.collapsedContainer;
150 $moving.css( 'position', 'relative' )
151 .css( ( rtl ? 'left' : 'right' ), 0 )
152 .animate( { width: '1px' }, 'normal', function () {
153 var data, expContainerSettings;
154 $( this ).hide();
155 // add the placeholder
156 $( '<span class="placeholder" style="display: none;"></span>' ).insertAfter( this );
157 $( this ).detach().prependTo( target ).data( 'collapsibleTabsSettings', outerData );
158 $( this ).attr( 'style', 'display: list-item;' );
159 data = $.collapsibleTabs.getSettings( $( ele ) );
160 if ( data ) {
161 expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
162 if ( expContainerSettings ) {
163 expContainerSettings.shifting = false;
164 $.collapsibleTabs.handleResize();
165 }
166 }
167 } );
168 },
169 moveToExpanded: function ( ele ) {
170 var data, expContainerSettings, $target, expandedWidth,
171 $moving = $( ele );
172
173 data = $.collapsibleTabs.getSettings( $moving );
174 if ( !data ) {
175 return;
176 }
177 expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
178 if ( !expContainerSettings ) {
179 return;
180 }
181 expContainerSettings.shifting = true;
182
183 // grab the next appearing placeholder so we can use it for replacing
184 $target = $( data.expandedContainer ).find( 'span.placeholder:first' );
185 expandedWidth = data.expandedWidth;
186 $moving.css( 'position', 'relative' ).css( ( rtl ? 'right' : 'left' ), 0 ).css( 'width', '1px' );
187 $target.replaceWith(
188 $moving
189 .detach()
190 .css( 'width', '1px' )
191 .data( 'collapsibleTabsSettings', data )
192 .animate( { width: expandedWidth + 'px' }, 'normal', function () {
193 $( this ).attr( 'style', 'display: block;' );
194 var data, expContainerSettings;
195 data = $.collapsibleTabs.getSettings( $( this ) );
196 if ( data ) {
197 expContainerSettings = $.collapsibleTabs.getSettings( $( data.expandedContainer ) );
198 if ( expContainerSettings ) {
199 expContainerSettings.shifting = false;
200 $.collapsibleTabs.handleResize();
201 }
202 }
203 } )
204 );
205 }
206 };
207
208 }( jQuery ) );