pushpop-rails 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +42 -0
- data/Rakefile +29 -0
- data/lib/generators/pushpop/install/install_generator.rb +17 -0
- data/lib/pushpop-rails.rb +4 -0
- data/lib/pushpop/rails.rb +8 -0
- data/lib/pushpop/rails/engine.rb +7 -0
- data/lib/pushpop/rails/version.rb +5 -0
- data/vendor/assets/Pushpop/background.png +0 -0
- data/vendor/assets/Pushpop/background@2x.png +0 -0
- data/vendor/assets/Pushpop/externals/scrollkit/background.png +0 -0
- data/vendor/assets/Pushpop/externals/scrollkit/scrollkit.css +202 -0
- data/vendor/assets/Pushpop/externals/scrollkit/scrollkit.js +924 -0
- data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.eot +0 -0
- data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.svg +57 -0
- data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.ttf +0 -0
- data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.woff +0 -0
- data/vendor/assets/Pushpop/pushpop-modal-view-stack/pushpop-modal-view-stack.css +148 -0
- data/vendor/assets/Pushpop/pushpop-modal-view-stack/pushpop-modal-view-stack.js +306 -0
- data/vendor/assets/Pushpop/pushpop-popover-view-stack/pushpop-popover-view-stack.css +170 -0
- data/vendor/assets/Pushpop/pushpop-popover-view-stack/pushpop-popover-view-stack.js +278 -0
- data/vendor/assets/Pushpop/pushpop-split-view/pushpop-split-view.css +38 -0
- data/vendor/assets/Pushpop/pushpop-split-view/pushpop-split-view.js +33 -0
- data/vendor/assets/Pushpop/pushpop-tab-view/pushpop-tab-view.css +130 -0
- data/vendor/assets/Pushpop/pushpop-tab-view/pushpop-tab-view.js +298 -0
- data/vendor/assets/Pushpop/pushpop-table-view/pushpop-table-view.css +1273 -0
- data/vendor/assets/Pushpop/pushpop-table-view/pushpop-table-view.js +2275 -0
- data/vendor/assets/Pushpop/pushpop.css +2243 -0
- data/vendor/assets/Pushpop/pushpop.js +1554 -0
- data/vendor/assets/javascripts/pushpop_rails.js +7 -0
- data/vendor/assets/stylesheets/pushpop_rails.css +9 -0
- metadata +92 -0
@@ -0,0 +1,1554 @@
|
|
1
|
+
;'use strict';
|
2
|
+
|
3
|
+
// Load custom build of Modernizr if an appropriate build has not been loaded.
|
4
|
+
if (!window['Modernizr'] || Modernizr.touch === undefined || Modernizr.csstransitions === undefined || Modernizr.csstransforms === undefined || Modernizr.csstransforms3d === undefined) {
|
5
|
+
|
6
|
+
/* Modernizr 2.5.2 (Custom Build) | MIT & BSD
|
7
|
+
* Build: http://www.modernizr.com/download/#-csstransforms-csstransforms3d-csstransitions-touch-cssclasses-teststyles-testprop-testallprops-prefixes-domprefixes
|
8
|
+
*/
|
9
|
+
;window.Modernizr=function(a,b,c){function z(a){j.cssText=a}function A(a,b){return z(m.join(a+";")+(b||""))}function B(a,b){return typeof a===b}function C(a,b){return!!~(""+a).indexOf(b)}function D(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function E(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:B(f,"function")?f.bind(d||b):f}return!1}function F(a,b,c){var d=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+o.join(d+" ")+d).split(" ");return B(b,"string")||B(b,"undefined")?D(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),E(e,b,c))}var d="2.5.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k=b.createElement("div"),l=b.body,m=l?l:b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),k.appendChild(j);return f=["­","<style>",a,"</style>"].join(""),k.id=h,m.innerHTML+=f,m.appendChild(k),l||g.appendChild(m),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},x={}.hasOwnProperty,y;!B(x,"undefined")&&!B(x.call,"undefined")?y=function(a,b){return x.call(a,b)}:y=function(a,b){return b in a&&B(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e});var G=function(c,d){var f=c.join(""),g=d.length;w(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch||(j.touch&&j.touch.offsetTop)===9,e.csstransforms3d=(j.csstransforms3d&&j.csstransforms3d.offsetLeft)===9&&j.csstransforms3d.offsetHeight===3},g,d)}([,["@media (",m.join("touch-enabled),("),h,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",m.join("transform-3d),("),h,")","{#csstransforms3d{left:9px;position:absolute;height:3px;}}"].join("")],[,"touch","csstransforms3d"]);q.touch=function(){return e.touch},q.csstransforms=function(){return!!F("transform")},q.csstransforms3d=function(){var a=!!F("perspective");return a&&"webkitPerspective"in g.style&&(a=e.csstransforms3d),a},q.csstransitions=function(){return F("transition")};for(var H in q)y(q,H)&&(v=H.toLowerCase(),e[v]=q[H](),t.push((e[v]?"":"no-")+v));return z(""),i=k=null,e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.testProp=function(a){return D([a])},e.testAllProps=F,e.testStyles=w,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document);
|
10
|
+
}
|
11
|
+
|
12
|
+
// Load spin.js v1.2.6 - fgnass.github.com/spin.js#v1.2.6
|
13
|
+
!function(e,t,n){function o(e,n){var r=t.createElement(e||"div"),i;for(i in n)r[i]=n[i];return r}function u(e){for(var t=1,n=arguments.length;t<n;t++)e.appendChild(arguments[t]);return e}function f(e,t,n,r){var o=["opacity",t,~~(e*100),n,r].join("-"),u=.01+n/r*100,f=Math.max(1-(1-e)/t*(100-u),e),l=s.substring(0,s.indexOf("Animation")).toLowerCase(),c=l&&"-"+l+"-"||"";return i[o]||(a.insertRule("@"+c+"keyframes "+o+"{"+"0%{opacity:"+f+"}"+u+"%{opacity:"+e+"}"+(u+.01)+"%{opacity:1}"+(u+t)%100+"%{opacity:"+e+"}"+"100%{opacity:"+f+"}"+"}",a.cssRules.length),i[o]=1),o}function l(e,t){var i=e.style,s,o;if(i[t]!==n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(o=0;o<r.length;o++){s=r[o]+t;if(i[s]!==n)return s}}function c(e,t){for(var n in t)e.style[l(e,n)||n]=t[n];return e}function h(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)e[i]===n&&(e[i]=r[i])}return e}function p(e){var t={x:e.offsetLeft,y:e.offsetTop};while(e=e.offsetParent)t.x+=e.offsetLeft,t.y+=e.offsetTop;return t}var r=["webkit","Moz","ms","O"],i={},s,a=function(){var e=o("style",{type:"text/css"});return u(t.getElementsByTagName("head")[0],e),e.sheet||e.styleSheet}(),d={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"auto",left:"auto"},v=function m(e){if(!this.spin)return new m(e);this.opts=h(e||{},m.defaults,d)};v.defaults={},h(v.prototype,{spin:function(e){this.stop();var t=this,n=t.opts,r=t.el=c(o(0,{className:n.className}),{position:"relative",width:0,zIndex:n.zIndex}),i=n.radius+n.length+n.width,u,a;e&&(e.insertBefore(r,e.firstChild||null),a=p(e),u=p(r),c(r,{left:(n.left=="auto"?a.x-u.x+(e.offsetWidth>>1):parseInt(n.left,10)+i)+"px",top:(n.top=="auto"?a.y-u.y+(e.offsetHeight>>1):parseInt(n.top,10)+i)+"px"})),r.setAttribute("aria-role","progressbar"),t.lines(r,t.opts);if(!s){var f=0,l=n.fps,h=l/n.speed,d=(1-n.opacity)/(h*n.trail/100),v=h/n.lines;(function m(){f++;for(var e=n.lines;e;e--){var i=Math.max(1-(f+e*v)%h*d,n.opacity);t.opacity(r,n.lines-e,i,n)}t.timeout=t.el&&setTimeout(m,~~(1e3/l))})()}return t},stop:function(){var e=this.el;return e&&(clearTimeout(this.timeout),e.parentNode&&e.parentNode.removeChild(e),this.el=n),this},lines:function(e,t){function i(e,r){return c(o(),{position:"absolute",width:t.length+t.width+"px",height:t.width+"px",background:e,boxShadow:r,transformOrigin:"left",transform:"rotate("+~~(360/t.lines*n+t.rotate)+"deg) translate("+t.radius+"px"+",0)",borderRadius:(t.corners*t.width>>1)+"px"})}var n=0,r;for(;n<t.lines;n++)r=c(o(),{position:"absolute",top:1+~(t.width/2)+"px",transform:t.hwaccel?"translate3d(0,0,0)":"",opacity:t.opacity,animation:s&&f(t.opacity,t.trail,n,t.lines)+" "+1/t.speed+"s linear infinite"}),t.shadow&&u(r,c(i("#000","0 0 4px #000"),{top:"2px"})),u(e,u(r,i(t.color,"0 0 1px rgba(0,0,0,.1)")));return e},opacity:function(e,t,n){t<e.childNodes.length&&(e.childNodes[t].style.opacity=n)}}),function(){function e(e,t){return o("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',t)}var t=c(o("group"),{behavior:"url(#default#VML)"});!l(t,"transform")&&t.adj?(a.addRule(".spin-vml","behavior:url(#default#VML)"),v.prototype.lines=function(t,n){function s(){return c(e("group",{coordsize:i+" "+i,coordorigin:-r+" "+ -r}),{width:i,height:i})}function l(t,i,o){u(a,u(c(s(),{rotation:360/n.lines*t+"deg",left:~~i}),u(c(e("roundrect",{arcsize:n.corners}),{width:r,height:n.width,left:n.radius,top:-n.width>>1,filter:o}),e("fill",{color:n.color,opacity:n.opacity}),e("stroke",{opacity:0}))))}var r=n.length+n.width,i=2*r,o=-(n.width+n.length)*2+"px",a=c(s(),{position:"absolute",top:o,left:o}),f;if(n.shadow)for(f=1;f<=n.lines;f++)l(f,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(f=1;f<=n.lines;f++)l(f);return u(t,a)},v.prototype.opacity=function(e,t,n,r){var i=e.firstChild;r=r.shadow&&r.lines||0,i&&t+r<i.childNodes.length&&(i=i.childNodes[t+r],i=i&&i.firstChild,i=i&&i.firstChild,i&&(i.opacity=n))}):s=l(t,"animation")}(),typeof define=="function"&&define.amd?define(function(){return v}):e.Spinner=v}(window,document);
|
14
|
+
|
15
|
+
/**
|
16
|
+
The base Pushpop object.
|
17
|
+
*/
|
18
|
+
var Pushpop = window['Pushpop'] || {};
|
19
|
+
|
20
|
+
/**
|
21
|
+
Event types for Pushpop.
|
22
|
+
*/
|
23
|
+
Pushpop.EventType = {
|
24
|
+
WillPushView: 'Pushpop:WillPushView',
|
25
|
+
DidPushView: 'Pushpop:DidPushView',
|
26
|
+
WillPopView: 'Pushpop:WillPopView',
|
27
|
+
DidPopView: 'Pushpop:DidPopView',
|
28
|
+
WillDismissView: 'Pushpop:WillDismissView',
|
29
|
+
DidDismissView: 'Pushpop:DidDismissView',
|
30
|
+
WillPresentView: 'Pushpop:WillPresentView',
|
31
|
+
DidPresentView: 'Pushpop:DidPresentView'
|
32
|
+
};
|
33
|
+
|
34
|
+
/**
|
35
|
+
Default transition to use for pushing and popping views when no transition is specified.
|
36
|
+
*/
|
37
|
+
Pushpop.defaultTransition = 'slide-horizontal';
|
38
|
+
|
39
|
+
/**
|
40
|
+
|
41
|
+
*/
|
42
|
+
Pushpop.Util = {
|
43
|
+
_dashedToCamelCaseRegExp: /(\-[a-z])/g,
|
44
|
+
_dashedToCamelCaseReplacer: function($1) { return $1.toUpperCase().replace('-', ''); },
|
45
|
+
convertDashedStringToCamelCase: function(dashedString) {
|
46
|
+
return ('' + dashedString).replace(this._dashedToCamelCaseRegExp, this._dashedToCamelCaseReplacer);
|
47
|
+
},
|
48
|
+
|
49
|
+
_camelCaseToDashedRegExp: /([A-Z])/g,
|
50
|
+
_camelCaseToDashedReplacer: function($1) { return '-' + $1.toLowerCase(); },
|
51
|
+
convertCamelCaseStringToDashed: function(camelCaseString) {
|
52
|
+
var dashedString = ('' + camelCaseString).replace(this._camelCaseToDashedRegExp, this._camelCaseToDashedReplacer);
|
53
|
+
if (dashedString.length > 0 && dashedString.charAt(0) === '-') dashedString = dashedString.slice(1);
|
54
|
+
return dashedString;
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
/**
|
59
|
+
Traverses the parents of the specified element and returns the closest Pushpop.ViewStack.
|
60
|
+
@param {HTMLElement} element The element for which to find the closest parent view stack.
|
61
|
+
@type Pushpop.ViewStack
|
62
|
+
*/
|
63
|
+
Pushpop.getViewStackForElement = function(element) {
|
64
|
+
var $parents = $(element).parents();
|
65
|
+
var viewStack;
|
66
|
+
for (var i = 0, length = $parents.length; i < length; i++) if (!!(viewStack = $parents[i].viewStack) && viewStack instanceof Pushpop.ViewStack) return viewStack;
|
67
|
+
return null;
|
68
|
+
};
|
69
|
+
|
70
|
+
/**
|
71
|
+
Creates a new ViewStack.
|
72
|
+
@param {HTMLDivElement} element The DIV element to initialize as a new ViewStack.
|
73
|
+
@constructor
|
74
|
+
*/
|
75
|
+
Pushpop.ViewStack = function ViewStack(element) {
|
76
|
+
if (!element) return;
|
77
|
+
|
78
|
+
var $element = this.$element = $(element);
|
79
|
+
element = this.element = $element[0];
|
80
|
+
|
81
|
+
var viewStack = element.viewStack;
|
82
|
+
if (viewStack) return viewStack;
|
83
|
+
|
84
|
+
var self = element.viewStack = this;
|
85
|
+
|
86
|
+
var views = this.views = [];
|
87
|
+
|
88
|
+
var $rootViewElement = $element.children('.pp-view, .pp-split-view').first().addClass('pp-active');
|
89
|
+
var rootViewElement = $rootViewElement[0];
|
90
|
+
|
91
|
+
if (!rootViewElement) return;
|
92
|
+
|
93
|
+
var rootView = this.rootView = rootViewElement.view || new Pushpop.View($rootViewElement.addClass('root'));
|
94
|
+
views.push(rootView);
|
95
|
+
|
96
|
+
var rootScrollView = rootView.getScrollView();
|
97
|
+
if (rootScrollView) rootScrollView.recalculateDimensions();
|
98
|
+
};
|
99
|
+
|
100
|
+
Pushpop.ViewStack.prototype = {
|
101
|
+
constructor: Pushpop.ViewStack,
|
102
|
+
|
103
|
+
element: null,
|
104
|
+
$element: null,
|
105
|
+
|
106
|
+
views: null,
|
107
|
+
rootView: null,
|
108
|
+
isTransitioning: false,
|
109
|
+
handleEvent: function(evt) {
|
110
|
+
switch (evt.type) {
|
111
|
+
case 'webkitTransitionEnd':
|
112
|
+
case 'transitionend':
|
113
|
+
case 'oTransitionEnd':
|
114
|
+
case 'transitionEnd':
|
115
|
+
var target = evt.target;
|
116
|
+
if (!target.view || $(target).hasClass('pp-active')) return;
|
117
|
+
|
118
|
+
var newActiveView = target;
|
119
|
+
newActiveView = (newActiveView) ? newActiveView.view : null;
|
120
|
+
if (!newActiveView) return;
|
121
|
+
|
122
|
+
var viewStack = newActiveView.getViewStack();
|
123
|
+
if (!viewStack || !viewStack.isTransitioning) return;
|
124
|
+
|
125
|
+
var oldActiveView = viewStack.$element.children('.pp-view.pp-active')[0];
|
126
|
+
oldActiveView = (oldActiveView) ? oldActiveView.view : null;
|
127
|
+
if (!oldActiveView) return;
|
128
|
+
|
129
|
+
viewStack.isTransitioning = false;
|
130
|
+
$(target).unbind(evt);
|
131
|
+
|
132
|
+
var $newActiveViewElement = newActiveView.$element;
|
133
|
+
var $oldActiveViewElement = oldActiveView.$element;
|
134
|
+
var action = ($newActiveViewElement.hasClass('push') ? 'push' : 'pop');
|
135
|
+
|
136
|
+
$newActiveViewElement.addClass('pp-active');
|
137
|
+
$newActiveViewElement.removeClass('transition push pop ' + newActiveView.transition);
|
138
|
+
$oldActiveViewElement.removeClass('pp-active transition push pop ' + newActiveView.transition);
|
139
|
+
|
140
|
+
if (action === 'push') {
|
141
|
+
|
142
|
+
// Trigger an event indicating that the new view was pushed.
|
143
|
+
$newActiveViewElement.trigger($.Event(Pushpop.EventType.DidPushView, {
|
144
|
+
view: newActiveView
|
145
|
+
}));
|
146
|
+
} else {
|
147
|
+
|
148
|
+
// Trigger an event indicating that the previous view was popped.
|
149
|
+
$oldActiveViewElement.trigger($.Event(Pushpop.EventType.DidPopView, {
|
150
|
+
view: oldActiveView
|
151
|
+
}));
|
152
|
+
}
|
153
|
+
|
154
|
+
// Trigger an event indicating that the previous view was dismissed.
|
155
|
+
$oldActiveViewElement.trigger($.Event(Pushpop.EventType.DidDismissView, {
|
156
|
+
view: oldActiveView,
|
157
|
+
action: action
|
158
|
+
}));
|
159
|
+
|
160
|
+
// Trigger an event for each active child view of the previous view indicating
|
161
|
+
// that their parent was dismissed.
|
162
|
+
$oldActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
163
|
+
var viewStack = element.viewStack;
|
164
|
+
var activeView = viewStack.getActiveView();
|
165
|
+
if (!activeView) return;
|
166
|
+
|
167
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.DidDismissView, {
|
168
|
+
view: activeView,
|
169
|
+
action: 'parent-' + action
|
170
|
+
}));
|
171
|
+
});
|
172
|
+
|
173
|
+
// Trigger an event indicating that the new view was presented.
|
174
|
+
$newActiveViewElement.trigger($.Event(Pushpop.EventType.DidPresentView, {
|
175
|
+
view: newActiveView,
|
176
|
+
action: action
|
177
|
+
}));
|
178
|
+
|
179
|
+
// Trigger an event for each active child view of the new view indicating
|
180
|
+
// that their parent was presented.
|
181
|
+
$newActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
182
|
+
var viewStack = element.viewStack;
|
183
|
+
var activeView = viewStack.getActiveView();
|
184
|
+
if (!activeView) return;
|
185
|
+
|
186
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.DidPresentView, {
|
187
|
+
view: activeView,
|
188
|
+
action: 'parent-' + action
|
189
|
+
}));
|
190
|
+
});
|
191
|
+
|
192
|
+
// Hack to reset the scroll view position for views using ScrollKit on desktop browsers.
|
193
|
+
var newActiveScrollView, newActiveScrollPosition;
|
194
|
+
if ($newActiveViewElement.hasClass('sk-no-touch') && (newActiveScrollView = newActiveView.getScrollView())) {
|
195
|
+
newActiveScrollPosition = newActiveScrollView.getScrollPosition();
|
196
|
+
newActiveScrollView.setScrollPosition(newActiveScrollPosition.x - 1, newActiveScrollPosition.y - 1);
|
197
|
+
newActiveScrollView.setScrollPosition(newActiveScrollPosition.x + 1, newActiveScrollPosition.y + 1);
|
198
|
+
}
|
199
|
+
|
200
|
+
// Remove the previous active view from the DOM if it is marked for removal when popped.
|
201
|
+
if (action === 'pop' && oldActiveView.getShouldRemoveWhenPopped()) $oldActiveViewElement.remove();
|
202
|
+
|
203
|
+
// Call the callback to execute when a push/pop transition completes if one exists.
|
204
|
+
if (!viewStack.callback) return;
|
205
|
+
viewStack.callback();
|
206
|
+
viewStack.callback = null;
|
207
|
+
break;
|
208
|
+
default:
|
209
|
+
break;
|
210
|
+
}
|
211
|
+
},
|
212
|
+
|
213
|
+
/**
|
214
|
+
Pushes the specified view to the view stack using the optionally specified transition. If a
|
215
|
+
transition is not specified, the default will be used. A callback may optionally be provided
|
216
|
+
to be called after the transition completes.
|
217
|
+
@param {Pushpop.View} view The view to be pushed to the view stack.
|
218
|
+
@param {String|Function} [transitionOrCallback] Either the name of the transition to use when
|
219
|
+
pushing the view or a callback function to be executed upon completion of the default transition.
|
220
|
+
If this parameter is omitted, the default transition is used.
|
221
|
+
@param {Function} [callback] A callback function to be executed upon completion of the specified
|
222
|
+
transition.
|
223
|
+
*/
|
224
|
+
push: function(view, transitionOrCallback, callback) {
|
225
|
+
var oldActiveView = this.getActiveView();
|
226
|
+
var newActiveView = view;
|
227
|
+
|
228
|
+
if (newActiveView === oldActiveView) return;
|
229
|
+
|
230
|
+
if (this.isTransitioning) {
|
231
|
+
// Manually kick off transitionEnd event. Chances are it should have fired, but didn't.
|
232
|
+
if (!oldActiveView) return;
|
233
|
+
|
234
|
+
oldActiveView.$element.trigger('transitionend');
|
235
|
+
}
|
236
|
+
|
237
|
+
this.isTransitioning = true;
|
238
|
+
|
239
|
+
// Hide the onscreen keyboard if it is currently visible.
|
240
|
+
$(document.activeElement).blur();
|
241
|
+
|
242
|
+
var $element = this.$element;
|
243
|
+
|
244
|
+
var $oldActiveViewElement = oldActiveView.$element;
|
245
|
+
var $newActiveViewElement = newActiveView.$element;
|
246
|
+
|
247
|
+
this.views.push(newActiveView);
|
248
|
+
|
249
|
+
$newActiveViewElement.bind('webkitTransitionEnd transitionend oTransitionEnd transitionEnd', this.handleEvent);
|
250
|
+
|
251
|
+
var transition;
|
252
|
+
if (transitionOrCallback) {
|
253
|
+
if (typeof transitionOrCallback === 'function') {
|
254
|
+
this.callback = transitionOrCallback;
|
255
|
+
} else {
|
256
|
+
transition = transitionOrCallback;
|
257
|
+
this.callback = callback;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
transition = newActiveView.transition = transition || newActiveView.transition || Pushpop.defaultTransition;
|
262
|
+
|
263
|
+
$oldActiveViewElement.addClass('push ' + transition);
|
264
|
+
$newActiveViewElement.addClass('push ' + transition);
|
265
|
+
|
266
|
+
oldActiveView.forceReflow();
|
267
|
+
newActiveView.forceReflow();
|
268
|
+
|
269
|
+
var newActiveScrollView = newActiveView.getScrollView();
|
270
|
+
if (newActiveScrollView) newActiveScrollView.recalculateDimensions();
|
271
|
+
|
272
|
+
// Trigger an event indicating that the new view is about to be pushed.
|
273
|
+
$newActiveViewElement.trigger($.Event(Pushpop.EventType.WillPushView, {
|
274
|
+
view: newActiveView
|
275
|
+
}));
|
276
|
+
|
277
|
+
// Trigger an event indicating that the previous view is about to be dismissed.
|
278
|
+
$oldActiveViewElement.trigger($.Event(Pushpop.EventType.WillDismissView, {
|
279
|
+
view: oldActiveView,
|
280
|
+
action: 'push'
|
281
|
+
}));
|
282
|
+
|
283
|
+
// Trigger an event for each active child view of the previous view indicating that
|
284
|
+
// their parent is about to be dismissed.
|
285
|
+
$oldActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
286
|
+
var viewStack = element.viewStack;
|
287
|
+
var activeView = viewStack.getActiveView();
|
288
|
+
if (!activeView) return;
|
289
|
+
|
290
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.WillDismissView, {
|
291
|
+
view: activeView,
|
292
|
+
action: 'parent-push'
|
293
|
+
}));
|
294
|
+
});
|
295
|
+
|
296
|
+
// Trigger an event indicating that the new view is about to be presented.
|
297
|
+
$newActiveViewElement.trigger($.Event(Pushpop.EventType.WillPresentView, {
|
298
|
+
view: newActiveView,
|
299
|
+
action: 'push'
|
300
|
+
}));
|
301
|
+
|
302
|
+
// Trigger an event for each active child view of the new view indicating that
|
303
|
+
// their parent is about to be presented.
|
304
|
+
$newActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
305
|
+
var viewStack = element.viewStack;
|
306
|
+
var activeView = viewStack.getActiveView();
|
307
|
+
if (!activeView) return;
|
308
|
+
|
309
|
+
var activeScrollView = activeView.getScrollView();
|
310
|
+
if (activeScrollView) activeScrollView.recalculateDimensions();
|
311
|
+
|
312
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.WillPresentView, {
|
313
|
+
view: activeView,
|
314
|
+
action: 'parent-push'
|
315
|
+
}));
|
316
|
+
});
|
317
|
+
|
318
|
+
$oldActiveViewElement.addClass('transition');
|
319
|
+
$newActiveViewElement.addClass('transition');
|
320
|
+
|
321
|
+
// If the browser doesn't support css transitions, explicitely trigger the transitionend event
|
322
|
+
if (!Modernizr.csstransitions) $newActiveViewElement.trigger('transitionend');
|
323
|
+
},
|
324
|
+
|
325
|
+
/**
|
326
|
+
Pops the current or specified view off the view stack using the optionally specified transition.
|
327
|
+
If a view is not specified, the current view will be popped (unless it is the root view). If a
|
328
|
+
transition is not specified, the default will be used. A callback may optionally be provided to
|
329
|
+
be called after the transition completes.
|
330
|
+
@param {Pushpop.View|String} viewOrTransition Either the view to be popped to on the view stack
|
331
|
+
or the name of the transition to use when popping the view. If this parameter is omitted or if
|
332
|
+
it specifies a transition name, the current view will be assumed to be popped.
|
333
|
+
@param {String|Function} [transitionOrCallback] Either the name of the transition to use when
|
334
|
+
popping the view or a callback function to be executed upon completion of the default transition.
|
335
|
+
If this parameter is omitted, the default transition is used.
|
336
|
+
@param {Function} [callback] A callback function to be executed upon completion of the specified
|
337
|
+
transition.
|
338
|
+
*/
|
339
|
+
pop: function(viewOrTransition, transitionOrCallback, callback) {
|
340
|
+
var oldActiveView = this.getActiveView();
|
341
|
+
var newActiveView, transition;
|
342
|
+
|
343
|
+
if (this.isTransitioning) {
|
344
|
+
// Manually kick off transitionEnd event. Chances are it should have fired, but didn't.
|
345
|
+
if (!oldActiveView) return;
|
346
|
+
|
347
|
+
oldActiveView.$element.trigger('transitionend');
|
348
|
+
}
|
349
|
+
|
350
|
+
this.isTransitioning = true;
|
351
|
+
|
352
|
+
// Hide the onscreen keyboard if it is currently visible.
|
353
|
+
$(document.activeElement).blur();
|
354
|
+
|
355
|
+
var views = this.views;
|
356
|
+
if (views.length <= 1) return;
|
357
|
+
|
358
|
+
if (viewOrTransition && typeof viewOrTransition !== 'string') {
|
359
|
+
newActiveView = viewOrTransition;
|
360
|
+
|
361
|
+
if (newActiveView === oldActiveView) return;
|
362
|
+
|
363
|
+
if (this.containsView(newActiveView)) {
|
364
|
+
while (views.length > 1 && this.getActiveView() !== newActiveView) {
|
365
|
+
views.pop();
|
366
|
+
}
|
367
|
+
}
|
368
|
+
} else {
|
369
|
+
if (viewOrTransition) transition = viewOrTransition;
|
370
|
+
|
371
|
+
views.pop();
|
372
|
+
newActiveView = this.getActiveView();
|
373
|
+
}
|
374
|
+
|
375
|
+
if (transitionOrCallback) {
|
376
|
+
if (typeof transitionOrCallback === 'function') {
|
377
|
+
this.callback = transitionOrCallback;
|
378
|
+
} else {
|
379
|
+
transition = transitionOrCallback;
|
380
|
+
this.callback = callback;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
|
384
|
+
var $element = this.$element;
|
385
|
+
|
386
|
+
var $oldActiveViewElement = oldActiveView.$element;
|
387
|
+
var $newActiveViewElement = newActiveView.$element;
|
388
|
+
|
389
|
+
$newActiveViewElement.bind('webkitTransitionEnd transitionend oTransitionEnd transitionEnd', this.handleEvent);
|
390
|
+
|
391
|
+
transition = newActiveView.transition = transition || oldActiveView.transition || Pushpop.defaultTransition;
|
392
|
+
|
393
|
+
$oldActiveViewElement.addClass('pop ' + transition);
|
394
|
+
$newActiveViewElement.addClass('pop ' + transition);
|
395
|
+
|
396
|
+
oldActiveView.forceReflow();
|
397
|
+
newActiveView.forceReflow();
|
398
|
+
|
399
|
+
var newActiveScrollView = newActiveView.getScrollView();
|
400
|
+
if (newActiveScrollView) newActiveScrollView.recalculateDimensions();
|
401
|
+
|
402
|
+
// Trigger an event indicating that the previous view is about to be popped.
|
403
|
+
$oldActiveViewElement.trigger($.Event(Pushpop.EventType.WillPopView, {
|
404
|
+
view: oldActiveView
|
405
|
+
}));
|
406
|
+
|
407
|
+
// Trigger an event indicating that the previous view is about to be dismissed.
|
408
|
+
$oldActiveViewElement.trigger($.Event(Pushpop.EventType.WillDismissView, {
|
409
|
+
view: oldActiveView,
|
410
|
+
action: 'pop'
|
411
|
+
}));
|
412
|
+
|
413
|
+
// Trigger an event for each active child view of the previous view indicating that
|
414
|
+
// their parent is about to be dismissed.
|
415
|
+
$oldActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
416
|
+
var viewStack = element.viewStack;
|
417
|
+
var activeView = viewStack.getActiveView();
|
418
|
+
if (!activeView) return;
|
419
|
+
|
420
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.WillDismissView, {
|
421
|
+
view: activeView,
|
422
|
+
action: 'parent-pop'
|
423
|
+
}));
|
424
|
+
});
|
425
|
+
|
426
|
+
// Trigger an event indicating that the new view is about to be presented.
|
427
|
+
$newActiveViewElement.trigger($.Event(Pushpop.EventType.WillPresentView, {
|
428
|
+
view: newActiveView,
|
429
|
+
action: 'pop'
|
430
|
+
}));
|
431
|
+
|
432
|
+
// Trigger an event for each active child view of the new view indicating that
|
433
|
+
// their parent is about to be presented.
|
434
|
+
$newActiveViewElement.find('.pp-view-stack').each(function(index, element) {
|
435
|
+
var viewStack = element.viewStack;
|
436
|
+
var activeView = viewStack.getActiveView();
|
437
|
+
if (!activeView) return;
|
438
|
+
|
439
|
+
var activeScrollView = activeView.getScrollView();
|
440
|
+
if (activeScrollView) activeScrollView.recalculateDimensions();
|
441
|
+
|
442
|
+
activeView.$element.trigger($.Event(Pushpop.EventType.WillPresentView, {
|
443
|
+
view: activeView,
|
444
|
+
action: 'parent-pop'
|
445
|
+
}));
|
446
|
+
});
|
447
|
+
|
448
|
+
$oldActiveViewElement.addClass('transition');
|
449
|
+
$newActiveViewElement.addClass('transition');
|
450
|
+
|
451
|
+
// If the browser doesn't support css transitions, explicitely trigger the transitionend event
|
452
|
+
if (!Modernizr.csstransitions) $newActiveViewElement.trigger('transitionend');
|
453
|
+
},
|
454
|
+
|
455
|
+
/**
|
456
|
+
Returns the current active (topmost) Pushpop.View on this view stack.
|
457
|
+
@type Pushpop.View
|
458
|
+
*/
|
459
|
+
getActiveView: function() {
|
460
|
+
var views = this.views;
|
461
|
+
var viewCount = views.length;
|
462
|
+
|
463
|
+
return (viewCount === 0) ? null : views[viewCount - 1];
|
464
|
+
},
|
465
|
+
|
466
|
+
/**
|
467
|
+
Returns a flag indicating if the specified Pushpop.View is contained wihin this
|
468
|
+
view stack.
|
469
|
+
@param {Pushpop.View} view The view to search for in this view stack.
|
470
|
+
@type Boolean
|
471
|
+
*/
|
472
|
+
containsView: function(view) {
|
473
|
+
var views = this.views;
|
474
|
+
var viewCount = views.length;
|
475
|
+
|
476
|
+
for (var i = viewCount - 1; i >= 0; i--) if (views[i] === view) return true;
|
477
|
+
return false;
|
478
|
+
},
|
479
|
+
|
480
|
+
/**
|
481
|
+
Creates a new view and pushes it to the view stack using the optionally specified transition.
|
482
|
+
Before pushing the view, a required callback is called that passes in the newly-created view to
|
483
|
+
give an opportunity to set up the view's content. If a transition is not specified, the default
|
484
|
+
will be used. A callback may optionally be provided to be called after the transition completes.
|
485
|
+
@description NOTE: By default, the newly-created view is marked for removal from the DOM when it
|
486
|
+
is popped.
|
487
|
+
@param {Function} beforePushCallback A callback function to be executed before the newly-created
|
488
|
+
view is pushed. The function is called with a single parameter passing the view to be pushed.
|
489
|
+
@param {String|Function} [transitionOrCallback] Either the name of the transition to use when
|
490
|
+
pushing the view or a callback function to be executed upon completion of the default transition.
|
491
|
+
If this parameter is omitted, the default transition is used.
|
492
|
+
@param {Function} [callback] A callback function to be executed upon completion of the specified
|
493
|
+
transition.
|
494
|
+
*/
|
495
|
+
pushNewView: function(beforePushCallback, transitionOrCallback, callback) {
|
496
|
+
var $viewElement = $('<div class="pp-view"/>').appendTo(this.$element);
|
497
|
+
var view = new Pushpop.View($viewElement);
|
498
|
+
|
499
|
+
view.setShouldRemoveWhenPopped(true);
|
500
|
+
|
501
|
+
if (beforePushCallback && typeof beforePushCallback === 'function') beforePushCallback(view);
|
502
|
+
|
503
|
+
this.push(view, transitionOrCallback, callback);
|
504
|
+
},
|
505
|
+
|
506
|
+
/**
|
507
|
+
Creates a new view with a new table view and pushes it to the view stack using the optionally
|
508
|
+
specified transition. Before pushing the view, a required callback is called that passes in the
|
509
|
+
newly-created table view to give an opportunity to set up the table view's data source and other
|
510
|
+
properties. If a transition is not specified, the default will be used. A callback may optionally
|
511
|
+
be provided to be called after the transition completes.
|
512
|
+
@description NOTE: By default, the newly-created view containing the newly-created table view is
|
513
|
+
marked for removal from the DOM when it is popped.
|
514
|
+
@param {Function} beforePushCallback A callback function to be executed before the newly-created
|
515
|
+
view is pushed. The function is called with a single parameter passing the newly-created table
|
516
|
+
view contained in the view to be pushed.
|
517
|
+
@param {String|Function} [transitionOrCallback] Either the name of the transition to use when
|
518
|
+
pushing the view or a callback function to be executed upon completion of the default transition.
|
519
|
+
If this parameter is omitted, the default transition is used.
|
520
|
+
@param {Function} [callback] A callback function to be executed upon completion of the specified
|
521
|
+
transition.
|
522
|
+
*/
|
523
|
+
pushNewTableView: function(beforePushCallback, transitionOrCallback, callback) {
|
524
|
+
var $viewElement = $('<div class="pp-view sk-scroll-view" data-always-bounce-vertical="true"/>').appendTo(this.$element);
|
525
|
+
var $tableViewElement = $('<ul class="pp-table-view"/>').appendTo($viewElement);
|
526
|
+
var view = new Pushpop.View($viewElement);
|
527
|
+
var scrollView = new ScrollKit.ScrollView($viewElement);
|
528
|
+
var tableView = new Pushpop.TableView($tableViewElement);
|
529
|
+
|
530
|
+
view.setShouldRemoveWhenPopped(true);
|
531
|
+
|
532
|
+
if (beforePushCallback && typeof beforePushCallback === 'function') beforePushCallback(tableView);
|
533
|
+
|
534
|
+
this.push(view, transitionOrCallback, callback);
|
535
|
+
},
|
536
|
+
|
537
|
+
/**
|
538
|
+
Convenience accessor for jQuery's .bind() method.
|
539
|
+
*/
|
540
|
+
$bind: function() { this.$element.bind.apply(this.$element, arguments); },
|
541
|
+
|
542
|
+
/**
|
543
|
+
Convenience accessor for jQuery's .unbind() method.
|
544
|
+
*/
|
545
|
+
$unbind: function() { this.$element.unbind.apply(this.$element, arguments); },
|
546
|
+
|
547
|
+
/**
|
548
|
+
Convenience accessor for jQuery's .delegate() method.
|
549
|
+
*/
|
550
|
+
$delegate: function() { this.$element.delegate.apply(this.$element, arguments); },
|
551
|
+
|
552
|
+
/**
|
553
|
+
Convenience accessor for jQuery's .undelegate() method.
|
554
|
+
*/
|
555
|
+
$undelegate: function() { this.$element.undelegate.apply(this.$element, arguments); },
|
556
|
+
|
557
|
+
/**
|
558
|
+
Convenience accessor for jQuery's .trigger() method.
|
559
|
+
*/
|
560
|
+
$trigger: function() { this.$element.trigger.apply(this.$element, arguments); }
|
561
|
+
};
|
562
|
+
|
563
|
+
/**
|
564
|
+
Creates a new View.
|
565
|
+
@param {HTMLDivElement} element The DIV element to initialize as a new View.
|
566
|
+
@constructor
|
567
|
+
*/
|
568
|
+
Pushpop.View = function View(element) {
|
569
|
+
if (!element) return;
|
570
|
+
|
571
|
+
var $element = this.$element = $(element);
|
572
|
+
element = this.element = $element[0];
|
573
|
+
|
574
|
+
var view = element.view;
|
575
|
+
if (view) return view;
|
576
|
+
|
577
|
+
var self = element.view = this;
|
578
|
+
|
579
|
+
this.title = $element.attr('data-view-title');
|
580
|
+
|
581
|
+
// Set up this view's navigation bar button items.
|
582
|
+
var barButtonItems = this._barButtonItems = [];
|
583
|
+
$element.find('.pp-navigation-bar-button-items > .pp-button').each(function(index, element) {
|
584
|
+
self.addBarButtonItem(element.button || new Pushpop.Button(element));
|
585
|
+
});
|
586
|
+
|
587
|
+
$element.find('.pp-navigation-bar-button-items').remove();
|
588
|
+
|
589
|
+
// Determine if the back bar button item should be hidden when this view is active.
|
590
|
+
var hideBackBarButtonItem = $element.attr('data-hide-back-bar-button-item') || 'false';
|
591
|
+
hideBackBarButtonItem = hideBackBarButtonItem !== 'false';
|
592
|
+
this.setHideBackBarButtonItem(hideBackBarButtonItem);
|
593
|
+
|
594
|
+
//////
|
595
|
+
// OLD
|
596
|
+
var $navbarButtonsContainer = $element.find('.pp-navigationbar-buttons');
|
597
|
+
var $navbarButtons = this.$navbarButtons = $navbarButtonsContainer.find('.pp-barbutton');
|
598
|
+
|
599
|
+
// Hide the back button if data-hide-back-button="true" or if there is a left navigation bar
|
600
|
+
var dataHideBackButton = $navbarButtonsContainer.attr('data-hide-back-button');
|
601
|
+
this.hideNavBackButton = ((dataHideBackButton && dataHideBackButton !== 'false') || $navbarButtons.filter('.pp-barbutton-align-left').length > 0);
|
602
|
+
// OLD
|
603
|
+
//////
|
604
|
+
};
|
605
|
+
|
606
|
+
Pushpop.View.prototype = {
|
607
|
+
constructor: Pushpop.View,
|
608
|
+
|
609
|
+
element: null,
|
610
|
+
$element: null,
|
611
|
+
|
612
|
+
$navbarButtons: null, // OLD
|
613
|
+
hideNavBackButton: false, // OLD
|
614
|
+
|
615
|
+
transition: null,
|
616
|
+
|
617
|
+
/**
|
618
|
+
Sets the transition that should be used when pushing or popping to this view.
|
619
|
+
@param {String} value The name of the transition to be used when pushing or popping
|
620
|
+
to this view.
|
621
|
+
*/
|
622
|
+
setTransition: function(value) {
|
623
|
+
this.transition = value;
|
624
|
+
this.$element.addClass(value);
|
625
|
+
},
|
626
|
+
|
627
|
+
title: null,
|
628
|
+
|
629
|
+
getTitle: function() { return this.title; },
|
630
|
+
|
631
|
+
/**
|
632
|
+
Sets the title for this view.
|
633
|
+
@description The title is stored in the |data-view-title| attribute of this view's
|
634
|
+
element. NOTE: When a Pushpop.NavigationBar is used in conjunction with this view's
|
635
|
+
view stack, this view's title will appear in the navigation bar when it is the active
|
636
|
+
view.
|
637
|
+
@param {String} value The title for this view.
|
638
|
+
*/
|
639
|
+
setTitle: function(value) {
|
640
|
+
this.title = value;
|
641
|
+
this.$element.attr('data-view-title', value);
|
642
|
+
|
643
|
+
var navigationBar = this.getNavigationBar();
|
644
|
+
if (navigationBar) navigationBar.setTitle(value);
|
645
|
+
},
|
646
|
+
|
647
|
+
/**
|
648
|
+
|
649
|
+
*/
|
650
|
+
getActive: function() { return this.$element.hasClass('pp-active'); },
|
651
|
+
|
652
|
+
_barButtonItems: null,
|
653
|
+
|
654
|
+
/**
|
655
|
+
|
656
|
+
*/
|
657
|
+
getBarButtonItems: function() { return this._barButtonItems; },
|
658
|
+
|
659
|
+
/**
|
660
|
+
|
661
|
+
*/
|
662
|
+
setBarButtonItems: function(barButtonItems, animated) {
|
663
|
+
this.removeAllBarButtonItems();
|
664
|
+
for (var i = 0, length = barButtonItems.length; i < length; i++) this.addBarButtonItem(barButtonItems[i], animated);
|
665
|
+
},
|
666
|
+
|
667
|
+
/**
|
668
|
+
|
669
|
+
*/
|
670
|
+
addBarButtonItem: function(barButtonItem, animated) {
|
671
|
+
if (this.getActive()) {
|
672
|
+
var navigationBar = this.getNavigationBar();
|
673
|
+
if (navigationBar) navigationBar.addBarButtonItem(barButtonItem, animated);
|
674
|
+
} else {
|
675
|
+
barButtonItem.remove();
|
676
|
+
}
|
677
|
+
|
678
|
+
this._barButtonItems.push(barButtonItem);
|
679
|
+
},
|
680
|
+
|
681
|
+
/**
|
682
|
+
|
683
|
+
*/
|
684
|
+
removeBarButtonItem: function(barButtonItem, animated) {
|
685
|
+
var barButtonItems = this._barButtonItems;
|
686
|
+
for (var i = 0, length = barButtonItems.length; i < length; i++) {
|
687
|
+
if (barButtonItems[i] === barButtonItem) {
|
688
|
+
if (this.getActive()) {
|
689
|
+
var navigationBar = this.getNavigationBar();
|
690
|
+
if (navigationBar) navigationBar.removeBarButtonItem(barButtonItem, animated);
|
691
|
+
}
|
692
|
+
|
693
|
+
barButtonItems.splice(i, 1);
|
694
|
+
break;
|
695
|
+
}
|
696
|
+
}
|
697
|
+
},
|
698
|
+
|
699
|
+
/**
|
700
|
+
|
701
|
+
*/
|
702
|
+
removeAllBarButtonItems: function(animated) {
|
703
|
+
var barButtonItems = this._barButtonItems;
|
704
|
+
if (this.getActive()) {
|
705
|
+
var navigationBar = this.getNavigationBar();
|
706
|
+
if (navigationBar) for (var i = 0, length = barButtonItems.length; i < length; i++) navigationBar.removeBarButtonItem(barButtonItems[i], animated);
|
707
|
+
}
|
708
|
+
|
709
|
+
barButtonItems.length = 0;
|
710
|
+
},
|
711
|
+
|
712
|
+
_hideBackBarButtonItem: false,
|
713
|
+
|
714
|
+
/**
|
715
|
+
|
716
|
+
*/
|
717
|
+
getHideBackBarButtonItem: function() { return this._hideBackBarButtonItem; },
|
718
|
+
|
719
|
+
/**
|
720
|
+
|
721
|
+
*/
|
722
|
+
setHideBackBarButtonItem: function(hideBackBarButtonItem) {
|
723
|
+
this._hideBackBarButtonItem = hideBackBarButtonItem;
|
724
|
+
},
|
725
|
+
|
726
|
+
_shouldRemoveWhenPopped: false,
|
727
|
+
|
728
|
+
/**
|
729
|
+
|
730
|
+
*/
|
731
|
+
getShouldRemoveWhenPopped: function() { return this._shouldRemoveWhenPopped; },
|
732
|
+
|
733
|
+
/**
|
734
|
+
|
735
|
+
*/
|
736
|
+
setShouldRemoveWhenPopped: function(shouldRemoveWhenPopped) { this._shouldRemoveWhenPopped = shouldRemoveWhenPopped; },
|
737
|
+
|
738
|
+
/**
|
739
|
+
Traverses the parents of this view's element and returns the closest Pushpop.ViewStack.
|
740
|
+
@type Pushpop.ViewStack
|
741
|
+
*/
|
742
|
+
getViewStack: function() { return Pushpop.getViewStackForElement(this.$element); },
|
743
|
+
|
744
|
+
/**
|
745
|
+
|
746
|
+
*/
|
747
|
+
getNavigationBar: function() {
|
748
|
+
var viewStack = this.getViewStack();
|
749
|
+
if (!viewStack) return null;
|
750
|
+
|
751
|
+
var navigationBar = viewStack.$element.children('.pp-navigation-bar')[0];
|
752
|
+
navigationBar = (navigationBar) ? navigationBar.navigationBar : null;
|
753
|
+
|
754
|
+
return navigationBar;
|
755
|
+
},
|
756
|
+
|
757
|
+
/**
|
758
|
+
|
759
|
+
*/
|
760
|
+
getScrollView: function() {
|
761
|
+
var scrollViewElement = this.element;
|
762
|
+
var scrollView = scrollViewElement.scrollView;
|
763
|
+
if (!scrollView) {
|
764
|
+
scrollViewElement = this.$element.children('.sk-scroll-view')[0];
|
765
|
+
if (scrollViewElement) scrollView = scrollViewElement.scrollView;
|
766
|
+
}
|
767
|
+
|
768
|
+
return scrollView || null;
|
769
|
+
},
|
770
|
+
|
771
|
+
/**
|
772
|
+
Forces a reflow in the browser for this view.
|
773
|
+
*/
|
774
|
+
forceReflow: function() { var doNothing = this.element.offsetWidth; },
|
775
|
+
|
776
|
+
setBackButtonVisible: function(visible) {
|
777
|
+
if (this.$navbarButtons.filter('.pp-barbutton-align-left').length === 0) this.hideNavBackButton = !visible;
|
778
|
+
},
|
779
|
+
|
780
|
+
/**
|
781
|
+
Convenience accessor for jQuery's .bind() method.
|
782
|
+
*/
|
783
|
+
$bind: function() { this.$element.bind.apply(this.$element, arguments); },
|
784
|
+
|
785
|
+
/**
|
786
|
+
Convenience accessor for jQuery's .unbind() method.
|
787
|
+
*/
|
788
|
+
$unbind: function() { this.$element.unbind.apply(this.$element, arguments); },
|
789
|
+
|
790
|
+
/**
|
791
|
+
Convenience accessor for jQuery's .delegate() method.
|
792
|
+
*/
|
793
|
+
$delegate: function() { this.$element.delegate.apply(this.$element, arguments); },
|
794
|
+
|
795
|
+
/**
|
796
|
+
Convenience accessor for jQuery's .undelegate() method.
|
797
|
+
*/
|
798
|
+
$undelegate: function() { this.$element.undelegate.apply(this.$element, arguments); },
|
799
|
+
|
800
|
+
/**
|
801
|
+
Convenience accessor for jQuery's .trigger() method.
|
802
|
+
*/
|
803
|
+
$trigger: function() { this.$element.trigger.apply(this.$element, arguments); }
|
804
|
+
};
|
805
|
+
|
806
|
+
/**
|
807
|
+
Creates a new NavigationBar.
|
808
|
+
@param {HTMLDivElement} element The DIV element to initialize as a new NavigationBar.
|
809
|
+
@constructor
|
810
|
+
*/
|
811
|
+
Pushpop.NavigationBar = function NavigationBar(element) {
|
812
|
+
if (!element) return;
|
813
|
+
|
814
|
+
var $element = this.$element = $(element);
|
815
|
+
element = this.element = $element[0];
|
816
|
+
|
817
|
+
var navigationBar = element.navigationBar;
|
818
|
+
if (navigationBar) return navigationBar;
|
819
|
+
|
820
|
+
var self = element.navigationBar = this;
|
821
|
+
|
822
|
+
// Set up this navigation bar's style type.
|
823
|
+
var barStyleTypes = Pushpop.NavigationBar.BarStyleType;
|
824
|
+
var barStyleType;
|
825
|
+
for (var key in barStyleTypes) {
|
826
|
+
if ($element.hasClass(barStyleTypes[key])) {
|
827
|
+
barStyleType = barStyleTypes[key];
|
828
|
+
break;
|
829
|
+
}
|
830
|
+
}
|
831
|
+
|
832
|
+
this.setBarStyleType(barStyleType || Pushpop.NavigationBar.Default);
|
833
|
+
|
834
|
+
// Set up this navigation bar's button items.
|
835
|
+
this._barButtonItems = [];
|
836
|
+
|
837
|
+
// Set up the title container.
|
838
|
+
this.$titleElement = $('<h1 class="pp-navigation-bar-title"/>').appendTo($element);
|
839
|
+
|
840
|
+
// Set up the bar button item containers. NOTE: Two containers are created and are
|
841
|
+
// alternated each time setBarButtonItems() is called to provide a smooth transition.
|
842
|
+
this.$barButtonItemContainerElement = this.$barButtonItemContainerElementA = $('<div class="pp-navigation-bar-button-item-container"/>').appendTo($element);
|
843
|
+
this.$barButtonItemContainerElementB = $('<div class="pp-navigation-bar-button-item-container"/>').appendTo($element);
|
844
|
+
|
845
|
+
// Set up the back button.
|
846
|
+
var backBarButtonItem = new Pushpop.Button('Back', function(button) {
|
847
|
+
var viewStack = button.getViewStack();
|
848
|
+
if (viewStack) viewStack.pop();
|
849
|
+
});
|
850
|
+
|
851
|
+
this.setBackBarButtonItem(backBarButtonItem);
|
852
|
+
|
853
|
+
// Prevent dragging of the navigation bar.
|
854
|
+
$element.bind('touchmove', function(evt) { evt.preventDefault(); });
|
855
|
+
|
856
|
+
// Handle the "tap-to-top" action (automatically scroll to the top of the
|
857
|
+
// scroll view when the top of the navigation bar is tapped).
|
858
|
+
var tapToTop = $element.attr('data-tap-to-top') || 'false';
|
859
|
+
tapToTop = this. _tapToTop = tapToTop !== 'false';
|
860
|
+
|
861
|
+
$('<div class="pp-navigation-bar-tap-to-top-target"/>').appendTo($element).bind('click', function(evt) {
|
862
|
+
if (!self.getTapToTop()) return;
|
863
|
+
|
864
|
+
var activeView = self.getActiveView();
|
865
|
+
if (!activeView) return;
|
866
|
+
|
867
|
+
var scrollView = activeView.getScrollView();
|
868
|
+
if (scrollView && !scrollView.isScrolling) scrollView.scrollToTop();
|
869
|
+
});
|
870
|
+
|
871
|
+
// Handle view changes for the navigation bar's view stack.
|
872
|
+
var viewStack = this.getViewStack();
|
873
|
+
if (viewStack) {
|
874
|
+
viewStack.$bind(Pushpop.EventType.WillPresentView, function(evt) {
|
875
|
+
var view = evt.view;
|
876
|
+
|
877
|
+
// This event bubbles up, so make sure that the view is a child of this viewStack.
|
878
|
+
if (view.getViewStack() !== viewStack) return;
|
879
|
+
|
880
|
+
self.setTitle(view.getTitle());
|
881
|
+
|
882
|
+
var backBarButtonItem = self.getBackBarButtonItem();
|
883
|
+
if (backBarButtonItem) backBarButtonItem.setHidden(view.getHideBackBarButtonItem() || (viewStack.containsView(view) && viewStack.views.length === 1));
|
884
|
+
|
885
|
+
self.setBarButtonItems(view.getBarButtonItems(), true);
|
886
|
+
});
|
887
|
+
}
|
888
|
+
|
889
|
+
// Set up the navigation bar for the initial active view.
|
890
|
+
var activeView = this.getActiveView();
|
891
|
+
if (activeView) {
|
892
|
+
this.setTitle(activeView.getTitle());
|
893
|
+
this.setBarButtonItems(activeView.getBarButtonItems());
|
894
|
+
|
895
|
+
backBarButtonItem.setHidden(activeView.getHideBackBarButtonItem() || (viewStack.containsView(activeView) && viewStack.views.length === 1));
|
896
|
+
}
|
897
|
+
};
|
898
|
+
|
899
|
+
/**
|
900
|
+
Style types for Pushpop.NavigationBar.
|
901
|
+
*/
|
902
|
+
Pushpop.NavigationBar.BarStyleType = {
|
903
|
+
Default: 'pp-navigation-bar-style-default',
|
904
|
+
Black: 'pp-navigation-bar-style-black'
|
905
|
+
};
|
906
|
+
|
907
|
+
Pushpop.NavigationBar.prototype = {
|
908
|
+
constructor: Pushpop.NavigationBar,
|
909
|
+
|
910
|
+
element: null,
|
911
|
+
$element: null,
|
912
|
+
|
913
|
+
$titleElement: null,
|
914
|
+
$barButtonItemContainerElement: null,
|
915
|
+
$barButtonItemContainerElementA: null,
|
916
|
+
$barButtonItemContainerElementB: null,
|
917
|
+
|
918
|
+
_title: '',
|
919
|
+
|
920
|
+
/**
|
921
|
+
|
922
|
+
*/
|
923
|
+
getTitle: function() { return this._title; },
|
924
|
+
|
925
|
+
/**
|
926
|
+
|
927
|
+
*/
|
928
|
+
setTitle: function(title) { this.$titleElement.html(_title = title || ''); },
|
929
|
+
|
930
|
+
_tapToTop: false,
|
931
|
+
|
932
|
+
/**
|
933
|
+
|
934
|
+
*/
|
935
|
+
getTapToTop: function() { return this._tapToTop; },
|
936
|
+
|
937
|
+
/**
|
938
|
+
|
939
|
+
*/
|
940
|
+
setTapToTop: function(tapToTop) { this._tapToTop = tapToTop; },
|
941
|
+
|
942
|
+
_barStyle: Pushpop.NavigationBar.BarStyleType.Default,
|
943
|
+
|
944
|
+
/**
|
945
|
+
|
946
|
+
*/
|
947
|
+
getBarStyleType: function() { return this._barStyle; },
|
948
|
+
|
949
|
+
/**
|
950
|
+
|
951
|
+
*/
|
952
|
+
setBarStyleType: function(barStyle) {
|
953
|
+
this._barStyle = barStyle;
|
954
|
+
},
|
955
|
+
|
956
|
+
_translucent: false,
|
957
|
+
|
958
|
+
/**
|
959
|
+
|
960
|
+
*/
|
961
|
+
getTranslucent: function() { return this._translucent; },
|
962
|
+
|
963
|
+
/**
|
964
|
+
|
965
|
+
*/
|
966
|
+
setTranslucent: function(translucent) {
|
967
|
+
this._translucent = translucent;
|
968
|
+
},
|
969
|
+
|
970
|
+
_tintColor: null,
|
971
|
+
|
972
|
+
/**
|
973
|
+
|
974
|
+
*/
|
975
|
+
getTintColor: function() { return this._tintColor; },
|
976
|
+
|
977
|
+
/**
|
978
|
+
|
979
|
+
*/
|
980
|
+
setTintColor: function(tintColor) {
|
981
|
+
this._tintColor = tintColor;
|
982
|
+
},
|
983
|
+
|
984
|
+
_barButtonItems: null,
|
985
|
+
|
986
|
+
/**
|
987
|
+
|
988
|
+
*/
|
989
|
+
getBarButtonItems: function() { return this._barButtonItems; },
|
990
|
+
|
991
|
+
/**
|
992
|
+
|
993
|
+
*/
|
994
|
+
setBarButtonItems: function(barButtonItems, animated) {
|
995
|
+
this.removeAllBarButtonItems(animated);
|
996
|
+
|
997
|
+
// Toggle the current bar button item container before adding the new items to
|
998
|
+
// create a smooth transition.
|
999
|
+
this.$barButtonItemContainerElement = (this.$barButtonItemContainerElement === this.$barButtonItemContainerElementA) ? this.$barButtonItemContainerElementB : this.$barButtonItemContainerElementA;
|
1000
|
+
|
1001
|
+
for (var i = 0, length = barButtonItems.length; i < length; i++) this.addBarButtonItem(barButtonItems[i], animated);
|
1002
|
+
},
|
1003
|
+
|
1004
|
+
/**
|
1005
|
+
|
1006
|
+
*/
|
1007
|
+
addBarButtonItem: function(barButtonItem, animated) {
|
1008
|
+
var barStyle = this.getBarStyleType();
|
1009
|
+
if (barButtonItem.getButtonStyleType() === Pushpop.Button.ButtonStyleType.Default) {
|
1010
|
+
if (barStyle === Pushpop.NavigationBar.BarStyleType.Default) {
|
1011
|
+
barButtonItem.setButtonStyleType(Pushpop.Button.ButtonStyleType.Default);
|
1012
|
+
} else if (barStyle === Pushpop.NavigationBar.BarStyleType.Black) {
|
1013
|
+
barButtonItem.setButtonStyleType(Pushpop.Button.ButtonStyleType.Black);
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
this._barButtonItems.push(barButtonItem);
|
1018
|
+
barButtonItem.appendTo(this.$barButtonItemContainerElement, animated);
|
1019
|
+
},
|
1020
|
+
|
1021
|
+
/**
|
1022
|
+
|
1023
|
+
*/
|
1024
|
+
removeBarButtonItem: function(barButtonItem, animated) {
|
1025
|
+
var barButtonItems = this._barButtonItems;
|
1026
|
+
for (var i = 0, length = barButtonItems.length; i < length; i++) {
|
1027
|
+
if (barButtonItems[i] === barButtonItem) {
|
1028
|
+
barButtonItems.splice(i, 1);
|
1029
|
+
barButtonItem.remove(animated);
|
1030
|
+
break;
|
1031
|
+
}
|
1032
|
+
}
|
1033
|
+
},
|
1034
|
+
|
1035
|
+
/**
|
1036
|
+
|
1037
|
+
*/
|
1038
|
+
removeAllBarButtonItems: function(animated) {
|
1039
|
+
var barButtonItems = this._barButtonItems;
|
1040
|
+
for (var i = 0, length = barButtonItems.length; i < length; i++) barButtonItems[i].remove(animated);
|
1041
|
+
barButtonItems.length = 0;
|
1042
|
+
},
|
1043
|
+
|
1044
|
+
_backBarButtonItem: null,
|
1045
|
+
|
1046
|
+
/**
|
1047
|
+
|
1048
|
+
*/
|
1049
|
+
getBackBarButtonItem: function() { return this._backBarButtonItem; },
|
1050
|
+
|
1051
|
+
/**
|
1052
|
+
|
1053
|
+
*/
|
1054
|
+
setBackBarButtonItem: function(backBarButtonItem) {
|
1055
|
+
var previousBackBarButtonItem = this._backBarButtonItem;
|
1056
|
+
if (previousBackBarButtonItem) previousBackBarButtonItem.$element.removeClass('pp-navigation-bar-back-bar-button-item');
|
1057
|
+
this.$element.prepend((this._backBarButtonItem = backBarButtonItem).$element);
|
1058
|
+
|
1059
|
+
backBarButtonItem.$element.addClass('pp-navigation-bar-back-bar-button-item');
|
1060
|
+
|
1061
|
+
var viewStack = this.getViewStack();
|
1062
|
+
if (viewStack) backBarButtonItem.setHidden(viewStack.views.length === 1);
|
1063
|
+
|
1064
|
+
var barStyle = this.getBarStyleType();
|
1065
|
+
if (barStyle === Pushpop.NavigationBar.BarStyleType.Default) {
|
1066
|
+
backBarButtonItem.setButtonStyleType(Pushpop.Button.ButtonStyleType.Default);
|
1067
|
+
} else if (barStyle === Pushpop.NavigationBar.BarStyleType.Black) {
|
1068
|
+
backBarButtonItem.setButtonStyleType(Pushpop.Button.ButtonStyleType.Black);
|
1069
|
+
}
|
1070
|
+
},
|
1071
|
+
|
1072
|
+
/**
|
1073
|
+
Returns the view stack that contains this navigation bar.
|
1074
|
+
@description NOTE: If this navigation bar is not contained within a view stack,
|
1075
|
+
this method will return null.
|
1076
|
+
@type Pushpop.ViewStack
|
1077
|
+
*/
|
1078
|
+
getViewStack: function() {
|
1079
|
+
var parents = this.$element.parents();
|
1080
|
+
var viewStack;
|
1081
|
+
for (var i = 0, length = parents.length; i < length; i++) if ((viewStack = parents[i].viewStack)) return viewStack;
|
1082
|
+
return null;
|
1083
|
+
},
|
1084
|
+
|
1085
|
+
/**
|
1086
|
+
Returns the active view that this navigation bar currently represents.
|
1087
|
+
@description NOTE: If this navigation bar is not contained within a view stack
|
1088
|
+
or there is no active view, this method will return null.
|
1089
|
+
@type Pushpop.View
|
1090
|
+
*/
|
1091
|
+
getActiveView: function() {
|
1092
|
+
var viewStack = this.getViewStack();
|
1093
|
+
return (viewStack) ? viewStack.getActiveView() : null;
|
1094
|
+
}
|
1095
|
+
};
|
1096
|
+
|
1097
|
+
/**
|
1098
|
+
Creates a new Button.
|
1099
|
+
@param {HTMLAnchorElement} element The A element to initialize as a new Button.
|
1100
|
+
@param {Function} [action] Optional callback function to execute for the button's action.
|
1101
|
+
@param {String} [buttonAlignmentType] Optional alignment type specified by Pushpop.Button.ButtonAlignmentType.
|
1102
|
+
@param {String} [buttonStyleType] Optional style type specified by Pushpop.Button.ButtonStyleType.
|
1103
|
+
@constructor
|
1104
|
+
*/
|
1105
|
+
Pushpop.Button = function Button(elementOrTitle, action, buttonAlignmentType, buttonStyleType) {
|
1106
|
+
var $element = this.$element = (!elementOrTitle || typeof elementOrTitle === 'string') ?
|
1107
|
+
$('<a class="pp-button" href="#">' + (elementOrTitle || '') + '</a>') : $(elementOrTitle);
|
1108
|
+
|
1109
|
+
var element = this.element = $element[0];
|
1110
|
+
|
1111
|
+
var button = element.button;
|
1112
|
+
if (button) return button;
|
1113
|
+
|
1114
|
+
var self = element.button = this;
|
1115
|
+
|
1116
|
+
var buttonAlignmentTypes = Pushpop.Button.ButtonAlignmentType;
|
1117
|
+
var buttonStyleTypes = Pushpop.Button.ButtonStyleType;
|
1118
|
+
var key;
|
1119
|
+
|
1120
|
+
if (!buttonAlignmentType) for (key in buttonAlignmentTypes) {
|
1121
|
+
if ($element.hasClass(buttonAlignmentTypes[key])) {
|
1122
|
+
buttonAlignmentType = buttonAlignmentTypes[key];
|
1123
|
+
break;
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
if (!buttonStyleType) for (key in buttonStyleTypes) {
|
1128
|
+
if ($element.hasClass(buttonStyleTypes[key])) {
|
1129
|
+
buttonStyleType = buttonStyleTypes[key];
|
1130
|
+
break;
|
1131
|
+
}
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
this.setTitle($element.html());
|
1135
|
+
this.setAction(action || null);
|
1136
|
+
this.setButtonAlignmentType(buttonAlignmentType || Pushpop.Button.ButtonAlignmentType.Default);
|
1137
|
+
this.setButtonStyleType(buttonStyleType || Pushpop.Button.ButtonStyleType.Default);
|
1138
|
+
};
|
1139
|
+
|
1140
|
+
/**
|
1141
|
+
Alignment types for Pushpop.Button.
|
1142
|
+
*/
|
1143
|
+
Pushpop.Button.ButtonAlignmentType = {
|
1144
|
+
Default: 'pp-button-alignment-default',
|
1145
|
+
Left: 'pp-button-alignment-left',
|
1146
|
+
Right: 'pp-button-alignment-right'
|
1147
|
+
};
|
1148
|
+
|
1149
|
+
/**
|
1150
|
+
Style types for Pushpop.Button.
|
1151
|
+
*/
|
1152
|
+
Pushpop.Button.ButtonStyleType = {
|
1153
|
+
Default: 'pp-button-style-default',
|
1154
|
+
Black: 'pp-button-style-black',
|
1155
|
+
Blue: 'pp-button-style-blue',
|
1156
|
+
Green: 'pp-button-style-green',
|
1157
|
+
Red: 'pp-button-style-red'
|
1158
|
+
};
|
1159
|
+
|
1160
|
+
/**
|
1161
|
+
Event types for Pushpop.Button.
|
1162
|
+
*/
|
1163
|
+
Pushpop.Button.EventType = {
|
1164
|
+
WillTriggerAction: 'Pushpop:Button:WillTriggerAction',
|
1165
|
+
DidTriggerAction: 'Pushpop:Button:DidTriggerAction'
|
1166
|
+
};
|
1167
|
+
|
1168
|
+
Pushpop.Button.prototype = {
|
1169
|
+
constructor: Pushpop.Button,
|
1170
|
+
|
1171
|
+
element: null,
|
1172
|
+
$element: null,
|
1173
|
+
|
1174
|
+
_title: '',
|
1175
|
+
|
1176
|
+
/**
|
1177
|
+
|
1178
|
+
*/
|
1179
|
+
getTitle: function() { return this._title; },
|
1180
|
+
|
1181
|
+
/**
|
1182
|
+
|
1183
|
+
*/
|
1184
|
+
setTitle: function(title) { this.$element.html(_title = title || ''); },
|
1185
|
+
|
1186
|
+
_action: null,
|
1187
|
+
|
1188
|
+
/**
|
1189
|
+
|
1190
|
+
*/
|
1191
|
+
getAction: function() { return this._action; },
|
1192
|
+
|
1193
|
+
/**
|
1194
|
+
|
1195
|
+
*/
|
1196
|
+
setAction: function(action) { this._action = action; },
|
1197
|
+
|
1198
|
+
_active: false,
|
1199
|
+
|
1200
|
+
/**
|
1201
|
+
|
1202
|
+
*/
|
1203
|
+
getActive: function() { return this._active; },
|
1204
|
+
|
1205
|
+
/**
|
1206
|
+
|
1207
|
+
*/
|
1208
|
+
setActive: function(active) {
|
1209
|
+
if ((this._active = active)) {
|
1210
|
+
this.$element.addClass('pp-button-state-active');
|
1211
|
+
} else {
|
1212
|
+
this.$element.removeClass('pp-button-state-active');
|
1213
|
+
}
|
1214
|
+
},
|
1215
|
+
|
1216
|
+
_buttonAlignmentType: Pushpop.Button.ButtonAlignmentType.Default,
|
1217
|
+
|
1218
|
+
getButtonAlignmentType: function() { return this._buttonAlignmentType; },
|
1219
|
+
|
1220
|
+
setButtonAlignmentType: function(buttonAlignmentType) {
|
1221
|
+
var $element = this.$element, buttonAlignmentTypes = Pushpop.Button.ButtonAlignmentType;
|
1222
|
+
for (var i in buttonAlignmentTypes) $element.removeClass(buttonAlignmentTypes[i]);
|
1223
|
+
$element.addClass(this._buttonAlignmentType = buttonAlignmentType);
|
1224
|
+
},
|
1225
|
+
|
1226
|
+
_buttonStyleType: Pushpop.Button.ButtonStyleType.Default,
|
1227
|
+
|
1228
|
+
getButtonStyleType: function() { return this._buttonStyleType; },
|
1229
|
+
|
1230
|
+
setButtonStyleType: function(buttonStyleType) {
|
1231
|
+
var $element = this.$element, buttonStyleTypes = Pushpop.Button.ButtonStyleType;
|
1232
|
+
for (var i in buttonStyleTypes) $element.removeClass(buttonStyleTypes[i]);
|
1233
|
+
$element.addClass(this._buttonStyleType = buttonStyleType);
|
1234
|
+
},
|
1235
|
+
|
1236
|
+
_hidden: false,
|
1237
|
+
|
1238
|
+
/**
|
1239
|
+
|
1240
|
+
*/
|
1241
|
+
getHidden: function() { return this._hidden; },
|
1242
|
+
|
1243
|
+
/**
|
1244
|
+
|
1245
|
+
*/
|
1246
|
+
setHidden: function(hidden) {
|
1247
|
+
if ((this._hidden = hidden)) {
|
1248
|
+
this.$element.addClass('pp-hidden');
|
1249
|
+
} else {
|
1250
|
+
this.$element.removeClass('pp-hidden');
|
1251
|
+
}
|
1252
|
+
},
|
1253
|
+
|
1254
|
+
/**
|
1255
|
+
|
1256
|
+
*/
|
1257
|
+
triggerAction: function() {
|
1258
|
+
var $element = this.$element;
|
1259
|
+
var action = this.getAction();
|
1260
|
+
|
1261
|
+
$element.trigger($.Event(Pushpop.Button.EventType.WillTriggerAction, {
|
1262
|
+
button: this,
|
1263
|
+
action: action
|
1264
|
+
}));
|
1265
|
+
|
1266
|
+
if (action) {
|
1267
|
+
if (typeof action === 'string') window[action](this);
|
1268
|
+
else if (typeof action === 'function') action(this);
|
1269
|
+
}
|
1270
|
+
|
1271
|
+
$element.trigger($.Event(Pushpop.Button.EventType.DidTriggerAction, {
|
1272
|
+
button: this,
|
1273
|
+
action: action
|
1274
|
+
}));
|
1275
|
+
},
|
1276
|
+
|
1277
|
+
_pendingRemovalTimeout: null,
|
1278
|
+
|
1279
|
+
remove: function(animated) {
|
1280
|
+
var $element = this.$element;
|
1281
|
+
|
1282
|
+
if (animated) {
|
1283
|
+
this.setHidden(true);
|
1284
|
+
this._pendingRemovalTimeout = window.setTimeout(function() {
|
1285
|
+
$element.detach();
|
1286
|
+
}, 300);
|
1287
|
+
} else {
|
1288
|
+
$element.detach();
|
1289
|
+
this.setHidden(true);
|
1290
|
+
}
|
1291
|
+
},
|
1292
|
+
|
1293
|
+
appendTo: function(element, animated) {
|
1294
|
+
if (this._pendingRemovalTimeout) window.clearTimeout(this._pendingRemovalTimeout);
|
1295
|
+
if (animated) {
|
1296
|
+
this.setHidden(true);
|
1297
|
+
$(element).append(this.$element);
|
1298
|
+
this.forceReflow();
|
1299
|
+
this.setHidden(false);
|
1300
|
+
} else {
|
1301
|
+
this.setHidden(false);
|
1302
|
+
$(element).append(this.$element);
|
1303
|
+
}
|
1304
|
+
},
|
1305
|
+
|
1306
|
+
/**
|
1307
|
+
Forces a reflow in the browser for this button.
|
1308
|
+
*/
|
1309
|
+
forceReflow: function() { var doNothing = this.element.offsetWidth; },
|
1310
|
+
|
1311
|
+
/**
|
1312
|
+
Returns the view that contains this button.
|
1313
|
+
@description NOTE: If this button is not contained within a view, this method will return null.
|
1314
|
+
@type Pushpop.View
|
1315
|
+
*/
|
1316
|
+
getView: function() {
|
1317
|
+
var parents = this.$element.parents();
|
1318
|
+
var view;
|
1319
|
+
for (var i = 0, length = parents.length; i < length; i++) if ((view = parents[i].view)) return view;
|
1320
|
+
return null;
|
1321
|
+
},
|
1322
|
+
|
1323
|
+
/**
|
1324
|
+
Returns the view stack that contains this button.
|
1325
|
+
@description NOTE: If this button is not contained within a view stack, this method will return null.
|
1326
|
+
@type Pushpop.ViewStack
|
1327
|
+
*/
|
1328
|
+
getViewStack: function() {
|
1329
|
+
var parents = this.$element.parents();
|
1330
|
+
var viewStack;
|
1331
|
+
for (var i = 0, length = parents.length; i < length; i++) if ((viewStack = parents[i].viewStack)) return viewStack;
|
1332
|
+
return null;
|
1333
|
+
}
|
1334
|
+
};
|
1335
|
+
|
1336
|
+
/**
|
1337
|
+
|
1338
|
+
*/
|
1339
|
+
Pushpop.ActionSheet = function ActionSheet(element) {
|
1340
|
+
if (!element) return;
|
1341
|
+
|
1342
|
+
var $element = this.$element = $(element);
|
1343
|
+
element = this.element = $element[0];
|
1344
|
+
|
1345
|
+
var actionSheet = element.actionSheet;
|
1346
|
+
if (actionSheet) return actionSheet;
|
1347
|
+
|
1348
|
+
var self = element.actionSheet = this;
|
1349
|
+
|
1350
|
+
this._visible = $element.hasClass('pp-active');
|
1351
|
+
|
1352
|
+
$element.delegate('.pp-button', Pushpop.Button.EventType.DidTriggerAction, function(evt) {
|
1353
|
+
var button = evt.button;
|
1354
|
+
if (button && button.$element.hasClass('pp-action-sheet-dismiss')) self.dismiss();
|
1355
|
+
});
|
1356
|
+
|
1357
|
+
// Prevent dragging of the action sheet.
|
1358
|
+
$element.bind('touchmove', function(evt) { evt.preventDefault(); });
|
1359
|
+
};
|
1360
|
+
|
1361
|
+
Pushpop.ActionSheet.prototype = {
|
1362
|
+
constructor: Pushpop.ActionSheet,
|
1363
|
+
|
1364
|
+
element: null,
|
1365
|
+
$element: null,
|
1366
|
+
|
1367
|
+
_visible: false,
|
1368
|
+
|
1369
|
+
/**
|
1370
|
+
|
1371
|
+
*/
|
1372
|
+
getVisible: function() { return this._visible; },
|
1373
|
+
|
1374
|
+
/**
|
1375
|
+
|
1376
|
+
*/
|
1377
|
+
show: function() {
|
1378
|
+
if (this.getVisible()) return;
|
1379
|
+
|
1380
|
+
this.$element.addClass('pp-active');
|
1381
|
+
this._visible = true;
|
1382
|
+
},
|
1383
|
+
|
1384
|
+
/**
|
1385
|
+
|
1386
|
+
*/
|
1387
|
+
dismiss: function() {
|
1388
|
+
if (!this.getVisible()) return;
|
1389
|
+
|
1390
|
+
this.$element.removeClass('pp-active');
|
1391
|
+
this._visible = false;
|
1392
|
+
}
|
1393
|
+
};
|
1394
|
+
|
1395
|
+
$(function() {
|
1396
|
+
var buttons = Pushpop.buttons = Pushpop.buttons || {};
|
1397
|
+
var views = Pushpop.views = Pushpop.views || {};
|
1398
|
+
var viewStacks = Pushpop.viewStacks = Pushpop.viewStacks || {};
|
1399
|
+
var navigationBars = Pushpop.navigationBars = Pushpop.navigationBars || {};
|
1400
|
+
var actionSheets = Pushpop.actionSheets = Pushpop.actionSheets || {};
|
1401
|
+
|
1402
|
+
$('.pp-button').each(function(index, element) {
|
1403
|
+
var button = new Pushpop.Button(element);
|
1404
|
+
if (element.id) buttons[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = button;
|
1405
|
+
});
|
1406
|
+
|
1407
|
+
$('.pp-view').each(function(index, element) {
|
1408
|
+
var view = new Pushpop.View(element);
|
1409
|
+
if (element.id) views[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = view;
|
1410
|
+
});
|
1411
|
+
|
1412
|
+
if (Pushpop.SplitView) $('.pp-split-view').each(function(index, element) {
|
1413
|
+
var splitView = new Pushpop.SplitView(element);
|
1414
|
+
if (element.id) views[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = splitView;
|
1415
|
+
});
|
1416
|
+
|
1417
|
+
if (Pushpop.TabView) $('.pp-tab-view').each(function(index, element) {
|
1418
|
+
var tabView = new Pushpop.TabView(element);
|
1419
|
+
if (element.id) views[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = tabView;
|
1420
|
+
});
|
1421
|
+
|
1422
|
+
$('.pp-view-stack').each(function(index, element) {
|
1423
|
+
var viewStack = new Pushpop.ViewStack(element);
|
1424
|
+
if (element.id) viewStacks[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = viewStack;
|
1425
|
+
});
|
1426
|
+
|
1427
|
+
if (Pushpop.ModalViewStack) $('.pp-modal-view-stack').each(function(index, element) {
|
1428
|
+
var viewStack = new Pushpop.ModalViewStack(element);
|
1429
|
+
if (element.id) viewStacks[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = viewStack;
|
1430
|
+
});
|
1431
|
+
|
1432
|
+
if (Pushpop.PopoverViewStack) $('.pp-popover-view-stack').each(function(index, element) {
|
1433
|
+
var viewStack = new Pushpop.PopoverViewStack(element);
|
1434
|
+
if (element.id) viewStacks[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = viewStack;
|
1435
|
+
});
|
1436
|
+
|
1437
|
+
$('.pp-navigation-bar').each(function(index, element) {
|
1438
|
+
var navigationBar = new Pushpop.NavigationBar(element);
|
1439
|
+
if (element.id) navigationBars[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = navigationBar;
|
1440
|
+
});
|
1441
|
+
|
1442
|
+
$('.pp-action-sheet').each(function(index, element) {
|
1443
|
+
var actionSheet = new Pushpop.ActionSheet(element);
|
1444
|
+
if (element.id) actionSheets[Pushpop.Util.convertDashedStringToCamelCase(element.id)] = actionSheet;
|
1445
|
+
});
|
1446
|
+
|
1447
|
+
// Handle mouse/touch events globally to trigger button actions.
|
1448
|
+
var $body = $(document.body);
|
1449
|
+
|
1450
|
+
$body.delegate('.pp-button', 'click', function(evt) { evt.preventDefault(); });
|
1451
|
+
|
1452
|
+
$body.delegate('.pp-button', !!('ontouchstart' in window) ? 'touchstart' : 'mousedown', function(evt) {
|
1453
|
+
var button = this.button;
|
1454
|
+
if (!button) return;
|
1455
|
+
button.setActive(true);
|
1456
|
+
});
|
1457
|
+
|
1458
|
+
$body.delegate('.pp-button', !!('ontouchmove' in window) ? 'touchmove' : 'mousemove', function(evt) {
|
1459
|
+
var button = this.button;
|
1460
|
+
if (!button || !button.getActive()) return;
|
1461
|
+
|
1462
|
+
button.setActive(false);
|
1463
|
+
});
|
1464
|
+
|
1465
|
+
$body.delegate('.pp-button', !!('ontouchend' in window) ? 'touchend' : 'mouseup', function(evt) {
|
1466
|
+
var button = this.button;
|
1467
|
+
if (!button || !button.getActive()) return;
|
1468
|
+
|
1469
|
+
evt.preventDefault();
|
1470
|
+
|
1471
|
+
button.setActive(false);
|
1472
|
+
button.triggerAction();
|
1473
|
+
});
|
1474
|
+
|
1475
|
+
// Handle actions for buttons set up to automatically push/pop views.
|
1476
|
+
$body.delegate('.pp-button.pp-push, .pp-button.pp-pop', Pushpop.Button.EventType.DidTriggerAction, function(evt) {
|
1477
|
+
var button = evt.button;
|
1478
|
+
var $element = button.$element;
|
1479
|
+
var href = $element.attr('href');
|
1480
|
+
var transition = $element.attr('data-transition');
|
1481
|
+
var $viewElement, view, viewStack;
|
1482
|
+
|
1483
|
+
if ($element.hasClass('pp-push')) {
|
1484
|
+
$viewElement = $(href);
|
1485
|
+
if ($viewElement.length === 0) return;
|
1486
|
+
|
1487
|
+
view = $viewElement[0].view || new Pushpop.View($viewElement);
|
1488
|
+
|
1489
|
+
viewStack = view.getViewStack();
|
1490
|
+
if (viewStack) viewStack.push(view, transition);
|
1491
|
+
}
|
1492
|
+
|
1493
|
+
else if ($element.hasClass('pp-pop')) {
|
1494
|
+
if (href === '#') {
|
1495
|
+
viewStack = button.getViewStack();
|
1496
|
+
if (viewStack) viewStack.pop(transition);
|
1497
|
+
} else {
|
1498
|
+
$viewElement = $(href);
|
1499
|
+
if ($viewElement.length === 0) return;
|
1500
|
+
|
1501
|
+
view = $viewElement[0].view || new Pushpop.View($viewElement);
|
1502
|
+
|
1503
|
+
viewStack = view.getViewStack();
|
1504
|
+
if (viewStack) viewStack.pop(view, transition);
|
1505
|
+
}
|
1506
|
+
}
|
1507
|
+
});
|
1508
|
+
|
1509
|
+
// TODO: Is this still needed?
|
1510
|
+
$(document).bind('touchstart', function() {});
|
1511
|
+
|
1512
|
+
// TODO: Clean up (use new events?)
|
1513
|
+
$('a.pp-push').live('click', function(evt) {
|
1514
|
+
var $this = $(this);
|
1515
|
+
if ($this.hasClass('pp-button')) return;
|
1516
|
+
|
1517
|
+
evt.preventDefault();
|
1518
|
+
|
1519
|
+
var href = $this.attr('href');
|
1520
|
+
var $viewElement, view, viewStack;
|
1521
|
+
|
1522
|
+
$viewElement = $(href);
|
1523
|
+
if ($viewElement.length === 0) return;
|
1524
|
+
|
1525
|
+
view = $viewElement[0].view || new Pushpop.View($viewElement);
|
1526
|
+
|
1527
|
+
viewStack = view.getViewStack();
|
1528
|
+
if (viewStack) viewStack.push(view, $this.attr('data-transition'));
|
1529
|
+
});
|
1530
|
+
|
1531
|
+
// TODO: Clean up (use new events?)
|
1532
|
+
$('a.pp-pop').live('click', function(evt) {
|
1533
|
+
var $this = $(this);
|
1534
|
+
if ($this.hasClass('pp-button')) return;
|
1535
|
+
|
1536
|
+
evt.preventDefault();
|
1537
|
+
|
1538
|
+
var href = $this.attr('href');
|
1539
|
+
var $viewElement, view, viewStack;
|
1540
|
+
|
1541
|
+
if (href === '#') {
|
1542
|
+
viewStack = Pushpop.getViewStackForElement($this);
|
1543
|
+
if (viewStack) viewStack.pop($this.attr('data-transition'));
|
1544
|
+
} else {
|
1545
|
+
$viewElement = $(href);
|
1546
|
+
if ($viewElement.length === 0) return;
|
1547
|
+
|
1548
|
+
view = $viewElement[0].view || new Pushpop.View($viewElement);
|
1549
|
+
|
1550
|
+
viewStack = view.getViewStack();
|
1551
|
+
if (viewStack) viewStack.pop(view, $this.attr('data-transition'));
|
1552
|
+
}
|
1553
|
+
});
|
1554
|
+
});
|