2 * Collapsible tabs jQuery Plugin
5 var rtl
= $( 'html' ).attr( 'dir' ) === 'rtl',
6 rAF
= window
.requestAnimationFrame
|| setTimeout
;
8 $.fn
.collapsibleTabs = function ( options
) {
9 // Merge options into the defaults
10 var settings
= $.extend( {}, $.collapsibleTabs
.defaults
, options
);
12 // return if the function is called on an empty jquery object
17 this.each( function () {
19 // add the element to our array of collapsible managers
20 $.collapsibleTabs
.instances
= $.collapsibleTabs
.instances
.add( $el
);
21 // attach the settings to the elements
22 $el
.data( 'collapsibleTabsSettings', settings
);
23 // attach data to our collapsible elements
24 $el
.children( settings
.collapsible
).each( function () {
25 $.collapsibleTabs
.addData( $( this ) );
29 // if we haven't already bound our resize handler, bind it now
30 if ( !$.collapsibleTabs
.boundEvent
) {
31 $( window
).on( 'resize', $.debounce( 100, function () {
32 rAF( $.collapsibleTabs
.handleResize
);
34 $.collapsibleTabs
.boundEvent
= true;
37 // call our resize handler to setup the page
38 rAF( $.collapsibleTabs
.handleResize
);
45 expandedContainer
: '#p-views ul',
46 collapsedContainer
: '#p-cactions ul',
47 collapsible
: 'li.collapsible',
49 expandCondition: function ( eleWidth
) {
50 // If there are at least eleWidth + 1 pixels of free space, expand.
51 // We add 1 because .width() will truncate fractional values but .offset() will not.
52 return $.collapsibleTabs
.calculateTabDistance() >= eleWidth
+ 1;
54 collapseCondition: function () {
55 // If there's an overlap, collapse.
56 return $.collapsibleTabs
.calculateTabDistance() < 0;
59 addData: function ( $collapsible
) {
60 var settings
= $collapsible
.parent().data( 'collapsibleTabsSettings' );
62 $collapsible
.data( 'collapsibleTabsSettings', {
63 expandedContainer
: settings
.expandedContainer
,
64 collapsedContainer
: settings
.collapsedContainer
,
65 expandedWidth
: $collapsible
.width(),
66 prevElement
: $collapsible
.prev()
70 getSettings: function ( $collapsible
) {
71 var settings
= $collapsible
.data( 'collapsibleTabsSettings' );
73 $.collapsibleTabs
.addData( $collapsible
);
74 settings
= $collapsible
.data( 'collapsibleTabsSettings' );
78 handleResize: function () {
79 $.collapsibleTabs
.instances
.each( function () {
81 data
= $.collapsibleTabs
.getSettings( $el
);
83 if ( data
.shifting
) {
87 // if the two navigations are colliding
88 if ( $el
.children( data
.collapsible
).length
> 0 && data
.collapseCondition() ) {
90 $el
.trigger( 'beforeTabCollapse' );
91 // move the element to the dropdown menu
92 $.collapsibleTabs
.moveToCollapsed( $el
.children( data
.collapsible
+ ':last' ) );
95 // if there are still moveable items in the dropdown menu,
96 // and there is sufficient space to place them in the tab container
97 if ( $( data
.collapsedContainer
+ ' ' + data
.collapsible
).length
> 0 &&
98 data
.expandCondition( $.collapsibleTabs
.getSettings( $( data
.collapsedContainer
).children(
99 data
.collapsible
+ ':first' ) ).expandedWidth
) ) {
100 // move the element from the dropdown to the tab
101 $el
.trigger( 'beforeTabExpand' );
103 .moveToExpanded( data
.collapsedContainer
+ ' ' + data
.collapsible
+ ':first' );
107 moveToCollapsed: function ( ele
) {
108 var outerData
, expContainerSettings
, target
,
111 outerData
= $.collapsibleTabs
.getSettings( $moving
);
115 expContainerSettings
= $.collapsibleTabs
.getSettings( $( outerData
.expandedContainer
) );
116 if ( !expContainerSettings
) {
119 expContainerSettings
.shifting
= true;
121 // Remove the element from where it's at and put it in the dropdown menu
122 target
= outerData
.collapsedContainer
;
123 $moving
.css( 'position', 'relative' )
124 .css( ( rtl
? 'left' : 'right' ), 0 )
125 .animate( { width
: '1px' }, 'normal', function () {
126 var data
, expContainerSettings
;
128 // add the placeholder
129 $( '<span class="placeholder" style="display: none;"></span>' ).insertAfter( this );
130 $( this ).detach().prependTo( target
).data( 'collapsibleTabsSettings', outerData
);
131 $( this ).attr( 'style', 'display: list-item;' );
132 data
= $.collapsibleTabs
.getSettings( $( ele
) );
134 expContainerSettings
= $.collapsibleTabs
.getSettings( $( data
.expandedContainer
) );
135 if ( expContainerSettings
) {
136 expContainerSettings
.shifting
= false;
137 rAF( $.collapsibleTabs
.handleResize
);
142 moveToExpanded: function ( ele
) {
143 var data
, expContainerSettings
, $target
, expandedWidth
,
146 data
= $.collapsibleTabs
.getSettings( $moving
);
150 expContainerSettings
= $.collapsibleTabs
.getSettings( $( data
.expandedContainer
) );
151 if ( !expContainerSettings
) {
154 expContainerSettings
.shifting
= true;
156 // grab the next appearing placeholder so we can use it for replacing
157 $target
= $( data
.expandedContainer
).find( 'span.placeholder:first' );
158 expandedWidth
= data
.expandedWidth
;
159 $moving
.css( 'position', 'relative' ).css( ( rtl
? 'right' : 'left' ), 0 ).css( 'width', '1px' );
163 .css( 'width', '1px' )
164 .data( 'collapsibleTabsSettings', data
)
165 .animate( { width
: expandedWidth
+ 'px' }, 'normal', function () {
166 var data
, expContainerSettings
;
167 $( this ).attr( 'style', 'display: block;' );
168 data
= $.collapsibleTabs
.getSettings( $( this ) );
170 expContainerSettings
= $.collapsibleTabs
.getSettings( $( data
.expandedContainer
) );
171 if ( expContainerSettings
) {
172 expContainerSettings
.shifting
= false;
173 rAF( $.collapsibleTabs
.handleResize
);
180 * Returns the amount of horizontal distance between the two tabs groups
181 * (#left-navigation and #right-navigation), in pixels. If negative, this
182 * means that the tabs overlap, and the value is the width of overlapping
185 * Used in default expandCondition and collapseCondition.
187 * @return {Numeric} distance/overlap in pixels
189 calculateTabDistance: function () {
190 var $leftTab
, $rightTab
, leftEnd
, rightStart
;
192 // In RTL, #right-navigation is actually on the left and vice versa.
193 // Hooray for descriptive naming.
195 $leftTab
= $( '#left-navigation' );
196 $rightTab
= $( '#right-navigation' );
198 $leftTab
= $( '#right-navigation' );
199 $rightTab
= $( '#left-navigation' );
202 leftEnd
= $leftTab
.offset().left
+ $leftTab
.width();
203 rightStart
= $rightTab
.offset().left
;
205 return rightStart
- leftEnd
;