pushpop-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/README.md +42 -0
  2. data/Rakefile +29 -0
  3. data/lib/generators/pushpop/install/install_generator.rb +17 -0
  4. data/lib/pushpop-rails.rb +4 -0
  5. data/lib/pushpop/rails.rb +8 -0
  6. data/lib/pushpop/rails/engine.rb +7 -0
  7. data/lib/pushpop/rails/version.rb +5 -0
  8. data/vendor/assets/Pushpop/background.png +0 -0
  9. data/vendor/assets/Pushpop/background@2x.png +0 -0
  10. data/vendor/assets/Pushpop/externals/scrollkit/background.png +0 -0
  11. data/vendor/assets/Pushpop/externals/scrollkit/scrollkit.css +202 -0
  12. data/vendor/assets/Pushpop/externals/scrollkit/scrollkit.js +924 -0
  13. data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.eot +0 -0
  14. data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.svg +57 -0
  15. data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.ttf +0 -0
  16. data/vendor/assets/Pushpop/font/pushpop-glyphs-webfont.woff +0 -0
  17. data/vendor/assets/Pushpop/pushpop-modal-view-stack/pushpop-modal-view-stack.css +148 -0
  18. data/vendor/assets/Pushpop/pushpop-modal-view-stack/pushpop-modal-view-stack.js +306 -0
  19. data/vendor/assets/Pushpop/pushpop-popover-view-stack/pushpop-popover-view-stack.css +170 -0
  20. data/vendor/assets/Pushpop/pushpop-popover-view-stack/pushpop-popover-view-stack.js +278 -0
  21. data/vendor/assets/Pushpop/pushpop-split-view/pushpop-split-view.css +38 -0
  22. data/vendor/assets/Pushpop/pushpop-split-view/pushpop-split-view.js +33 -0
  23. data/vendor/assets/Pushpop/pushpop-tab-view/pushpop-tab-view.css +130 -0
  24. data/vendor/assets/Pushpop/pushpop-tab-view/pushpop-tab-view.js +298 -0
  25. data/vendor/assets/Pushpop/pushpop-table-view/pushpop-table-view.css +1273 -0
  26. data/vendor/assets/Pushpop/pushpop-table-view/pushpop-table-view.js +2275 -0
  27. data/vendor/assets/Pushpop/pushpop.css +2243 -0
  28. data/vendor/assets/Pushpop/pushpop.js +1554 -0
  29. data/vendor/assets/javascripts/pushpop_rails.js +7 -0
  30. data/vendor/assets/stylesheets/pushpop_rails.css +9 -0
  31. 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=["&#173;","<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
+ });