This repository has been archived by the owner on Oct 8, 2021. It is now read-only.
/
jquery.mobile.fixHeaderFooter.js
202 lines (186 loc) · 7.15 KB
/
jquery.mobile.fixHeaderFooter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
* jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers
* Copyright (c) jQuery Project
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
* Note: Code is in draft form and is subject to change
*/
(function($, undefined ) {
$.fn.fixHeaderFooter = function(options){
if( !$.support.scrollTop ){ return $(this); }
return $(this).each(function(){
if( $(this).data('fullscreen') ){ $(this).addClass('ui-page-fullscreen'); }
$(this).find('.ui-header[data-position="fixed"]').addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown
$(this).find('.ui-footer[data-position="fixed"]').addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup
});
};
//single controller for all showing,hiding,toggling
$.fixedToolbars = (function(){
if( !$.support.scrollTop ){ return; }
var currentstate = 'inline',
delayTimer,
ignoreTargets = 'a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed',
toolbarSelector = '.ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last',
stickyFooter, //for storing quick references to duplicate footers
supportTouch = $.support.touch,
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
stateBefore = null,
scrollTriggered = false;
$(function() {
$(document)
.bind(touchStartEvent,function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; }
stateBefore = currentstate;
$.fixedToolbars.hide(true);
})
.bind('scrollstart',function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; } //because it could be a touchmove...
scrollTriggered = true;
if(stateBefore == null){ stateBefore = currentstate; }
$.fixedToolbars.hide(true);
})
.bind(touchStopEvent,function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; }
if( !scrollTriggered ){
$.fixedToolbars.toggle(stateBefore);
stateBefore = null;
}
})
.bind('scrollstop',function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; }
scrollTriggered = false;
$.fixedToolbars.toggle( stateBefore == 'overlay' ? 'inline' : 'overlay' );
stateBefore = null;
});
//function to return another footer already in the dom with the same data-id
function findStickyFooter(el){
var thisFooter = el.find('[data-role="footer"]');
return $( '.ui-footer[data-id="'+ thisFooter.data('id') +'"]:not(.ui-footer-duplicate)' ).not(thisFooter);
}
//before page is shown, check for duplicate footer
$('.ui-page').live('pagebeforeshow', function(event, ui){
stickyFooter = findStickyFooter( $(event.target) );
if( stickyFooter.length ){
//if the existing footer is the first of its kind, create a placeholder before stealing it
if( stickyFooter.parents('.ui-page:eq(0)').find('.ui-footer[data-id="'+ stickyFooter.data('id') +'"]').length == 1 ){
stickyFooter.before( stickyFooter.clone().addClass('ui-footer-duplicate') );
}
$(event.target).find('[data-role="footer"]').addClass('ui-footer-duplicate');
stickyFooter.appendTo($.pageContainer).css('top',0);
setTop(stickyFooter);
}
});
//after page is shown, append footer to new page
$('.ui-page').live('pageshow', function(event, ui){
if( stickyFooter && stickyFooter.length ){
stickyFooter.appendTo(event.target).css('top',0);
}
$.fixedToolbars.show(true, this);
});
});
// element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
// coordinates inside of the rect it returns don't have the page scroll position
// factored out of it like the other platforms do. To get around this,
// we'll just calculate the top offset the old fashioned way until core has
// a chance to figure out how to handle this situation.
//
// TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
function getOffsetTop(ele)
{
var top = 0;
if (ele)
{
var op = ele.offsetParent, body = document.body;
top = ele.offsetTop;
while (ele && ele != body)
{
top += ele.scrollTop || 0;
if (ele == op)
{
top += op.offsetTop;
op = ele.offsetParent;
}
ele = ele.parentNode;
}
}
return top;
}
function setTop(el){
var fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
thisCSStop = el.css('top') == 'auto' ? 0 : parseFloat(el.css('top')),
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
useRelative = el.parents('.ui-page:not(.ui-page-fullscreen)').length,
relval;
if( el.is('.ui-header-fixed') ){
relval = fromTop - thisTop + thisCSStop;
if( relval < thisTop){ relval = 0; }
return el.css('top', ( useRelative ) ? relval : fromTop);
}
else{
//relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
//if( relval > thisTop ){ relval = 0; }
relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop);
return el.css('top', ( useRelative ) ? relval : fromTop + screenHeight - thisHeight );
}
}
//exposed methods
return {
show: function(immediately, page){
currentstate = 'overlay';
var $ap = page ? $(page) : ($.mobile.activePage ? $.mobile.activePage : $(".ui-page-active"));
return $ap.children( toolbarSelector ).each(function(){
var el = $(this),
fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
alreadyVisible = (el.is('.ui-header-fixed') && fromTop <= thisTop + thisHeight) || (el.is('.ui-footer-fixed') && thisTop <= fromTop + screenHeight);
//add state class
el.addClass('ui-fixed-overlay').removeClass('ui-fixed-inline');
if( !alreadyVisible && !immediately ){
el.addClass('in').animationComplete(function(){
el.removeClass('in');
});
}
setTop(el);
});
},
hide: function(immediately){
currentstate = 'inline';
var $ap = $.mobile.activePage ? $.mobile.activePage : $(".ui-page-active");
return $ap.children( toolbarSelector ).each(function(){
var el = $(this);
var thisCSStop = el.css('top'); thisCSStop = thisCSStop == 'auto' ? 0 : parseFloat(thisCSStop);
//add state class
el.addClass('ui-fixed-inline').removeClass('ui-fixed-overlay');
if (thisCSStop < 0 || (el.is('.ui-header-fixed') && thisCSStop != 0))
{
if(immediately){
el.css('top',0);
}
else{
if( el.css('top') !== 'auto' && parseFloat(el.css('top')) !== 0 ){
var classes = 'out reverse';
el.addClass(classes).animationComplete(function(){
el.removeClass(classes);
el.css('top',0);
});
}
}
}
});
},
hideAfterDelay: function(){
delayTimer = setTimeout(function(){
$.fixedToolbars.hide();
}, 3000);
},
toggle: function(from){
if(from){ currentstate = from; }
return (currentstate == 'overlay') ? $.fixedToolbars.hide() : $.fixedToolbars.show();
}
};
})();
})(jQuery);