/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.sblab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse, Kik Interactive, Matteo Spinelli ** $INITIAL: (C) 2006-2019 bLAB ** $CREATED: 12-12-18 by sbosse. ** $VERSION: 1.1.10 ** $ORIGINAL: ** $INFO: ** ** CORDOVA/NW.JS APP.js Framework ** ** App.js v3.0.8 ** Instant mobile web app creation ** Copyright (c) 2012 Kik Interactive, http://kik.com ** Released under the MIT license ** ** iScroll v4.1.6 ** Copyright (c) 2011 Matteo Spinelli, http://cubiq.org ** Released under the MIT license ** ** $ENDOFINFO ** */ var Swapper = function(c, b) { function a(e, d, f, g) { a._swapper(e, d, f, g) } if (c && c.fn) { c.extend(c.fn, { swapper: function(d, e, f) { d = c(d)[0]; this.forEach(function(g) { a._swapper(g, d, e, f) }); return this } }) } if (b && b.fn) { b.fn.swapper = function(d, e, f) { d = b(d)[0]; this.each(function() { a._swapper(this, d, e, f) }); return this } } return a }(window.Zepto, window.jQuery); Swapper._os = function(f, d) { var c, a, b; if (b = /\bCPU.*OS (\d+(_\d+)?)/i.exec(f)) { c = "ios"; a = b[1].replace("_", ".") } else { if (b = /\bAndroid (\d+(\.\d+)?)/.exec(f)) { c = "android"; a = b[1] } } var e = { name: c, version: a && d(a) }; e[c] = true; return e }(navigator.userAgent, parseFloat); Swapper._isNode = function(a, b) { return function(d) { if (!d) { return false } try { return (d instanceof a) || (d instanceof b) } catch (c) {} if (typeof d !== "object") { return false } if (typeof d.nodeType !== "number") { return false } if (typeof d.nodeName !== "string") { return false } return true } }(Node, HTMLElement); Swapper._isInDOM = function(a) { return function(c, b) { if (!b && !a(c)) { throw TypeError("element must be a DOM node, got " + c) } while (c = c.parentNode) { if (c === document) { return true } } return false } }(Swapper._isNode); Swapper._insertBefore = function() { return function(a, b) { b.parentNode.insertBefore(a, b) } }(); Swapper._insertAfter = function() { return function(a, c) { var b = c.parentNode; if (b.lastchild === c) { b.appendChild(a) } else { b.insertBefore(a, c.nextSibling) } } }(); Swapper._removeNode = function() { return function(a) { if (a.parentNode) { a.parentNode.removeChild(a) } } }(); Swapper._setTransform = function() { return function(b, a) { b.style["-webkit-transform"] = a; b.style["-moz-transform"] = a; b.style["-ms-transform"] = a; b.style["-o-transform"] = a; b.style.transform = a } }(); Swapper._setTransition = function() { return function(a, b) { if (b) { a.style["-webkit-transition"] = "-webkit-" + b; a.style["-moz-transition"] = "-moz-" + b; a.style["-ms-transition"] = "-ms-" + b; a.style["-o-transition"] = "-o-" + b; a.style.transition = b } else { a.style["-webkit-transition"] = ""; a.style["-moz-transition"] = ""; a.style["-ms-transition"] = ""; a.style["-o-transition"] = ""; a.style.transition = "" } } }(); Swapper._getStyles = function(a) { return function(c, d) { var b; if (d) { b = c.style } else { b = a.defaultView.getComputedStyle(c, null) } return { "-webkit-transition": b["-webkit-transition"], "-moz-transition": b["-moz-transition"], "-ms-transition": b["-ms-transition"], "-o-transition": b["-o-transition"], transition: b.transition, display: b.display, opacity: b.opacity, top: b.top, left: b.left, height: b.height, width: b.width, position: b.position } } }(document); Swapper._easings = { linear: "linear", ease: "ease", "ease-in": "ease-in", "ease-out": "ease-out", "ease-in-out": "ease-in-out", "step-start": "step-start", "step-end": "step-end" }; Swapper._transitions = { fade: [{ fade: true }, { fade: true }], "fade-on": [{ fade: true }, {}], "fade-off": [{}, { fade: true }, true], "scale-in": [{ transform: "scale(0.01)" }, {}], "scale-out": [{}, { transform: "scale(0.01)" }, true], "rotate-left": [{ transform: "rotateY(-180deg) perspective(360px)", fade: true }, { transform: "rotateY( 180deg) perspective(360px)", fade: true }], "rotate-right": [{ transform: "rotateY( 180deg) perspective(360px)", fade: true }, { transform: "rotateY(-180deg) perspective(360px)", fade: true }], "cube-left": [{ transform: "translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)" }, { transform: "translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)" }], "cube-right": [{ transform: "translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)" }, { transform: "translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)" }], "swap-left": [{ transform: "translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)" }, { transform: "translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)" }], "swap-right": [{ transform: "translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)" }, { transform: "translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)" }], "explode-in": [{ fade: true, transform: "scale(1.25)" }, {}], "explode-out": [{}, { fade: true, transform: "scale(1.25)" }, true], "implode-in": [{}, { fade: true, transform: "scale(0.60)" }, true], "implode-out": [{ fade: true, transform: "scale(0.80)" }, {}], "slide-left": [{ transform: "translate3d( 100%,0,0)" }, { transform: "translate3d(-100%,0,0)" }], "slide-right": [{ transform: "translate3d(-100%,0,0)" }, { transform: "translate3d( 100%,0,0)" }], "slide-up": [{ transform: "translate3d(0, 100%,0)" }, { transform: "translate3d(0,-100%,0)" }], "slide-down": [{ transform: "translate3d(0,-100%,0)" }, { transform: "translate3d(0, 100%,0)" }], "slideon-left": [{ transform: "translate3d(-100%,0,0)" }, {}], "slideoff-left": [{}, { transform: "translate3d(-100%,0,0)" }, true], "slideon-right": [{ transform: "translate3d(100%,0,0)" }, {}], "slideoff-right": [{}, { transform: "translate3d(100%,0,0)" }, true], "slideon-up": [{ transform: "translate3d(0,-100%,0)" }, {}], "slideoff-up": [{}, { transform: "translate3d(0,-100%,0)" }, true], "slideon-down": [{ transform: "translate3d(0,100%,0)" }, {}], "slideoff-down": [{}, { transform: "translate3d(0,100%,0)" }, true], "slideon-left-ios": [{ transform: "translate3d(100%,0,0)" }, { transform: "translate3d(-30%,0,0)" }], "slideoff-right-ios": [{ transform: "translate3d(-30%,0,0)" }, { transform: "translate3d(100%,0,0)" }, true], "glideon-right": [{ transform: "translate3d(110%,0,0)" }, { transform: "translate3d(-20%,0,0)" }], "glideoff-right": [{ transform: "translate3d(-20%,0,0)" }, { transform: "translate3d(110%,0,0)" }, true], "glideon-left": [{ transform: "translate3d(-110%,0,0)" }, { transform: "translate3d(20%,0,0)" }], "glideoff-left": [{ transform: "translate3d(20%,0,0)" }, { transform: "translate3d(-110%,0,0)" }, true], "glideon-down": [{ transform: "translate3d(0,110%,0)" }, { transform: "translate3d(0,-20%,0)" }], "glideoff-down": [{ transform: "translate3d(0,-20%,0)" }, { transform: "translate3d(0,110%,0)" }, true], "glideon-up": [{ transform: "translate3d(0,-110%,0)" }, { transform: "translate3d(0,20%,0)" }], "glideoff-up": [{ transform: "translate3d(0,20%,0)" }, { transform: "translate3d(0,-110%,0)" }, true], "android-l-in": [{ transform: "translate3d(0,6%,0)", fade: true }, {}], "android-l-out": [{}, { transform: "translate3d(0,6%,0)", fade: true }, true] }; Swapper._validate = function(e, f, d) { return { element: c, options: b, callback: a }; function c(g) { if (!e(g)) { throw TypeError("element must be a DOM node, got " + g) } } function b(g) { switch (typeof g) { case "string": g = { transition: g }; break; case "undefined": g = {}; break; case "object": break; default: throw TypeError("options must be an object if defined, got " + g) } switch (typeof g.transition) { case "string": if (!(g.transition in f) && (g.transition !== "instant")) { throw TypeError(g.transition + " is not a valid transition") } break; case "undefined": break; default: throw TypeError("transition must be a string if defined, got " + g.transition) } switch (typeof g.duration) { case "number": if (g.duration < 0) { throw TypeError("duration must be a non-negative integer, got " + g.duration) } break; case "undefined": break; default: throw TypeError("duration must be a number if defined, got " + g.duration) } switch (typeof g.easing) { case "string": if (!(g.easing in d) && (g.easing.substr(0, 13) !== "cubic-bezier(")) { throw TypeError(g.easing + " is not a valid easing") } break; case "undefined": break; default: throw TypeError("easing must be a string if defined, got " + g.easing) } return g } function a(g) { switch (typeof g) { case "undefined": g = function() {}; break; case "function": break; default: throw TypeError("callback must be a function if defined, got " + g) } return g } }(Swapper._isNode, Swapper._transitions, Swapper._easings); Swapper._swapper = function(k, w, f, e, A, x, g, h, j, D, l, q, m, s) { var a = "translate3d(0,0,0) scale(1)", E = "fade", z = "ease-in-out"; var p = (k.ios && (Math.floor(k.version) === 5)); function r(G, F, H, I) { q.element(G); q.element(F); if (typeof H === "function") { I = H; H = {} } H = q.options(H); I = q.callback(I); if (G._swapper) { throw Error("elem1 is currently being swapped") } else { if (F._swapper) { throw Error("elem2 is currently being swapped") } } if (!f(G, true)) { throw Error("elem1 must be in the DOM to be swapped") } G._swapper = true; F._swapper = true; x(F); o(G, F, H, function() { G._swapper = false; F._swapper = false; I() }) } function o(O, N, P, M) { if (P.transition === "instant") { A(N, O); x(O); M(); return } var L = D[P.transition || E], K = P.easing || z, J = P.duration || 300; if (K.substr(0, 13) !== "cubic-bezier(") { K = l[K] } A(N, O); var I = j(O), H = j(N), G = j(O, true), F = j(N, true); C(O, N, I, H); if (L[2]) { e(N, O) } N.style.opacity = "0"; u(O, N); setTimeout(function() { N.style.opacity = H.opacity; b(O, N, L); setTimeout(function() { n(O, N, J, K); setTimeout(function() { y(O, N, L); B(O, N, I, H, L, J, function() { x(O); t(O, N, J, K); setTimeout(function() { v(O, N, G, F, L); d(O, N, G, F); setTimeout(function() { c(O, N, G, F); M() }, 0) }, 0) }) }, 0) }, 50) }, 0) } function C(G, F, I, H) { var J = G.getBoundingClientRect(); if (I.display !== "none") { if (p) { F.style.position = "absolute" } else { F.style.position = "fixed" } F.style.top = J.top + "px"; F.style.left = J.left + "px" } F.style.height = H.height || I.height; F.style.width = H.width || I.width } function d(G, F, I, H) { F.style.position = H.position; F.style.top = H.top; F.style.left = H.left; F.style.height = H.height; F.style.width = H.width } function b(G, F, H) { g(G, a); g(F, H[0].transform || a); if (H[0].fade) { F.style.opacity = "0" } if (H[1].fade) { G.style.opacity = "1" } } function y(G, F, H) { g(G, H[1].transform || a); g(F, a); if (H[0].fade) { F.style.opacity = "1" } if (H[1].fade) { G.style.opacity = "0" } } function v(G, F, I, H, J) { g(G, ""); g(F, ""); if (J[0].fade) { F.style.opacity = H.opacity } if (J[1].fade) { G.style.opacity = I.opacity } } function n(G, F, H, J) { var I = "transform " + (H / 1000) + "s " + J + ",opacity " + (H / 1000) + "s " + J; h(G, I); h(F, I) } function t(G, F, H, I) { h(G, ""); h(F, "") } function u(G, F) { h(G, ""); h(F, "") } function c(G, F, I, H) { G.style["-webkit-transition"] = I["-webkit-transition"]; G.style["-moz-transition"] = I["-moz-transition"]; G.style["-ms-transition"] = I["-ms-transition"]; G.style["-o-transition"] = I["-o-transition"]; G.style.transition = I.transition; F.style["-webkit-transition"] = H["-webkit-transition"]; F.style["-moz-transition"] = H["-moz-transition"]; F.style["-ms-transition"] = H["-ms-transition"]; F.style["-o-transition"] = H["-o-transition"]; F.style.transition = H.transition } function i(F, G) { if (F.display === "none") { return false } if (G.fade) { return true } if (!G.transform) { return false } else { if (G.transform === a) { return false } else { return true } } } function B(Q, N, H, F, K, I, M) { var G; if (i(F, K[0])) { G = N; P() } else { if (i(H, K[1])) { G = Q; P() } else { setTimeout(L, I) } } function P() { G.addEventListener("webkitTransitionEnd", L, false); G.addEventListener("transitionend", L, false); G.addEventListener("oTransitionEnd", L, false); G.addEventListener("otransitionend", L, false); G.addEventListener("MSTransitionEnd", L, false); G.addEventListener("transitionend", L, false) } function O() { G.removeEventListener("webkitTransitionEnd", L); G.removeEventListener("transitionend", L); G.removeEventListener("oTransitionEnd", L); G.removeEventListener("otransitionend", L); G.removeEventListener("MSTransitionEnd", L); G.removeEventListener("transitionend", L) } var J = false; function L(R) { if (J || !R || !R.target || (R.target !== G)) { return } J = true; if (G) { O() } M() } } return r }(Swapper._os, Swapper._isNode, Swapper._isInDOM, Swapper._insertBefore, Swapper._insertAfter, Swapper._removeNode, Swapper._setTransform, Swapper._setTransition, Swapper._getStyles, Swapper._transitions, Swapper._easings, Swapper._validate, window, document); var Clickable = function(c, b) { function a() { a._enableClicking.apply(this, arguments) } a.touchable = function() { return a._os.touchable }; a.sticky = function() { a._enableStickyClick.apply(this, arguments) }; a.unsticky = function(d) { if (typeof d === "object" && d && typeof d._removeStickyClick === "function") { d._removeStickyClick() } }; if (b && b.fn) { b.fn.clickable = function(d) { this.each(function() { a._enableClicking(this, d) }); return this }; b.fn.stickyClick = function(d) { this.each(function() { a._enableStickyClick(this, d) }); return this }; b.fn.unstickyClick = function(d) { this.each(function() { a.unsticky(this) }); return this } } if (c && c.fn) { c.extend(c.fn, { clickable: function(d) { this.forEach(function(e) { a._enableClicking(e, d) }); return this }, stickyClick: function(d) { this.forEach(function(e) { a._enableStickyClick(e, d) }); return this }, unstickyClick: function(d) { this.forEach(function(e) { a.unsticky(this) }); return this } }) } return a }(window.Zepto, window.jQuery); Clickable._os = function(f, d) { var c, a, b; if (b = /\bCPU.*OS (\d+(_\d+)?)/i.exec(f)) { c = "ios"; a = b[1].replace("_", ".") } else { if (b = /\bAndroid (\d+(\.\d+)?)/.exec(f)) { c = "android"; a = b[1] } } var e = { name: c, version: a && d(a), touchable: !!c }; e[c] = true; return e }(navigator.userAgent, parseFloat); Clickable._trimString = function(a) { var b = /^\s+|\s+$/g; return function(c) { return a(c).replace(b, "") } }(String); Clickable._isDOMNode = function(a, b) { return function(d) { if (!d) { return false } try { return (d instanceof a) || (d instanceof b) } catch (c) {} if (typeof d !== "object") { return false } if (typeof d.nodeType !== "number") { return false } if (typeof d.nodeName !== "string") { return false } return true } }(Node, HTMLElement); Clickable._isInDOM = function() { return function(a) { while (a = a.parentNode) { if (a === document) { return true } } return false } }(); Clickable._bindEvents = function() { return function(c, b) { for (var a in b) { if (c.addEventListener) { c.addEventListener(a, b[a], false) } else { if (c.attachEvent) { c.attachEvent("on" + a, b[a]) } } } } }(); Clickable._unbindEvents = function() { return function(c, b) { for (var a in b) { if (c.removeEventListener) { c.removeEventListener(a, b[a]) } } } }(); Clickable._addClass = function() { return function(b, a) { b.className += " " + a } }(); Clickable._removeClass = function(a) { return function(c, b) { c.className = a(c.className.replace(new RegExp("\\b" + b + "\\b"), "")) } }(Clickable._trimString); Clickable._enableClicking = function(h, o, a, f, c, k, n) { var i = "active", m = "data-clickable-class", g = 40; var p = false, l = !!h.ios; function b(L, O) { if (!o(L)) { throw TypeError("element " + L + " must be a DOM element") } if (L._clickable) { return } L._clickable = true; switch (typeof O) { case "undefined": O = i; break; case "string": break; default: throw TypeError("active class " + O + " must be a string") } var E = false, q = false, G, F, J, K, s; L.setAttribute(m, O); L.style["-webkit-tap-highlight-color"] = "rgba(255,255,255,0)"; v(); return; function M(Q, R) { E = true; J = +new Date(); G = Q; F = R; K = j(L); if (K) { s = K.scrollTop; K.addEventListener("scroll", A, true) } } function I() { if (K) { K.removeEventListener("scroll", A) } K = null; s = null; G = null; F = null; E = false } function A() { B() } function P() { return E } function t() { k(L, O) } function r() { n(L, O) } function v() { f(L, { click: x }); if (!h.touchable) { f(L, { mousedown: C, mousemove: D, mouseout: D, mouseup: z }); return } if (h.ios) { f(L, { DOMNodeInsertedIntoDocument: N, DOMNodeRemovedFromDocument: y }); if (a(L)) { N() } } else { N() } } function N() { f(L, { touchstart: w, touchmove: H, touchcancel: B, touchend: u }) } function y() { c(L, { touchstart: w, touchmove: H, touchcancel: B, touchend: u }) } function x(Q) { Q = Q || window.event; if (!L.disabled && q) { q = false; setTimeout(function() { p = false }, 0) } else { if (Q.stopImmediatePropagation) { Q.stopImmediatePropagation() } Q.preventDefault(); Q.stopPropagation(); Q.cancelBubble = true; Q.returnValue = false; return false } } function C(Q) { q = false; if (L.disabled || !e(Q.target, L)) { Q.preventDefault(); I(); return } M(Q.clientX, Q.clientY); t() } function D(Q) { Q.preventDefault(); I(); q = false; r() } function z(Q) { if (L.disabled) { Q.preventDefault(); I(); q = false; return } if (!P()) { Q.preventDefault(); q = false } else { q = true } I(); r() } function w(Q) { q = false; if (p || L.disabled || (Q.touches.length !== 1) || !e(Q.target, L)) { I(); return } p = true; var R = Q.changedTouches[0]; M(R.clientX, R.clientY); if (K) { if (K._isScrolling || (s < 0) || (K.scrollHeight < s)) { I(); return } } var R = J; setTimeout(function() { if (P() && (R === J)) { t() } }, g) } function B(Q) { q = false; I(); if (Q) { p = false } if (L.disabled) { return } r() } function H(R) { var Q = document.elementFromPoint(R.touches[0].pageX, R.touches[0].pageY); if (L !== Q) { B(R) } } function u(V) { var R = P(), S = K, T = s, Q = G, W = F; B(); if (!R || L.disabled) { p = false; return } if (S) { if (S._isScrolling || (S.scrollTop !== T)) { return } } if (!V.stopImmediatePropagation) { q = true; return } var U = +new Date() - J; if (U > g) { q = true; d(L, Q, W) } else { t(); setTimeout(function() { r(); q = true; d(L, Q, W) }, 1) } } } function e(r, q) { do { if (r === q) { return true } else { if (r._clickable) { return false } } } while (r = r.parentNode); return false } function d(s, q, t) { var r = document.createEvent("MouseEvents"); r.initMouseEvent("click", true, true, window, 1, q || 0, t || 0, q || 0, t || 0, false, false, false, false, 0, null); s.dispatchEvent(r) } function j(q) { if (!h.ios || (h.version < 5)) { return } while (q = q.parentNode) { if (q._scrollable) { if (q._iScroll) { return } return q } } } return b }(Clickable._os, Clickable._isDOMNode, Clickable._isInDOM, Clickable._bindEvents, Clickable._unbindEvents, Clickable._addClass, Clickable._removeClass); Clickable._enableStickyClick = function(a, c, f) { var d = "data-clickable-class"; function e(i, h, g) { if (!c(i)) { throw TypeError("button must be a DOM element, got " + i) } switch (typeof h) { case "string": break; case "function": g = h; h = undefined; break; default: throw TypeError("button active class must be a string if defined, got " + h) } if (typeof g !== "function") { throw TypeError("sticky click handler must be a function, got " + g) } f(i, h); var j = b(i, g); i.addEventListener("click", j, false); if (i._removeStickyClick) { i._removeStickyClick = function() { i.removeEventListener("click", j) } } } function b(i, h) { var j = false, g = i.getAttribute(d); return function() { if (j) { return } j = true; var k = false, n; i.disabled = true; i.className += " " + g; try { n = h.call(i, m) } catch (l) { if (window.console && window.console.error) { if ((typeof l === "object") && l.stack) { window.console.error(l.stack) } else { window.console.error(l + "") } } m() } if (n === false) { m() } function m() { if (k) { return } k = true; j = false; if (i.disabled) { i.disabled = false; i.className = a(i.className.replace(new RegExp("\\b" + g + "\\b", "g"), "")) } } } } return e }(Clickable._trimString, Clickable._isDOMNode, Clickable._enableClicking); var iScroll = function(an, Z) { function ah(f) { if ("" === am) { return f } f = f.charAt(0).toUpperCase() + f.substr(1); return am + f } var ao = Math, P = Z.createElement("div").style, am; a: { for (var aj = ["t", "webkitT", "MozT", "msT", "OT"], Y, X = 0, k = aj.length; X < k; X++) { if (Y = aj[X] + "ransform", Y in P) { am = aj[X].substr(0, aj[X].length - 1); break a } } am = !1 } var ak = am ? "-" + am.toLowerCase() + "-" : "", ai = ah("transform"), g = ah("transitionProperty"), ad = ah("transitionDuration"), e = ah("transformOrigin"), d = ah("transitionTimingFunction"), i = ah("transitionDelay"), ag = /android/gi.test(navigator.appVersion), W = /iphone|ipad/gi.test(navigator.appVersion), aj = /hp-tablet/gi.test(navigator.appVersion), V = ah("perspective") in P, al = "ontouchstart" in an && !aj, T = !!am, c = ah("transition") in P, af = "onorientationchange" in an ? "orientationchange" : "resize", ac = al ? "touchstart" : "mousedown", U = al ? "touchmove" : "mousemove", S = al ? "touchend" : "mouseup", R = al ? "touchcancel" : "mouseup", ab = "Moz" == am ? "DOMMouseScroll" : "mousewheel", aa; aa = !1 === am ? !1 : { "": "transitionend", webkit: "webkitTransitionEnd", Moz: "transitionend", O: "oTransitionEnd", ms: "MSTransitionEnd" }[am]; var b = an.requestAnimationFrame || an.webkitRequestAnimationFrame || an.mozRequestAnimationFrame || an.oRequestAnimationFrame || an.msRequestAnimationFrame || function(f) { return setTimeout(f, 1) }, Q = an.cancelRequestAnimationFrame || an.webkitCancelAnimationFrame || an.webkitCancelRequestAnimationFrame || an.mozCancelRequestAnimationFrame || an.oCancelRequestAnimationFrame || an.msCancelRequestAnimationFrame || clearTimeout, ae = V ? " translateZ(0)" : "", aj = function(f, h) { var l = this, j; l.wrapper = "object" == typeof f ? f : Z.getElementById(f); l.wrapper.style.overflow = "hidden"; l.scroller = l.wrapper.children[0]; l.options = { hScroll: !0, vScroll: !0, x: 0, y: 0, bounce: !0, bounceLock: !1, momentum: !0, lockDirection: !0, useTransform: !0, useTransition: !1, topOffset: 0, checkDOMChanges: !1, handleClick: !0, hScrollbar: !0, vScrollbar: !0, fixedScrollbar: ag, hideScrollbar: W, fadeScrollbar: W && V, scrollbarClass: "", zoom: !1, zoomMin: 1, zoomMax: 4, doubleTapZoom: 2, wheelAction: "scroll", snap: !1, snapThreshold: 1, onRefresh: null, onBeforeScrollStart: function(m) { m.preventDefault() }, onScrollStart: null, onBeforeScrollMove: null, onScrollMove: null, onBeforeScrollEnd: null, onScrollEnd: null, onTouchEnd: null, onDestroy: null, onZoomStart: null, onZoom: null, onZoomEnd: null }; for (j in h) { l.options[j] = h[j] } l.x = l.options.x; l.y = l.options.y; l.options.useTransform = T && l.options.useTransform; l.options.hScrollbar = l.options.hScroll && l.options.hScrollbar; l.options.vScrollbar = l.options.vScroll && l.options.vScrollbar; l.options.zoom = l.options.useTransform && l.options.zoom; l.options.useTransition = c && l.options.useTransition; l.options.zoom && ag && (ae = ""); l.scroller.style[g] = l.options.useTransform ? ak + "transform" : "top left"; l.scroller.style[ad] = "0"; l.scroller.style[e] = "0 0"; l.options.useTransition && (l.scroller.style[d] = "cubic-bezier(0.33,0.66,0.66,1)"); l.options.useTransform ? l.scroller.style[ai] = "translate(" + l.x + "px," + l.y + "px)" + ae : l.scroller.style.cssText += ";position:absolute;top:" + l.y + "px;left:" + l.x + "px"; l.options.useTransition && (l.options.fixedScrollbar = !0); l.refresh(); l._bind(af, an); l._bind(ac); al || (l._bind("mouseout", l.wrapper), "none" != l.options.wheelAction && l._bind(ab)); l.options.checkDOMChanges && (l.checkDOMTime = setInterval(function() { l._checkDOMChanges() }, 500)) }; aj.prototype = { enabled: !0, x: 0, y: 0, steps: [], scale: 1, currPageX: 0, currPageY: 0, pagesX: [], pagesY: [], aniTime: null, wheelZoomCount: 0, handleEvent: function(f) { switch (f.type) { case ac: if (!al && 0 !== f.button) { break } this._start(f); break; case U: this._move(f); break; case S: case R: this._end(f); break; case af: this._resize(); break; case ab: this._wheel(f); break; case "mouseout": this._mouseout(f); break; case aa: this._transitionEnd(f) } }, _checkDOMChanges: function() { !this.moved && (!this.zoomed && !(this.animating || this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) && this.refresh() }, _scrollbar: function(f) { var h; this[f + "Scrollbar"] ? (this[f + "ScrollbarWrapper"] || (h = Z.createElement("div"), this.options.scrollbarClass ? h.className = this.options.scrollbarClass + f.toUpperCase() : h.style.cssText = "position:absolute;z-index:100;" + ("h" == f ? "height:7px;bottom:1px;left:2px;right:" + (this.vScrollbar ? "7" : "2") + "px" : "width:7px;bottom:" + (this.hScrollbar ? "7" : "2") + "px;top:2px;right:1px"), h.style.cssText += ";pointer-events:none;" + ak + "transition-property:opacity;" + ak + "transition-duration:" + (this.options.fadeScrollbar ? "350ms" : "0") + ";overflow:hidden;opacity:" + (this.options.hideScrollbar ? "0" : "1"), this.wrapper.appendChild(h), this[f + "ScrollbarWrapper"] = h, h = Z.createElement("div"), this.options.scrollbarClass || (h.style.cssText = "position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);" + ak + "background-clip:padding-box;" + ak + "box-sizing:border-box;" + ("h" == f ? "height:100%" : "width:100%") + ";" + ak + "border-radius:3px;border-radius:3px"), h.style.cssText += ";pointer-events:none;" + ak + "transition-property:" + ak + "transform;" + ak + "transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);" + ak + "transition-duration:0;" + ak + "transform: translate(0,0)" + ae, this.options.useTransition && (h.style.cssText += ";" + ak + "transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)"), this[f + "ScrollbarWrapper"].appendChild(h), this[f + "ScrollbarIndicator"] = h), "h" == f ? (this.hScrollbarSize = this.hScrollbarWrapper.clientWidth, this.hScrollbarIndicatorSize = ao.max(ao.round(this.hScrollbarSize * this.hScrollbarSize / this.scrollerW), 8), this.hScrollbarIndicator.style.width = this.hScrollbarIndicatorSize + "px", this.hScrollbarMaxScroll = this.hScrollbarSize - this.hScrollbarIndicatorSize, this.hScrollbarProp = this.hScrollbarMaxScroll / this.maxScrollX) : (this.vScrollbarSize = this.vScrollbarWrapper.clientHeight, this.vScrollbarIndicatorSize = ao.max(ao.round(this.vScrollbarSize * this.vScrollbarSize / this.scrollerH), 8), this.vScrollbarIndicator.style.height = this.vScrollbarIndicatorSize + "px", this.vScrollbarMaxScroll = this.vScrollbarSize - this.vScrollbarIndicatorSize, this.vScrollbarProp = this.vScrollbarMaxScroll / this.maxScrollY), this._scrollbarPos(f, !0)) : this[f + "ScrollbarWrapper"] && (T && (this[f + "ScrollbarIndicator"].style[ai] = ""), this[f + "ScrollbarWrapper"].parentNode.removeChild(this[f + "ScrollbarWrapper"]), this[f + "ScrollbarWrapper"] = null, this[f + "ScrollbarIndicator"] = null) }, _resize: function() { var f = this; setTimeout(function() { f.refresh() }, ag ? 200 : 0) }, _pos: function(f, h) { this.zoomed || (f = this.hScroll ? f : 0, h = this.vScroll ? h : 0, this.options.useTransform ? this.scroller.style[ai] = "translate(" + f + "px," + h + "px) scale(" + this.scale + ")" + ae : (f = ao.round(f), h = ao.round(h), this.scroller.style.left = f + "px", this.scroller.style.top = h + "px"), this.x = f, this.y = h, this._scrollbarPos("h"), this._scrollbarPos("v")) }, _scrollbarPos: function(f, h) { var j = "h" == f ? this.x : this.y; this[f + "Scrollbar"] && (j *= this[f + "ScrollbarProp"], 0 > j ? (this.options.fixedScrollbar || (j = this[f + "ScrollbarIndicatorSize"] + ao.round(3 * j), 8 > j && (j = 8), this[f + "ScrollbarIndicator"].style["h" == f ? "width" : "height"] = j + "px"), j = 0) : j > this[f + "ScrollbarMaxScroll"] && (this.options.fixedScrollbar ? j = this[f + "ScrollbarMaxScroll"] : (j = this[f + "ScrollbarIndicatorSize"] - ao.round(3 * (j - this[f + "ScrollbarMaxScroll"])), 8 > j && (j = 8), this[f + "ScrollbarIndicator"].style["h" == f ? "width" : "height"] = j + "px", j = this[f + "ScrollbarMaxScroll"] + (this[f + "ScrollbarIndicatorSize"] - j))), this[f + "ScrollbarWrapper"].style[i] = "0", this[f + "ScrollbarWrapper"].style.opacity = h && this.options.hideScrollbar ? "0" : "1", this[f + "ScrollbarIndicator"].style[ai] = "translate(" + ("h" == f ? j + "px,0)" : "0," + j + "px)") + ae) }, _start: function(f) { var h = al ? f.touches[0] : f, l, j; if (this.enabled) { this.options.onBeforeScrollStart && this.options.onBeforeScrollStart.call(this, f); (this.options.useTransition || this.options.zoom) && this._transitionTime(0); this.zoomed = this.animating = this.moved = !1; this.dirY = this.dirX = this.absDistY = this.absDistX = this.distY = this.distX = 0; this.options.zoom && (al && 1 < f.touches.length) && (j = ao.abs(f.touches[0].pageX - f.touches[1].pageX), l = ao.abs(f.touches[0].pageY - f.touches[1].pageY), this.touchesDistStart = ao.sqrt(j * j + l * l), this.originX = ao.abs(f.touches[0].pageX + f.touches[1].pageX - 2 * this.wrapperOffsetLeft) / 2 - this.x, this.originY = ao.abs(f.touches[0].pageY + f.touches[1].pageY - 2 * this.wrapperOffsetTop) / 2 - this.y, this.options.onZoomStart && this.options.onZoomStart.call(this, f)); if (this.options.momentum && (this.options.useTransform ? (l = getComputedStyle(this.scroller, null)[ai].replace(/[^0-9\-.,]/g, "").split(","), j = 1 * l[4], l = 1 * l[5]) : (j = 1 * getComputedStyle(this.scroller, null).left.replace(/[^0-9-]/g, ""), l = 1 * getComputedStyle(this.scroller, null).top.replace(/[^0-9-]/g, "")), j != this.x || l != this.y)) { this.options.useTransition ? this._unbind(aa) : Q(this.aniTime), this.steps = [], this._pos(j, l) } this.absStartX = this.x; this.absStartY = this.y; this.startX = this.x; this.startY = this.y; this.pointX = h.pageX; this.pointY = h.pageY; this.startTime = f.timeStamp || Date.now(); this.options.onScrollStart && this.options.onScrollStart.call(this, f); this._bind(U); this._bind(S); this._bind(R) } }, _move: function(f) { var h = al ? f.touches[0] : f, o = h.pageX - this.pointX, n = h.pageY - this.pointY, m = this.x + o, l = this.y + n, j = f.timeStamp || Date.now(); this.options.onBeforeScrollMove && this.options.onBeforeScrollMove.call(this, f); if (this.options.zoom && al && 1 < f.touches.length) { m = ao.abs(f.touches[0].pageX - f.touches[1].pageX), l = ao.abs(f.touches[0].pageY - f.touches[1].pageY), this.touchesDist = ao.sqrt(m * m + l * l), this.zoomed = !0, h = 1 / this.touchesDistStart * this.touchesDist * this.scale, h < this.options.zoomMin ? h = 0.5 * this.options.zoomMin * Math.pow(2, h / this.options.zoomMin) : h > this.options.zoomMax && (h = 2 * this.options.zoomMax * Math.pow(0.5, this.options.zoomMax / h)), this.lastScale = h / this.scale, m = this.originX - this.originX * this.lastScale + this.x, l = this.originY - this.originY * this.lastScale + this.y, this.scroller.style[ai] = "translate(" + m + "px," + l + "px) scale(" + h + ")" + ae, this.options.onZoom && this.options.onZoom.call(this, f) } else { this.pointX = h.pageX; this.pointY = h.pageY; if (0 < m || m < this.maxScrollX) { m = this.options.bounce ? this.x + o / 2 : 0 <= m || 0 <= this.maxScrollX ? 0 : this.maxScrollX } if (l > this.minScrollY || l < this.maxScrollY) { l = this.options.bounce ? this.y + n / 2 : l >= this.minScrollY || 0 <= this.maxScrollY ? this.minScrollY : this.maxScrollY } this.distX += o; this.distY += n; this.absDistX = ao.abs(this.distX); this.absDistY = ao.abs(this.distY); 6 > this.absDistX && 6 > this.absDistY || (this.options.lockDirection && (this.absDistX > this.absDistY + 5 ? (l = this.y, n = 0) : this.absDistY > this.absDistX + 5 && (m = this.x, o = 0)), this.moved = !0, this._pos(m, l), this.dirX = 0 < o ? -1 : 0 > o ? 1 : 0, this.dirY = 0 < n ? -1 : 0 > n ? 1 : 0, 300 < j - this.startTime && (this.startTime = j, this.startX = this.x, this.startY = this.y), this.options.onScrollMove && this.options.onScrollMove.call(this, f)) } }, _end: function(s) { if (!(al && 0 !== s.touches.length)) { var t = this, r = al ? s.changedTouches[0] : s, q, p, o = { dist: 0, time: 0 }, m = { dist: 0, time: 0 }, n = (s.timeStamp || Date.now()) - t.startTime, f = t.x, l = t.y; t._unbind(U); t._unbind(S); t._unbind(R); t.options.onBeforeScrollEnd && t.options.onBeforeScrollEnd.call(t, s); if (t.zoomed) { f = t.scale * t.lastScale, f = Math.max(t.options.zoomMin, f), f = Math.min(t.options.zoomMax, f), t.lastScale = f / t.scale, t.scale = f, t.x = t.originX - t.originX * t.lastScale + t.x, t.y = t.originY - t.originY * t.lastScale + t.y, t.scroller.style[ad] = "200ms", t.scroller.style[ai] = "translate(" + t.x + "px," + t.y + "px) scale(" + t.scale + ")" + ae, t.zoomed = !1, t.refresh(), t.options.onZoomEnd && t.options.onZoomEnd.call(t, s) } else { if (t.moved) { if (300 > n && t.options.momentum) { o = f ? t._momentum(f - t.startX, n, -t.x, t.scrollerW - t.wrapperW + t.x, t.options.bounce ? t.wrapperW : 0) : o; m = l ? t._momentum(l - t.startY, n, -t.y, 0 > t.maxScrollY ? t.scrollerH - t.wrapperH + t.y - t.minScrollY : 0, t.options.bounce ? t.wrapperH : 0) : m; f = t.x + o.dist; l = t.y + m.dist; if (0 < t.x && 0 < f || t.x < t.maxScrollX && f < t.maxScrollX) { o = { dist: 0, time: 0 } } if (t.y > t.minScrollY && l > t.minScrollY || t.y < t.maxScrollY && l < t.maxScrollY) { m = { dist: 0, time: 0 } } } o.dist || m.dist ? (o = ao.max(ao.max(o.time, m.time), 10), t.options.snap && (m = f - t.absStartX, n = l - t.absStartY, ao.abs(m) < t.options.snapThreshold && ao.abs(n) < t.options.snapThreshold ? t.scrollTo(t.absStartX, t.absStartY, 200) : (m = t._snap(f, l), f = m.x, l = m.y, o = ao.max(m.time, o))), t.scrollTo(ao.round(f), ao.round(l), o)) : t.options.snap ? (m = f - t.absStartX, n = l - t.absStartY, ao.abs(m) < t.options.snapThreshold && ao.abs(n) < t.options.snapThreshold ? t.scrollTo(t.absStartX, t.absStartY, 200) : (m = t._snap(t.x, t.y), (m.x != t.x || m.y != t.y) && t.scrollTo(m.x, m.y, m.time))) : t._resetPos(200) } else { al && (t.doubleTapTimer && t.options.zoom ? (clearTimeout(t.doubleTapTimer), t.doubleTapTimer = null, t.options.onZoomStart && t.options.onZoomStart.call(t, s), t.zoom(t.pointX, t.pointY, 1 == t.scale ? t.options.doubleTapZoom : 1), t.options.onZoomEnd && setTimeout(function() { t.options.onZoomEnd.call(t, s) }, 200)) : this.options.handleClick && (t.doubleTapTimer = setTimeout(function() { t.doubleTapTimer = null; for (q = r.target; 1 != q.nodeType;) { q = q.parentNode } "SELECT" != q.tagName && ("INPUT" != q.tagName && "TEXTAREA" != q.tagName) && (p = Z.createEvent("MouseEvents"), p.initMouseEvent("click", !0, !0, s.view, 1, r.screenX, r.screenY, r.clientX, r.clientY, s.ctrlKey, s.altKey, s.shiftKey, s.metaKey, 0, null), p._fake = !0, q.dispatchEvent(p)) }, t.options.zoom ? 250 : 0))), t._resetPos(200) } t.options.onTouchEnd && t.options.onTouchEnd.call(t, s) } } }, _resetPos: function(f) { var h = 0 <= this.x ? 0 : this.x < this.maxScrollX ? this.maxScrollX : this.x, j = this.y >= this.minScrollY || 0 < this.maxScrollY ? this.minScrollY : this.y < this.maxScrollY ? this.maxScrollY : this.y; if (h == this.x && j == this.y) { if (this.moved && (this.moved = !1, this.options.onScrollEnd && this.options.onScrollEnd.call(this)), this.hScrollbar && this.options.hideScrollbar && ("webkit" == am && (this.hScrollbarWrapper.style[i] = "300ms"), this.hScrollbarWrapper.style.opacity = "0"), this.vScrollbar && this.options.hideScrollbar) { "webkit" == am && (this.vScrollbarWrapper.style[i] = "300ms"), this.vScrollbarWrapper.style.opacity = "0" } } else { this.scrollTo(h, j, f || 0) } }, _wheel: function(f) { var h = this, l, j; if ("wheelDeltaX" in f) { l = f.wheelDeltaX / 12, j = f.wheelDeltaY / 12 } else { if ("wheelDelta" in f) { l = j = f.wheelDelta / 12 } else { if ("detail" in f) { l = j = 3 * -f.detail } else { return } } } if ("zoom" == h.options.wheelAction) { if (j = h.scale * Math.pow(2, 1 / 3 * (j ? j / Math.abs(j) : 0)), j < h.options.zoomMin && (j = h.options.zoomMin), j > h.options.zoomMax && (j = h.options.zoomMax), j != h.scale) { !h.wheelZoomCount && h.options.onZoomStart && h.options.onZoomStart.call(h, f), h.wheelZoomCount++, h.zoom(f.pageX, f.pageY, j, 400), setTimeout(function() { h.wheelZoomCount--; !h.wheelZoomCount && h.options.onZoomEnd && h.options.onZoomEnd.call(h, f) }, 400) } } else { l = h.x + l, j = h.y + j, 0 < l ? l = 0 : l < h.maxScrollX && (l = h.maxScrollX), j > h.minScrollY ? j = h.minScrollY : j < h.maxScrollY && (j = h.maxScrollY), 0 > h.maxScrollY && h.scrollTo(l, j, 0) } }, _mouseout: function(f) { var h = f.relatedTarget; if (h) { for (; h = h.parentNode;) { if (h == this.wrapper) { return } } } this._end(f) }, _transitionEnd: function(f) { f.target == this.scroller && (this._unbind(aa), this._startAni()) }, _startAni: function() { var f = this, h = f.x, o = f.y, n = Date.now(), m, l, j; f.animating || (f.steps.length ? (m = f.steps.shift(), m.x == h && m.y == o && (m.time = 0), f.animating = !0, f.moved = !0, f.options.useTransition) ? (f._transitionTime(m.time), f._pos(m.x, m.y), f.animating = !1, m.time ? f._bind(aa) : f._resetPos(0)) : (j = function() { var q = Date.now(), p; if (q >= n + m.time) { f._pos(m.x, m.y); f.animating = false; f.options.onAnimationEnd && f.options.onAnimationEnd.call(f); f._startAni() } else { q = (q - n) / m.time - 1; l = ao.sqrt(1 - q * q); q = (m.x - h) * l + h; p = (m.y - o) * l + o; f._pos(q, p); if (f.animating) { f.aniTime = b(j) } } }, j()) : f._resetPos(400)) }, _transitionTime: function(f) { f += "ms"; this.scroller.style[ad] = f; this.hScrollbar && (this.hScrollbarIndicator.style[ad] = f); this.vScrollbar && (this.vScrollbarIndicator.style[ad] = f) }, _momentum: function(f, h, n, m, l) { var h = ao.abs(f) / h, j = h * h / 0.0012; 0 < f && j > n ? (n += l / (6 / (0.0006 * (j / h))), h = h * n / j, j = n) : 0 > f && j > m && (m += l / (6 / (0.0006 * (j / h))), h = h * m / j, j = m); return { dist: j * (0 > f ? -1 : 1), time: ao.round(h / 0.0006) } }, _offset: function(f) { for (var h = -f.offsetLeft, j = -f.offsetTop; f = f.offsetParent;) { h -= f.offsetLeft, j -= f.offsetTop } f != this.wrapper && (h *= this.scale, j *= this.scale); return { left: h, top: j } }, _snap: function(f, h) { var m, l, j; j = this.pagesX.length - 1; m = 0; for (l = this.pagesX.length; m < l; m++) { if (f >= this.pagesX[m]) { j = m; break } } j == this.currPageX && (0 < j && 0 > this.dirX) && j--; f = this.pagesX[j]; l = (l = ao.abs(f - this.pagesX[this.currPageX])) ? 500 * (ao.abs(this.x - f) / l) : 0; this.currPageX = j; j = this.pagesY.length - 1; for (m = 0; m < j; m++) { if (h >= this.pagesY[m]) { j = m; break } } j == this.currPageY && (0 < j && 0 > this.dirY) && j--; h = this.pagesY[j]; m = (m = ao.abs(h - this.pagesY[this.currPageY])) ? 500 * (ao.abs(this.y - h) / m) : 0; this.currPageY = j; j = ao.round(ao.max(l, m)) || 200; return { x: f, y: h, time: j } }, _bind: function(f, h, j) { (h || this.scroller).addEventListener(f, this, !!j) }, _unbind: function(f, h, j) { (h || this.scroller).removeEventListener(f, this, !!j) }, destroy: function() { this.scroller.style[ai] = ""; this.vScrollbar = this.hScrollbar = !1; this._scrollbar("h"); this._scrollbar("v"); this._unbind(af, an); this._unbind(ac); this._unbind(U); this._unbind(S); this._unbind(R); this.options.hasTouch || (this._unbind("mouseout", this.wrapper), this._unbind(ab)); this.options.useTransition && this._unbind(aa); this.options.checkDOMChanges && clearInterval(this.checkDOMTime); this.options.onDestroy && this.options.onDestroy.call(this) }, refresh: function() { var f, h, l, j = 0; h = 0; this.scale < this.options.zoomMin && (this.scale = this.options.zoomMin); this.wrapperW = this.wrapper.clientWidth || 1; this.wrapperH = this.wrapper.clientHeight || 1; this.minScrollY = -this.options.topOffset || 0; this.scrollerW = ao.round(this.scroller.offsetWidth * this.scale); this.scrollerH = ao.round((this.scroller.offsetHeight + this.minScrollY) * this.scale); this.maxScrollX = this.wrapperW - this.scrollerW; this.maxScrollY = this.wrapperH - this.scrollerH + this.minScrollY; this.dirY = this.dirX = 0; this.options.onRefresh && this.options.onRefresh.call(this); this.hScroll = this.options.hScroll && 0 > this.maxScrollX; this.vScroll = this.options.vScroll && (!this.options.bounceLock && !this.hScroll || this.scrollerH > this.wrapperH); this.hScrollbar = this.hScroll && this.options.hScrollbar; this.vScrollbar = this.vScroll && this.options.vScrollbar && this.scrollerH > this.wrapperH; f = this._offset(this.wrapper); this.wrapperOffsetLeft = -f.left; this.wrapperOffsetTop = -f.top; if ("string" == typeof this.options.snap) { this.pagesX = []; this.pagesY = []; l = this.scroller.querySelectorAll(this.options.snap); f = 0; for (h = l.length; f < h; f++) { j = this._offset(l[f]), j.left += this.wrapperOffsetLeft, j.top += this.wrapperOffsetTop, this.pagesX[f] = j.left < this.maxScrollX ? this.maxScrollX : j.left * this.scale, this.pagesY[f] = j.top < this.maxScrollY ? this.maxScrollY : j.top * this.scale } } else { if (this.options.snap) { for (this.pagesX = []; j >= this.maxScrollX;) { this.pagesX[h] = j, j -= this.wrapperW, h++ } this.maxScrollX % this.wrapperW && (this.pagesX[this.pagesX.length] = this.maxScrollX - this.pagesX[this.pagesX.length - 1] + this.pagesX[this.pagesX.length - 1]); h = j = 0; for (this.pagesY = []; j >= this.maxScrollY;) { this.pagesY[h] = j, j -= this.wrapperH, h++ } this.maxScrollY % this.wrapperH && (this.pagesY[this.pagesY.length] = this.maxScrollY - this.pagesY[this.pagesY.length - 1] + this.pagesY[this.pagesY.length - 1]) } } this._scrollbar("h"); this._scrollbar("v"); this.zoomed || (this.scroller.style[ad] = "0", this._resetPos(200)) }, scrollTo: function(f, h, m, l) { var j = f; this.stop(); j.length || (j = [{ x: f, y: h, time: m, relative: l }]); f = 0; for (h = j.length; f < h; f++) { j[f].relative && (j[f].x = this.x - j[f].x, j[f].y = this.y - j[f].y), this.steps.push({ x: j[f].x, y: j[f].y, time: j[f].time || 0 }) } this._startAni() }, scrollToElement: function(f, h) { var j; if (f = f.nodeType ? f : this.scroller.querySelector(f)) { j = this._offset(f), j.left += this.wrapperOffsetLeft, j.top += this.wrapperOffsetTop, j.left = 0 < j.left ? 0 : j.left < this.maxScrollX ? this.maxScrollX : j.left, j.top = j.top > this.minScrollY ? this.minScrollY : j.top < this.maxScrollY ? this.maxScrollY : j.top, h = void 0 === h ? ao.max(2 * ao.abs(j.left), 2 * ao.abs(j.top)) : h, this.scrollTo(j.left, j.top, h) } }, scrollToPage: function(f, h, j) { j = void 0 === j ? 400 : j; this.options.onScrollStart && this.options.onScrollStart.call(this); if (this.options.snap) { f = "next" == f ? this.currPageX + 1 : "prev" == f ? this.currPageX - 1 : f, h = "next" == h ? this.currPageY + 1 : "prev" == h ? this.currPageY - 1 : h, f = 0 > f ? 0 : f > this.pagesX.length - 1 ? this.pagesX.length - 1 : f, h = 0 > h ? 0 : h > this.pagesY.length - 1 ? this.pagesY.length - 1 : h, this.currPageX = f, this.currPageY = h, f = this.pagesX[f], h = this.pagesY[h] } else { if (f *= -this.wrapperW, h *= -this.wrapperH, f < this.maxScrollX && (f = this.maxScrollX), h < this.maxScrollY) { h = this.maxScrollY } } this.scrollTo(f, h, j) }, disable: function() { this.stop(); this._resetPos(0); this.enabled = !1; this._unbind(U); this._unbind(S); this._unbind(R) }, enable: function() { this.enabled = !0 }, stop: function() { this.options.useTransition ? this._unbind(aa) : Q(this.aniTime); this.steps = []; this.animating = this.moved = !1 }, zoom: function(f, h, m, l) { var j = m / this.scale; this.options.useTransform && (this.zoomed = !0, l = void 0 === l ? 200 : l, f = f - this.wrapperOffsetLeft - this.x, h = h - this.wrapperOffsetTop - this.y, this.x = f - f * j + this.x, this.y = h - h * j + this.y, this.scale = m, this.refresh(), this.x = 0 < this.x ? 0 : this.x < this.maxScrollX ? this.maxScrollX : this.x, this.y = this.y > this.minScrollY ? this.minScrollY : this.y < this.maxScrollY ? this.maxScrollY : this.y, this.scroller.style[ad] = l + "ms", this.scroller.style[ai] = "translate(" + this.x + "px," + this.y + "px) scale(" + m + ")" + ae, this.zoomed = !1) }, isReady: function() { return !this.moved && !this.zoomed && !this.animating } }; P = null; return aj }(window, document); var Scrollable = function(h, g) { function b() { b._enableScrolling.apply(this, arguments) } b.node = function() { return b._getScrollableNode.apply(this, arguments) }; b.infinite = function() { return b._enableInfiniteScrolling.apply(this, arguments) }; if (h && h.fn) { h.extend(h.fn, { scrollable: function(i) { this.forEach(function(j) { b._enableScrolling(j, i) }); return this }, scrollableNode: function() { return h(this.map(function() { return b._getScrollableNode(this) })) }, scrollableInfinite: function(j, k) { var i; this.forEach(function(m) { var l = b._enableInfiniteScrolling(m, j, k); if (!i) { i = l } }); return i } }); var d = h.fn.scrollTop, f = h.fn.scrollLeft; h.fn.scrollTop = function(k) { if (typeof k === "undefined") { var i = this[0], j = b._isDOMNode(i); if (j && i._scrollTop) { return i._scrollTop() } else { if (d) { return d.apply(this, arguments) } else { if (j) { return i.scrollTop } else { return null } } } } this.forEach(function(l) { var m = b._isDOMNode(l); if (m && l._scrollTop) { l._scrollTop(k) } else { if (d) { d.call(h(l), k) } else { if (m) { l.scrollTop = k } } } }); return this }; h.fn.scrollLeft = function(k) { if (typeof k === "undefined") { var i = this[0], j = b._isDOMNode(i); if (j && i._scrollLeft) { return i._scrollLeft() } else { if (d) { return f.apply(this, arguments) } else { if (j) { return i.scrollLeft } else { return null } } } } this.forEach(function(l) { var m = b._isDOMNode(l); if (m && l._scrollLeft) { l._scrollLeft(k) } else { if (f) { f.call(h(l), k) } else { if (m) { l.scrollLeft = k } } } }); return this } } if (g && g.fn) { g.fn.scrollable = function(i) { this.each(function() { b._enableScrolling(this, i) }); return this }; g.fn.scrollableNode = function() { return g(this.map(function() { return b._getScrollableNode(this) })) }; g.fn.scrollableInfinite = function(j, k) { var i; this.each(function() { var l = b._enableInfiniteScrolling(this, j, k); if (!i) { i = l } }); return i }; var c = g.fn.scrollTop, e = g.fn.scrollLeft; g.fn.scrollTop = function(j) { if (typeof j === "undefined") { var i = this[0]; if (b._isDOMNode(i) && i._scrollTop) { return i._scrollTop() } else { return c.apply(this, arguments) } } this.each(function() { if (b._isDOMNode(this) && this._scrollTop) { this._scrollTop(j) } else { c.call(g(this), j) } }); return this }; g.fn.scrollLeft = function(j) { if (typeof j === "undefined") { var i = this[0]; if (b._isDOMNode(i) && i._scrollLeft) { return i._scrollLeft() } else { return e.apply(this, arguments) } } this.each(function() { if (b._isDOMNode(this) && this._scrollLeft) { this._scrollLeft(j) } else { e.call(g(this), j) } }); return this } } return b }(window.Zepto, window.jQuery); Scrollable._os = function(g, e) { var d, b, c; if (c = /\bCPU.*OS (\d+(_\d+)?)/i.exec(g)) { d = "ios"; b = c[1].replace("_", ".") } else { if (c = /\bAndroid (\d+(\.\d+)?)/.exec(g)) { d = "android"; b = c[1] } } var f = { name: d, version: b && e(b), mobile: !!d }; f[d] = true; return f }(navigator.userAgent, parseFloat); Scrollable._isArray = function(b) { return function(c) { if (b) { return b(c) } else { return Object.prototype.toString.call(c) !== "[object Array]" } } }(Array.isArray); Scrollable._isDOMNode = function(b, c) { return function(e) { if (!e) { return false } try { return (e instanceof b) || (e instanceof c) } catch (d) {} if (typeof e !== "object") { return false } if (typeof e.nodeType !== "number") { return false } if (typeof e.nodeName !== "string") { return false } return true } }(Node, HTMLElement); Scrollable._isjQueryElem = function(b) { if (typeof b !== "object" || b === null) { return false } else { return (b.val && b.addClass && b.css && b.html && b.show) } }; Scrollable._findInArray = function(b) { return function(d, f, g) { if (b) { return b.call(d, f, g) } for (var e = g || 0, c = d.length; e < c; e++) { if ((e in d) && (d[e] === f)) { return e } } return -1 } }(Array.prototype.indexOf); Scrollable._forEachInArray = function(b) { return function(d, g, e) { if (b) { return b.call(d, g, e) } for (var f = 0, c = d.length; f < c; f++) { if (f in d) { g.call(e, d[f], f, d) } } } }(Array.prototype.forEach); Scrollable._onReady = function(c, d, i) { var h = [], g = false; e(f); return function(j) { if (g) { j() } else { h.push(j) } }; function f() { if (g) { return } g = true; i(h, function(j) { setTimeout(j, 0) }) } function b(k) { try { c.documentElement.doScroll("left") } catch (j) { setTimeout(function() { b(k) }, 1); return } k() } function e(l) { if (c.readyState === "complete") { setTimeout(l, 0); return } if (c.addEventListener) { c.addEventListener("DOMContentLoaded", l, false); d.addEventListener("load", l, false) } else { if (c.attachEvent) { c.attachEvent("onreadystatechange", l); d.attachEvent("onload", l); var j = false; try { j = (d.frameElement === null) } catch (k) {} if (c.documentElement.doScroll && j) { setTimeout(function() { b(l) }, 0) } } } } }(document, window, Scrollable._forEachInArray); Scrollable._scrollWatcher = function(b) { return c; function c(i) { var j = false, e = false, l = i.scrollTop; i.addEventListener("touchstart", h); i.addEventListener("touchmove", d); i.addEventListener("touchcancel", g); i.addEventListener("touchend", o); i.addEventListener("scroll", k); n(); i._resetScrolling = f; return; function n() { i._isScrolling = (e || j) } function f() { e = false; j = false; n() } function m(r, q, p) { if ((r.touches.length <= q) && ((typeof p === "undefined") || (r.changedTouches.length <= p))) { return false } r.preventDefault(); f(); return true } function h(p) { if (m(p, 1)) { return } f() } function d(p) { if (m(p, 1)) { return } j = true; l = i.scrollTop; n() } function g(p) { if (m(p, 0, 1)) { return } f() } function o(p) { if (m(p, 0, 1)) { return } var q; if (j) { q = Math.abs(i.scrollTop - l); if (q > 5) { e = true } j = false; n() } } function k() { if (!j && e) { f() } } } }(Scrollable._os); Scrollable._enableScrolling = function(f, o, k, e, d, p, m, n) { var j = i(); return q; function i() { if ((f.ios && (f.version >= 5)) || (f.android && (f.version >= 4))) { return true } else { return false } } function q(t, s) { if (!o(t)) { throw t + " is not a DOM element" } if (t._scrollable) { return } t._scrollable = true; var r; t._scrollTop = function(u, v) { if (typeof u === "undefined") { return r ? Math.max(parseInt(-r.y), 0) : t.scrollTop } if (!r && (!f.mobile || j)) { t.scrollTop = u; v && v(); return } k(function() { r.scrollTo(r.x, Math.min(-u, 0), 1); v && v() }) }; t._scrollLeft = function(u) { if (typeof u === "undefined") { return r ? Math.max(parseInt(-r.x), 0) : t.scrollLeft } if (!r && (!f.mobile || j)) { t.scrollLeft = u; return } k(function() { r.scrollTo(Math.min(-u, 0), r.y, 1) }) }; t.style.overflow = "scroll"; if (!s) { if (!f.mobile) { return } if (j) { t.style["-webkit-overflow-scrolling"] = "touch"; if (f.ios) { d(t) } return } } c(t, function(u) { r = u }) } function c(s, t) { s._iScroll = true; l(s); var r = g(s); k(function() { var u = new p(s, { checkDOMChanges: true, useTransform: true, useTransition: true, hScrollbar: false, vScrollbar: false, bounce: !!f.ios, onScrollMove: r, onBeforeScrollEnd: r, onScrollEnd: function() { s._iScrolling = false; r() }, onBeforeScrollStart: h, onScrollStart: function() { s._iScrolling = true } }); s._iScroll = u; t(u) }) } function l(s) { var t = n.createElement("div"), r = Array.prototype.slice.call(s.childNodes || []); e(r, function(v) { var u = s.removeChild(v); t.appendChild(u) }); s.appendChild(t); s.style.position = "relative"; t.style["min-height"] = "100%"; t.style["min-width"] = "100%" } function g(s) { var r, t; return function() { var v = s._scrollTop(), u = s._scrollLeft(); if ((v === r) && (u === t)) { return } r = v; t = u; b(s) } } function b(s) { if (s.dispatchEvent) { var r = n.createEvent("MouseEvents"); r.initMouseEvent("scroll", false, false, m, 0, 0, 0, 0, 0, false, false, false, false, 0, null); s.dispatchEvent(r) } } function h(s) { var r = s.target; while (r.nodeType !== 1) { r = r.parentNode } if ((r.tagName !== "SELECT") && (r.tagName !== "INPUT") && (r.tagName !== "TEXTAREA")) { s.preventDefault() } } }(Scrollable._os, Scrollable._isDOMNode, Scrollable._onReady, Scrollable._forEachInArray, Scrollable._scrollWatcher, iScroll, window, document); Scrollable._getScrollableNode = function(b) { return function(c) { if (b(c) && c._iScroll) { return c.childNodes[0] } else { return c } } }(Scrollable._isDOMNode); Scrollable._enableInfiniteScrolling = function(g, l, f, h, i, m, d, n) { var e = 320; return j; function j(J, q, y) { if (f(J)) { if (J.length) { var E = J.length - 1; for (var F = 0; F < E; F++) { j(J[F], q, y) } return j(J[E], q, y) } else { return } } if (!l(J)) { throw J + " is not a DOM element" } if (!y && typeof q === "function") { y = q; q = {} } if (y) { if (q.downGenerator) { throw Error("Two downGenerator functions specified") } q.downGenerator = y } if ((typeof q !== "object") || (q === null)) { throw TypeError("options must be an object if defined, got " + q) } if (!q.downGenerator && !q.upGenerator) { throw Error("No generators specified. What are you even scrolling?") } if (typeof q.autoStart === "undefined") { q.autoStart = true } if (q.downGenerator && typeof q.downGenerator !== "function") { throw "downGenerator " + downGenerator + " is not a function" } if (q.upGenerator && typeof q.upGenerator !== "function") { throw "upGenerator " + upGenerator + " is not a function" } if (q.scroller && !l(q.scroller)) { throw TypeError("options.scroller must be a DOM node, got " + q.scroller) } var I = q.scroller || b(J), t = q.loading, s = q.triggerRadius, x = false, z = !q.upGenerator, G = !q.downGenerator, B = false, L = false, C, K, w; if (f(I)) { I = I[0] } if (f(t)) { t = t[0] } if (t === null) { t = undefined } if (typeof t !== "undefined") { if (q.downGenerator) { C = c([t])[0]; if (q.downGenerator) { K = C.cloneNode(true) } } else { K = c([t])[0] } } if (s === null) { s = undefined } switch (typeof s) { case "undefined": s = e; case "number": break; default: throw TypeError("trigger radius must be a number if defined, got " + s) } if (!I) { m(J); I = J } if (C) { d(J).appendChild(C) } D(); if (q.autoStart) { A() } var H = { layout: A, forceLayout: v, isEnabled: M, enable: D, disable: u, destroy: p }; if (!I._infinites) { I._infinites = [] } I._infinites.push(H); return H; function M() { return x } function D() { if (x) { return } if (B) { throw Error("cannot enable infinite scroller that has been destroyed") } x = true; I.addEventListener("scroll", A, false) } function u() { if (!x) { return } x = false; I.removeEventListener("scroll", A) } function A() { if (!x || L || B) { return } var O = o(I, s); if (!O) { return } var N = (O === "up"); if (N && (J._isScrolling || J._iScrolling)) { if (w) { clearTimeout(w) } w = setTimeout(function() { A() }, 100); return } L = true; r(N, function(P) { L = false; if (P) { A() } else { p(N) } }) } function v(N) { if (!x || B || L) { return } L = true; if (typeof N === "undefined") { N = !q.downGenerator } r(N, function(O) { L = false; if (O) { A() } else { p(N) } }) } function r(Q, S) { var R = Q ? q.upGenerator : q.downGenerator; var N = R(O); if (typeof N !== "undefined") { O(N) } function O(U, X) { if (B || (z && Q) || (G && !Q)) { return } var aa = Q ? K : C; var T = U && U.length && !X; if (U) { if (!h(U) && !f(U)) { U = [U] } U = c(U); var Y = d(J); var W = I.scrollHeight; i(U, function(ab) { P(Y, ab) }); if (aa) { P(Y, aa) } var V = I.scrollHeight; if (Q) { var Z = V - W; I._scrollTop(I._scrollTop() + Z, function() { if (!!g.ios && !I._iScroll) { k(U) } S(T) }) } else { S(T) } } else { S(T) } } function P(U, T) { if (Q) { U.insertBefore(T, U.firstChild) } else { U.appendChild(T) } } } function p(N) { if (B) { return } if (N) { z = true; if (K && K.parentNode) { K.parentNode.removeChild(K) } } else { G = true; if (C && C.parentNode) { C.parentNode.removeChild(C) } } B = (G || !q.downGenerator) && (z || !q.upGenerator); if (B) { u() } } function o(O, N) { var R = O; while (R !== document.documentElement) { if (R.parentNode) { R = R.parentNode } else { return false } } var P = O.clientHeight, S = (O._scrollTop ? O._scrollTop() : O.scrollTop), Q = O.scrollHeight; if (!G && Q - S - P <= N) { return "down" } else { if (!z && S < N) { return "up" } else { return false } } } } function b(o) { do { if (o._scrollable) { return o } o = o.parentNode } while (o) } function c(o) { var p = []; i(o, function(q) { switch (typeof q) { case "undefined": return; case "string": var r = document.createElement("div"); r.innerHTML = q; if (r.childNodes) { i(c(r.childNodes), function(s) { p.push(s) }) } return; case "object": if (q === null) { return } else { if (l(q)) { p.push(q); return } else { if (f(q)) { i(q, function(s) { p.push(s) }); return } } } default: throw TypeError("expected an element, got " + q) } }); return p } function k(o) { i(o, function(q) { var p = q.style.webkitTransform; q.style.webkitTransform = "translate3d(0,0,0)"; setTimeout(function() { q.style.webkitTransform = p }, 0) }) } }(Scrollable._os, Scrollable._isDOMNode, Scrollable._isjQueryElem, Scrollable._isArray, Scrollable._forEachInArray, Scrollable._enableScrolling, Scrollable._getScrollableNode, window.jQuery); window.App = function(OldApp) { var App = { noConflict: noConflict }; return App; function noConflict() { if (window.App === App) { window.App = OldApp; } return App; } }(window.App); App._Utils = function(window, document, App) { var query = function(queryString) { var re = /([^&=]+)=([^&]+)/g, decodedSpace = /\+/g; var result = {}, m, key, value; if (queryString) { queryString = queryString.replace(decodedSpace, '%20'); while ((m = re.exec(queryString))) { key = decodeURIComponent(m[1]); value = decodeURIComponent(m[2]); result[key] = value; } } return result; }(window.location.href.split('?')[1]); var os = function(userAgent) { var faked = false, name, version, match; if (query['_app_platform'] === 'android') { faked = true; name = 'android'; version = '5.0'; } else if (query['_app_platform'] === 'ios') { faked = true; name = 'ios'; version = '8.1'; } else if (match = /\bCPU.*OS (\d+(_\d+)?)/i.exec(userAgent)) { name = 'ios'; version = match[1].replace('_', '.'); } else if (match = /\bAndroid (\d+(\.\d+)?)/.exec(userAgent)) { name = 'android'; version = match[1]; } else { if (typeof process != 'undefined' && process.versions) { name = 'node-webkit'; version = process.versions['node-webkit']; } else name = 'unknown'; } var data = { faked: faked, name: name, versionString: version, version: version && parseFloat(version) }; data[name] = true; if (data.ios) { document.body.className += ' app-ios app-ios-' + parseInt(version); } else if (data.android) { document.body.className += ' app-android app-android-' + parseInt(version); } if (data.faked || !data.ios) { document.body.className += ' app-no-scrollbar'; } return data; }(navigator.userAgent); var forEach = function(forEach) { if (forEach) { return function(arr, callback, self) { return forEach.call(arr, callback, self); }; } else { return function(arr, callback, self) { for (var i = 0, len = arr.length; i < len; i++) { if (i in arr) { callback.call(self, arr[i], i, arr); } } }; } }(Array.prototype.forEach); function isArray(arr) { if (Array.isArray) { return Array.isArray(arr); } else { return Object.prototype.toString.call(arr) !== '[object Array]'; } } function isNode(elem) { if (!elem) { return false; } try { return (elem instanceof Node) || (elem instanceof HTMLElement); } catch (err) {} if (typeof elem !== 'object') { return false; } if (typeof elem.nodeType !== 'number') { return false; } if (typeof elem.nodeName !== 'string') { return false; } return true; } function isjQueryElem($elem) { if (typeof $elem !== 'object' || $elem === null) { return false; } else { return ($elem.val && $elem.addClass && $elem.css && $elem.html && $elem.show); } } function onReady(func) { if (document.readyState === 'complete') { setTimeout(function() { func(); }, 0); return; } window.addEventListener('load', runCallback, false); function runCallback() { window.removeEventListener('load', runCallback); setTimeout(function() { func(); }, 0); } } var queueAnimation = function() { var animationQueue; return queueAnimation; function queueAnimation(func) { if (animationQueue) { animationQueue.push(func); } else { animationQueue = [func]; foregroundFlush(); } } function foregroundFlush() { if (typeof kik === 'object' && typeof kik.browser === 'object' && kik.browser.background && typeof kik.browser.once === 'function') { kik.browser.once('foreground', flushAnimations); } else { flushAnimations(); } } function flushAnimations() { var anim = animationQueue.shift(); if (anim) { onReady(function() { var unlocked = false; function unlock() { // prevent unlocking mult. times if (unlocked) { return; } unlocked = true; setTimeout(foregroundFlush, 0); } anim(unlock); }); } else { animationQueue = null; } } }(); function setTransform(elem, transform) { elem.style['-webkit-transform'] = transform; elem.style['-moz-transform'] = transform; elem.style['-ms-transform'] = transform; elem.style['-o-transform'] = transform; elem.style['transform'] = transform; } function setTransition(elem, transition) { if (transition) { elem.style['-webkit-transition'] = '-webkit-' + transition; elem.style['-moz-transition'] = '-moz-' + transition; elem.style['-ms-transition'] = '-ms-' + transition; elem.style['-o-transition'] = '-o-' + transition; elem.style['transition'] = transition; } else { elem.style['-webkit-transition'] = ''; elem.style['-moz-transition'] = ''; elem.style['-ms-transition'] = ''; elem.style['-o-transition'] = ''; elem.style['transition'] = ''; } } function getStyles(elem, notComputed) { var styles; if (notComputed) { styles = elem.style; } else { styles = document.defaultView.getComputedStyle(elem, null); } return { display: styles.display, opacity: styles.opacity, paddingRight: styles.paddingRight, paddingLeft: styles.paddingLeft, marginRight: styles.marginRight, marginLeft: styles.marginLeft, borderRightWidth: styles.borderRightWidth, borderLeftWidth: styles.borderLeftWidth, top: styles.top, left: styles.left, height: styles.height, width: styles.width, position: styles.position }; } function isVisible(elem) { var styles = getStyles(elem); return (styles.display !== 'none' && styles.opacity !== '0'); } // this is tuned for use with the iOS transition // be careful if using this elsewhere function transitionElems(transitions, timeout, easing, callback) { if (typeof transitions.length !== 'number') { transitions = [transitions]; } var opacities = transitions.map(function(transition) { return transition.elem.style.opacity; }); setInitialStyles(function() { animateElems(function() { restoreStyles(function() { callback(); }); }); }); function setInitialStyles(callback) { forEach(transitions, function(transition) { if (typeof transition.transitionStart !== 'undefined') { setTransform(transition.elem, transition.transitionStart); } if (typeof transition.opacityStart !== 'undefined') { transition.elem.style.opacity = transition.opacityStart + ''; } }); setTimeout(function() { forEach(transitions, function(transition) { var e = transition.easing || easing, transitionString = 'transform ' + (timeout / 1000) + 's ' + e + ', opacity ' + (timeout / 1000) + 's ' + e; setTransition(transition.elem, transitionString); }); setTimeout(callback, 0); }, 0); } function animateElems(callback) { forEach(transitions, function(transition) { if (typeof transition.transitionEnd !== 'undefined') { setTransform(transition.elem, transition.transitionEnd); } if (typeof transition.opacityEnd !== 'undefined') { transition.elem.style.opacity = transition.opacityEnd + ''; } }); var lastTransition = transitions[transitions.length - 1]; lastTransition.elem.addEventListener('webkitTransitionEnd', transitionFinished, false); lastTransition.elem.addEventListener('transitionend', transitionFinished, false); lastTransition.elem.addEventListener('onTransitionEnd', transitionFinished, false); lastTransition.elem.addEventListener('ontransitionend', transitionFinished, false); lastTransition.elem.addEventListener('MSTransitionEnd', transitionFinished, false); lastTransition.elem.addEventListener('transitionend', transitionFinished, false); var done = false; function transitionFinished(e) { if (done || (e.target !== lastTransition.elem)) { return; } done = true; forEach(transitions, function(transition) { lastTransition.elem.removeEventListener('webkitTransitionEnd', transitionFinished); lastTransition.elem.removeEventListener('transitionend', transitionFinished); lastTransition.elem.removeEventListener('onTransitionEnd', transitionFinished); lastTransition.elem.removeEventListener('ontransitionend', transitionFinished); lastTransition.elem.removeEventListener('MSTransitionEnd', transitionFinished); lastTransition.elem.removeEventListener('transitionend', transitionFinished); }); callback(); } } function restoreStyles(callback) { forEach(transitions, function(transition) { setTransition(transition.elem, ''); }); setTimeout(function() { forEach(transitions, function(transition, i) { setTransform(transition.elem, ''); transition.elem.style.opacity = opacities[i]; }); callback(); }, 0); } } App.platform = os.name; App.platformVersion = os.version; App.queue = queueAnimation; return { query: query, os: os, ready: onReady, forEach: forEach, isArray: isArray, isNode: isNode, isjQueryElem: isjQueryElem, setTransform: setTransform, setTransition: setTransition, animate: transitionElems, getStyles: getStyles, isVisible: isVisible }; }(window, document, App); App._Events = function(Utils) { var APPJS_EVENTS_VAR = '__appjsCustomEventing'; var hasCustomEvents = supportsCustomEventing(); return { init: setupCustomEventing, fire: fireEvent }; function supportsCustomEventing() { try { var elem = document.createElement('div'), evt = document.createEvent('CustomEvent'); evt.initEvent('fooBarFace', false, true); elem.dispatchEvent(evt); return true; } catch (err) { return false; } } function setupCustomEventing(elem, names) { if (hasCustomEvents) { return; } if (elem[APPJS_EVENTS_VAR]) { Utils.forEach(names, elem[APPJS_EVENTS_VAR].addEventType); return; } elem[APPJS_EVENTS_VAR] = { fire: fireElemEvent, addEventType: addEventType, addEventListener: elem.addEventListener, removeEventListener: elem.removeEventListener }; var listeners = {}; Utils.forEach(names, function(name) { listeners[name] = []; }); function addEventType(name) { if (names.indexOf(name) !== -1) { return; } names.push(name); listeners[name] = []; } function fireElemEvent(name) { if (names.indexOf(name) === -1) { return false; } var prevented = false, evt = { preventDefault: function() { prevented = true } }; Utils.forEach(listeners[name], function(listener) { setTimeout(function() { if (listener.call(elem, evt) === false) { prevented = true; } }, 0); }); return !prevented; } elem.addEventListener = function(name, listener) { if (names.indexOf(name) === -1) { elem[APPJS_EVENTS_VAR].addEventListener.apply(this, arguments); return; } var eventListeners = listeners[name]; if (eventListeners.indexOf(listener) === -1) { eventListeners.push(listener); } }; elem.removeEventListener = function(name, listener) { if (names.indexOf(name) === -1) { elem[APPJS_EVENTS_VAR].removeEventListener.apply(this, arguments); return; } var eventListeners = listeners[name], index = eventListeners.indexOf(listener); if (index !== -1) { eventListeners.splice(index, 1); } }; } function fireEvent(elem, eventName) { if (elem[APPJS_EVENTS_VAR]) { return elem[APPJS_EVENTS_VAR].fire(eventName); } else { var evt = document.createEvent('CustomEvent'); evt.initEvent(eventName, false, true); return elem.dispatchEvent(evt); } } }(App._Utils); App._Metrics = function(window, App) { var analyticsEnabled = false; App.enableGoogleAnalytics = function() { enableGoogleAnalytics(); }; return { watchPage: watchPage }; function enableGoogleAnalytics() { analyticsEnabled = true; } function addPageView(pageName, pageID) { if (!analyticsEnabled) { return; } var pathname = '/' + pageName; if (typeof pageID !== 'undefined') { pathname += '/' + pageID; } if (typeof window.ga === 'function') { window.ga('send', 'pageview', pathname); return; } if (!window._gaq) { window._gaq = []; } if (typeof window._gaq.push === 'function') { window._gaq.push([ '_trackPageview', pathname ]); } } function watchPage(page, pageName, pageArgs) { var data; if ((typeof pageArgs === 'object') && (typeof pageArgs.id !== 'undefined')) { data = pageArgs.id + ''; } page.addEventListener('appShow', function() { addPageView(pageName, data); }, false); } }(window, App); App._Dialog = function(window, document, Clickable, App, Utils) { var DIALOG_INDICATOR_CLASS = 'app-dialog-visible'; var currentCallback, dialogQueue; App.dialog = function(options, callback) { if ((typeof options !== 'object') || (options === null)) { throw TypeError('dialog options must be an object, got ' + options); } switch (typeof options.text) { case 'undefined': case 'string': break; default: if (!Utils.isNode(options.text)) { throw TypeError('dialog text must be a string if defined, got ' + options.text); } } for (var key in options) { if ((key === 'theme') || (key === 'title') || (key.substr(key.length - 6) === 'Button')) { switch (typeof options[key]) { case 'undefined': case 'string': break; default: throw TypeError('dialog button (' + key + ') must be a string if defined, got ' + options[key]); } } } switch (typeof callback) { case 'undefined': callback = function() {}; case 'function': break; default: throw TypeError('callback must be a function if defined, got ' + callback); } return showDialog(options, callback); }; App.dialog.close = function(status) { return closeDialog(status || false); }; App.dialog.status = function() { return hasDialog(); }; return App.dialog; function createDialog(options, callback) { var dialogContainer = document.createElement('div'); dialogContainer.className += ' app-dialog-container'; if (!Utils.os.android || (Utils.os.version >= 4)) { dialogContainer.addEventListener('touchstart', function(e) { if (e.target === dialogContainer) { e.preventDefault(); } }, false); } var dialog = document.createElement('div'); dialog.className = 'app-dialog'; if (options.theme) { dialog.className += ' ' + options.theme; } dialogContainer.appendChild(dialog); if (options.title) { var title = document.createElement('div'); title.className = 'title'; title.textContent = options.title; dialog.appendChild(title); } if (options.text || options.rawText) { var text = document.createElement('div'); text.className = 'text'; if (Utils.isNode(options.text)) { text.appendChild(options.text); } else if (options.rawText) { text.innerHTML = options.rawText; } else { text.textContent = options.text; } dialog.appendChild(text); } if (options.rawHTML) { dialog.appendChild(options.rawHTML); } if (options.okButton) { var button = document.createElement('div'); button.className = 'button ok last'; if (!options.cancelButton) { button.className += ' first'; } button.setAttribute('data-button', 'ok'); button.textContent = options.okButton; Clickable(button); button.addEventListener('click', handleChoice, false); dialog.appendChild(button); } if (options.cancelButton) { var button = document.createElement('div'); button.className = 'button cancel first'; if (!options.okButton) { button.className += ' last'; } button.setAttribute('data-button', 'cancel'); button.textContent = options.cancelButton; Clickable(button); button.addEventListener('click', handleChoice, false); dialog.appendChild(button); } function handleChoice() { var buttonName = this.getAttribute('data-button'); if (buttonName === 'cancel') { buttonName = false; } callback(buttonName); } return dialogContainer; } function showDialog(options, callback, force) { if (dialogQueue && !force) { dialogQueue.push([options, callback]); return; } dialogQueue = dialogQueue || []; var dialogLock = false, dialog = createDialog(options, dialogClosed), innerDialog = dialog.firstChild; currentCallback = dialogClosed; Utils.ready(function() { document.body.appendChild(dialog); setTimeout(function() { dialog.className += ' enabled'; document.body.className += ' ' + DIALOG_INDICATOR_CLASS; }, 50); }); function dialogClosed(status) { if (dialogLock) { return; } dialogLock = true; currentCallback = null; dialog.className = dialog.className.replace(/\benabled\b/g, '') + ' closing'; if (status) { dialog.className += ' closing-success'; } else { dialog.className += ' closing-fail'; } document.body.className = document.body.className.replace(new RegExp('\\b' + DIALOG_INDICATOR_CLASS + '\\b', 'g'), ''); setTimeout(function() { processDialogQueue(); callback(status); }, 0); setTimeout(function() { try { dialog.parentNode.removeChild(dialog); } catch (err) {} }, 600); return true; } } function closeDialog(status) { if (currentCallback) { return currentCallback(status || false); } } function hasDialog() { return !!currentCallback; } function processDialogQueue() { if (!dialogQueue) { return; } if (!dialogQueue.length) { dialogQueue = null; return; } var args = dialogQueue.shift(); args.push(true); showDialog.apply(window, args); } }(window, document, Clickable, App, App._Utils); App._Form = function(window, document, App, Utils) { App.form = function(form, callback) { if (Utils.isjQueryElem(form)) { form.each(function() { App.form(this, callback); }); return; } if (!Utils.isNode(form)) { throw TypeError('form must be a DOM node, got ' + form); } if (typeof callback !== 'function') { throw TypeError('callback must be a function, got ' + callback); } setupForm(form, callback); }; return {}; function setupForm(form, callback) { var isForm = (form.nodeName === 'FORM'), locked = false, submitButtons; if (isForm) { var submit = document.createElement('input'); submit.style.display = 'none'; submit.type = 'submit'; form.appendChild(submit); form.addEventListener('submit', function(e) { e.preventDefault(); submitForm(); }, false); submitButtons = form.querySelectorAll('.app-submit'); } else { submitButtons = [form]; } Utils.forEach(submitButtons, function(submitButton) { if (submitButton.nodeName === 'TEXTAREA') { isText = true; } else if (submitButton.nodeName !== 'INPUT') { isText = false; } else { switch ((submitButton.type || '').toLowerCase()) { case 'button': case 'submit': case 'reset': case 'hidden': isText = false; break; default: isText = true; break; } } if (isText) { submitButton.addEventListener('keydown', function(e) { if (e.which === 13) { e.preventDefault(); submitForm(); } }, false); } else { submitButton.addEventListener('click', function(e) { e.preventDefault(); submitForm(); }, false); } }); function submitForm() { if (locked) { return; } locked = true; form.disabled = true; var params = {}, inputs = isForm ? form.querySelectorAll('[name]') : [form], done = false; if (isForm) { Utils.forEach( form.querySelectorAll('[name]'), function(elem) { params[elem.name] = elem.value; } ); } else { params.value = form.value; if (form.name) { params[form.name] = form.value; } } Utils.forEach(inputs, function(elem) { elem.disabled = true; if (elem.blur) { elem.blur(); } }); if (isForm && form.blur) { form.blur(); } callback.call(this, params, function() { if (done) { return; } done = true; Utils.forEach(inputs, function(elem) { elem.disabled = false; }); locked = false; form.disabled = false; }); } } }(window, document, App, App._Utils); App._Scroll = function(Scrollable, App, Utils) { var TAGS = { APP_CONTENT: 'app-content', APP_SCROLLABLE: 'app-scrollable', APP_SCROLLHACK: 'app-scrollhack', NO_SCROLL: 'data-no-scroll', SCROLLABLE: 'data-scrollable', LAST_SCROLL: 'data-last-scroll', SCROLL_STYLE: 'data-scroll-style', TOUCH_SCROLL: '-webkit-overflow-scrolling' }, PAGE_MANAGER_VAR = '__appjsPageManager'; App.infiniteScroll = function(elem, options, generator) { if (Utils.isjQueryElem(elem)) { if (elem.length) { var l = elem.length - 1; for (var i = 0; i < l; i++) { App.infiniteScroll(elem[i], options, generator); } return App.infiniteScroll(elem[l], options, generator); } else { return; } } if (!Utils.isNode(elem)) { throw TypeError('infinite scroll container must be a DOM node, got ' + elem); } return setupInfiniteScroll(elem, options, generator); }; return { setup: setupScrollers, disable: disableScrolling, saveScrollPosition: savePageScrollPosition, saveScrollStyle: savePageScrollStyle, restoreScrollPosition: restorePageScrollPosition, restoreScrollStyle: restorePageScrollStyle }; function setupScrollers(page) { Utils.forEach( page.querySelectorAll('.' + TAGS.APP_CONTENT), function(content) { if (content.getAttribute(TAGS.NO_SCROLL) === null) { setupScroller(content); } } ); Utils.forEach( page.querySelectorAll('[' + TAGS.SCROLLABLE + ']'), function(content) { setupScroller(content); } ); } function setupScroller(content) { var forceIScroll = !!window['APP_FORCE_ISCROLL']; Scrollable(content, forceIScroll); content.className += ' ' + TAGS.APP_SCROLLABLE; if (!forceIScroll && Utils.os.ios && Utils.os.version < 6) { content.className += ' ' + TAGS.APP_SCROLLHACK; } } function disableScrolling(page) { Utils.forEach( page.querySelectorAll('*'), function(elem) { elem.style[TAGS.TOUCH_SCROLL] = ''; } ); } function getScrollableElems(page) { var elems = []; if (page) { Utils.forEach( page.querySelectorAll('.' + TAGS.APP_SCROLLABLE), function(elem) { if (elem._scrollable) { elems.push(elem); } } ); } return elems; } function savePageScrollPosition(page) { Utils.forEach( getScrollableElems(page), function(elem) { if (elem._iScroll) { return; } var scrollTop = elem._scrollTop(); elem.setAttribute(TAGS.LAST_SCROLL, scrollTop + ''); } ); } function savePageScrollStyle(page) { Utils.forEach( getScrollableElems(page), function(elem) { if (elem._iScroll) { return; } var scrollStyle = elem.style[TAGS.TOUCH_SCROLL] || ''; elem.style[TAGS.TOUCH_SCROLL] = ''; elem.setAttribute(TAGS.SCROLL_STYLE, scrollStyle); } ); } function restorePageScrollPosition(page, noTimeout) { Utils.forEach( getScrollableElems(page), function(elem) { if (elem._iScroll) { return; } var scrollTop = parseInt(elem.getAttribute(TAGS.LAST_SCROLL)); if (scrollTop) { if (!noTimeout) { setTimeout(function() { elem._scrollTop(scrollTop); }, 0); } else { elem._scrollTop(scrollTop); } } } ); } function restorePageScrollStyle(page) { Utils.forEach( getScrollableElems(page), function(elem) { if (elem._iScroll) { return; } var scrollStyle = elem.getAttribute(TAGS.SCROLL_STYLE) || ''; if (scrollStyle) { elem.style[TAGS.TOUCH_SCROLL] = scrollStyle; } } ); restorePageScrollPosition(page, true); } function setupInfiniteScroll(elem, options, generator) { var page = options.page || getParentPage(elem), pageManager = getPageManager(page); if (!page || !pageManager) { throw Error('could not find parent app-page'); } if (!options) { options = {}; } if (typeof options.scroller === 'undefined') { options.scroller = getParentScroller(elem); } options.autoStart = false; var scroller = Scrollable.infinite(elem, options, generator); scroller.forceLayout(); scroller.disable(); pageManager.ready(function() { scrollReady = true; try { scroller.enable(); } catch (err) { // scroll is already destroyed return; } scroller.layout(); page.addEventListener('appShow', function() { scroller.layout(); }); page.addEventListener('appDestroy', function() { scroller.destroy(); }); }); return scroller; } function getParentPage(elem) { var parent = elem; do { if (/\bapp\-page\b/.test(parent.className)) { return parent; } } while (parent = parent.parentNode); } function getParentScroller(elem) { var parent = elem; do { if (parent._scrollable || /\bapp\-content\b/.test(parent.className)) { return parent; } } while (parent = parent.parentNode); } function getPageManager(page) { if (page) { return page[PAGE_MANAGER_VAR]; } } }(Scrollable, App, App._Utils); // fixes ios bounce scrolling in mobile safari (function(document, App, Utils) { var touches = {}; if (App.platform === 'ios' && App.platformVersion >= 5 && !Utils.os.faked && (typeof kik !== 'object' || kik === null || !kik.enabled)) { bindListeners(); } return; function bindListeners() { document.addEventListener('touchstart', function(e) { var target = getTargetScroller(e); if (target && !target._iScroll) { if ((target.scrollHeight - target.clientHeight > 1) && ((target.scrollTop <= 0) || (target.scrollTop + target.clientHeight >= target.scrollHeight))) { addTouches(e); } } }); document.addEventListener('touchmove', function(e) { var target = getTargetScroller(e); if (!target) { e.preventDefault(); } else if (target._iScroll) { e.preventDefault(); } else if (e.changedTouches) { if (e.changedTouches.length === 1) { onMove(e); } updateTouches(e); } }); document.addEventListener('touchcancel', function(e) { clearTouches(e); }); document.addEventListener('touchend', function(e) { clearTouches(e); }); } function getTargetScroller(e) { var target = e.target; if (target) { do { if (target._scrollable) { break; } } while (target = target.parentNode); } return target; } function onMove(e) { var target = getTargetScroller(e), touch = e.changedTouches[0], y0 = touches[touch.identifier], y1 = touch.pageY; if (target && typeof y0 !== 'undefined') { if (target.scrollTop <= 0) { if (y0 > y1) { delete touches[touch.identifier]; } else { e.preventDefault(); } } else if (target.scrollTop + target.clientHeight >= target.scrollHeight) { if (y0 < y1) { delete touches[touch.identifier]; } else { e.preventDefault(); } } else { delete touches[touch.identifier]; } } } function addTouches(e) { if (e.changedTouches) { for (var i = 0, l = e.changedTouches.length; i < l; i++) { touches[e.changedTouches[i].identifier] = e.changedTouches[i].pageY; } } } function updateTouches(e) { if (e.changedTouches) { for (var i = 0, l = e.changedTouches.length; i < l; i++) { if (e.changedTouches[i].identifier in touches) { touches[e.changedTouches[i].identifier] = e.changedTouches[i].pageY; } } } } function clearTouches(e) { if (e.changedTouches) { for (var i = 0, l = e.changedTouches.length; i < l; i++) { delete touches[e.changedTouches[i].identifier]; } } if (e.touches) { var ids = []; for (var i = 0, l = e.touches.length; i < l; i++) { ids.push(e.touches[i].identifier + ''); } for (var id in touches) { if (ids.indexOf(id) === -1) { delete touches[id]; } } } } })(document, App, App._Utils); App._Pages = function(window, document, Clickable, Scrollable, App, Utils, Events, Metrics, Scroll) { var PAGE_NAME = 'data-page', PAGE_CLASS = 'app-page', APP_LOADED = 'app-loaded', APP_IOS_STATUSBAR = 'app-ios-statusbar', APP_ANDROID_STATUSBAR = 'app-android-statusbar', PAGE_READY_VAR = '__appjsFlushReadyQueue', PAGE_MANAGER_VAR = '__appjsPageManager', EVENTS = { SHOW: 'show', HIDE: 'hide', BACK: 'back', FORWARD: 'forward', BEFORE_BACK: 'beforeBack', READY: 'ready', DESTROY: 'destroy', LAYOUT: 'layout', ONLINE: 'online', OFFLINE: 'offline' }; var preloaded = false, forceIScroll = !!window['APP_FORCE_ISCROLL'], pages = {}, controllers = {}, cleanups = {}, statusBarEnabled = false; setupPageListeners(); if (window.APP_ENABLE_STATUSBAR || window.APP_ENABLE_IOS_STATUSBAR) { enableStatusBar(); } App.add = function(pageName, page) { if (typeof pageName !== 'string') { page = pageName; pageName = undefined; } if (!Utils.isNode(page)) { throw TypeError('page template node must be a DOM node, got ' + page); } addPage(page, pageName); }; App.controller = function(pageName, controller, cleanup) { if (typeof pageName !== 'string') { throw TypeError('page name must be a string, got ' + pageName); } if (typeof controller !== 'function') { throw TypeError('page controller must be a function, got ' + controller); } switch (typeof cleanup) { case 'undefined': cleanup = function() {}; break; case 'function': break; default: throw TypeError('page cleanup handler must be a function, got ' + cleanup); } if (controller) { addController(pageName, controller); } if (cleanup) { addCleanup(pageName, cleanup); } }; App.populator = App.controller; // backwards compat App.generate = function(pageName, args) { if (typeof pageName !== 'string') { throw TypeError('page name must be a string, got ' + pageName); } switch (typeof args) { case 'undefined': args = {}; break; case 'object': break; default: throw TypeError('page arguments must be an object if defined, got ' + args); } return generatePage(pageName, args); }; App.destroy = function(page) { if (!Utils.isNode(page)) { throw TypeError('page node must be a DOM node, got ' + page); } return destroyPage(page); }; App._layout = triggerPageSizeFix; App._enableStatusBar = enableStatusBar; App._enableIOSStatusBar = enableStatusBar; return { EVENTS: EVENTS, has: hasPage, createManager: createPageManager, startGeneration: startPageGeneration, finishGeneration: finishPageGeneration, fire: firePageEvent, startDestruction: startPageDestruction, finishDestruction: finishPageDestruction, fixContent: fixContentHeight, populateBackButton: populatePageBackButton }; /* Page elements */ function preloadPages() { if (preloaded) { return; } preloaded = true; var pageNodes = document.getElementsByClassName(PAGE_CLASS); for (var i = pageNodes.length; i--;) { addPage(pageNodes[i]); } document.body.className += ' ' + APP_LOADED; } function addPage(page, pageName) { if (!pageName) { pageName = page.getAttribute(PAGE_NAME); } if (!pageName) { throw TypeError('page name was not specified'); } page.setAttribute(PAGE_NAME, pageName); if (page.parentNode) { page.parentNode.removeChild(page); } pages[pageName] = page.cloneNode(true); } function hasPage(pageName) { preloadPages(); return (pageName in pages); } function clonePage(pageName) { if (!hasPage(pageName)) { throw TypeError(pageName + ' is not a known page'); } return pages[pageName].cloneNode(true); } /* Page controllers */ function addController(pageName, controller) { controllers[pageName] = controller; } function addCleanup(pageName, cleanup) { cleanups[pageName] = cleanup; } function populatePage(pageName, pageManager, page, args) { var controller = controllers[pageName]; if (!controller) { return; } for (var prop in controller) { pageManager[prop] = controller[prop]; } for (var prop in controller.prototype) { pageManager[prop] = controller.prototype[prop]; } pageManager.page = page; //TODO: getter pageManager.args = args; //TODO: getter (dont want this to hit localStorage) controller.call(pageManager, page, args); } function unpopulatePage(pageName, pageManager, page, args) { var cleanup = cleanups[pageName]; if (cleanup) { cleanup.call(pageManager, page, args); } firePageEvent(pageManager, page, EVENTS.DESTROY); } /* Page generation */ function createPageManager(restored) { var pageManager = { restored: restored, showing: false, online: navigator.onLine }; var readyQueue = []; pageManager.ready = function(func) { if (typeof func !== 'function') { throw TypeError('ready must be called with a function, got ' + func); } if (readyQueue) { readyQueue.push(func); } else { func.call(pageManager); } }; pageManager[PAGE_READY_VAR] = function() { Utils.ready(function() { if (!readyQueue) { return; } var queue = readyQueue.slice(); readyQueue = null; if (Utils.isNode(pageManager.page)) { firePageEvent(pageManager, pageManager.page, EVENTS.READY); } Utils.forEach(queue, function(func) { func.call(pageManager); }); }); }; return pageManager; } function generatePage(pageName, args) { var pageManager = {}, page = startPageGeneration(pageName, pageManager, args); finishPageGeneration(pageName, pageManager, page, args); return page; } function destroyPage(page) { var pageName = page.getAttribute(PAGE_NAME); startPageDestruction(pageName, {}, page, {}); finishPageDestruction(pageName, {}, page, {}); } function startPageGeneration(pageName, pageManager, args) { var page = clonePage(pageName); var eventNames = []; for (var evt in EVENTS) { eventNames.push(eventTypeToName(EVENTS[evt])); } Events.init(page, eventNames); Metrics.watchPage(page, pageName, args); page[PAGE_MANAGER_VAR] = pageManager; fixContentHeight(page); Utils.forEach(page.querySelectorAll('.app-button'), attachButtonEvent); //Attach click events for buttons added later on page.addEventListener('DOMNodeInserted', function(e) { var element = e.srcElement; if (element && element.classList && element.classList.contains('app-button')) { attachButtonEvent(element); } }); populatePage(pageName, pageManager, page, args); page.addEventListener(eventTypeToName(EVENTS.SHOW), function() { setTimeout(function() { if (typeof pageManager[PAGE_READY_VAR] === 'function') { pageManager[PAGE_READY_VAR](); } }, 0); }, false); return page; } function attachButtonEvent(button) { if (button.getAttribute('data-no-click') !== null || button._clickable) { return; } Clickable(button); button.addEventListener('click', function() { var target = button.getAttribute('data-target'), targetArgs = button.getAttribute('data-target-args'), back = (button.getAttribute('data-back') !== null), manualBack = (button.getAttribute('data-manual-back') !== null), args; try { args = JSON.parse(targetArgs); } catch (err) {} if ((typeof args !== 'object') || (args === null)) { args = {}; } if (!back && !target) { return; } if (back && manualBack) { return; } var clickableClass = button.getAttribute('data-clickable-class'); if (clickableClass) { button.disabled = true; button.classList.add(clickableClass); } if (back) { App.back(finish); } else if (target) { App.load(target, args, {}, finish); } function finish() { if (clickableClass) { button.disabled = false; button.classList.remove(clickableClass); } } }, false); } function firePageEvent(pageManager, page, eventType) { var eventName = eventTypeToName(eventType), funcName = eventTypeToFunctionName(eventType), success = true; if (!Events.fire(page, eventName)) { success = false; } if (typeof pageManager[funcName] === 'function') { if (pageManager[funcName]() === false) { success = false; } } return success; } function eventTypeToName(eventType) { return 'app' + eventType[0].toUpperCase() + eventType.slice(1); } function eventTypeToFunctionName(eventType) { return 'on' + eventType[0].toUpperCase() + eventType.slice(1); } function finishPageGeneration(pageName, pageManager, page, args) { Scroll.setup(page); } function startPageDestruction(pageName, pageManager, page, args) { if (!Utils.os.ios || Utils.os.version < 6) { Scroll.disable(page); } if (typeof pageManager.reply === 'function') { pageManager._appNoBack = true; pageManager.reply(); } } function finishPageDestruction(pageName, pageManager, page, args) { unpopulatePage(pageName, pageManager, page, args); } /* Page layout */ function setupPageListeners() { window.addEventListener('orientationchange', triggerPageSizeFix); window.addEventListener('resize', triggerPageSizeFix); window.addEventListener('load', triggerPageSizeFix); setTimeout(triggerPageSizeFix, 0); window.addEventListener('online', function() { if (App._Stack) { Utils.forEach(App._Stack.get(), function(pageInfo) { pageInfo[2].online = true; firePageEvent(pageInfo[2], pageInfo[3], EVENTS.ONLINE); }); } }, false); window.addEventListener('offline', function() { if (App._Stack) { Utils.forEach(App._Stack.get(), function(pageInfo) { pageInfo[2].online = false; firePageEvent(pageInfo[2], pageInfo[3], EVENTS.OFFLINE); }); } }, false); } function triggerPageSizeFix() { fixContentHeight(); var pageData; if (App._Stack) { pageData = App._Stack.getCurrent(); } if (pageData) { firePageEvent(pageData[2], pageData[3], EVENTS.LAYOUT); } //TODO: turns out this isnt all that expensive, but still, lets kill it if we can setTimeout(fixContentHeight, 0); setTimeout(fixContentHeight, 10); setTimeout(fixContentHeight, 100); } function fixContentHeight(page) { if (!page) { if (App._Navigation) { page = App._Navigation.getCurrentNode(); } if (!page) { return; } } var topbar = page.querySelector('.app-topbar'), content = page.querySelector('.app-content'), height = window.innerHeight; if (!content) { return; } if (!topbar) { content.style.height = height + 'px'; return; } var topbarStyles = document.defaultView.getComputedStyle(topbar, null), topbarHeight = Utils.os.android ? 56 : 44; if (statusBarEnabled) { topbarHeight += Utils.os.android ? 24 : 20; } if (topbarStyles.height) { topbarHeight = (parseInt(topbarStyles.height) || 0); if ((topbarStyles.boxSizing || topbarStyles.webkitBoxSizing) !== 'border-box') { topbarHeight += (parseInt(topbarStyles.paddingBottom) || 0) + (parseInt(topbarStyles.paddingTop) || 0); topbarHeight += (parseInt(topbarStyles.borderBottomWidth) || 0) + (parseInt(topbarStyles.borderTopWidth) || 0); } } content.style.height = (height - topbarHeight) + 'px'; } function populatePageBackButton(page, oldPage) { if (!oldPage) { return; } var backButton = page.querySelector('.app-topbar .left.app-button'), oldTitle = oldPage.querySelector('.app-topbar .app-title'); if (!backButton || !oldTitle || (backButton.getAttribute('data-autotitle') === null)) { return; } var oldText = oldTitle.textContent, newText = backButton.textContent; if (!oldText || newText) { return; } if (oldText.length > 13) { oldText = oldText.substr(0, 12) + '..'; } backButton.textContent = oldText; } function enableStatusBar() { if (statusBarEnabled) { return; } statusBarEnabled = true; if (Utils.os.android) { document.body.className += ' ' + APP_ANDROID_STATUSBAR; } else { document.body.className += ' ' + APP_IOS_STATUSBAR; } Utils.ready(function() { setTimeout(triggerPageSizeFix, 6); }); } }(window, document, Clickable, Scrollable, App, App._Utils, App._Events, App._Metrics, App._Scroll); App._Stack = function(window, document, App, Utils, Scroll, Pages) { var STACK_KEY = '__APP_JS_STACK__' + window.location.pathname, STACK_TIME = '__APP_JS_TIME__' + window.location.pathname; var stack = []; App.getStack = function() { return fetchStack(); }; App.getPage = function(index) { var stackSize = stack.length - 1; switch (typeof index) { case 'undefined': index = stackSize; break; case 'number': if (Math.abs(index) > stackSize) { throw TypeError('absolute index cannot be greater than stack size, got ' + index); } if (index < 0) { index = stackSize + index; } break; default: throw TypeError('page index must be a number if defined, got ' + index); } return fetchPage(index); }; App.removeFromStack = function(startIndex, endIndex) { // minus 1 because last item on stack is current page (which is untouchable) var stackSize = stack.length - 1; switch (typeof startIndex) { case 'undefined': startIndex = 0; break; case 'number': if (Math.abs(startIndex) > stackSize) { throw TypeError('absolute start index cannot be greater than stack size, got ' + startIndex); } if (startIndex < 0) { startIndex = stackSize + startIndex; } break; default: throw TypeError('start index must be a number if defined, got ' + startIndex); } switch (typeof endIndex) { case 'undefined': endIndex = stackSize; break; case 'number': if (Math.abs(endIndex) > stackSize) { throw TypeError('absolute end index cannot be greater than stack size, got ' + endIndex); } if (endIndex < 0) { endIndex = stackSize + endIndex; } break; default: throw TypeError('end index must be a number if defined, got ' + endIndex); } if (startIndex > endIndex) { throw TypeError('start index cannot be greater than end index'); } removeFromStack(startIndex, endIndex); }; App.addToStack = function(index, newPages) { // minus 1 because last item on stack is current page (which is untouchable) var stackSize = stack.length - 1; switch (typeof index) { case 'undefined': index = 0; break; case 'number': if (Math.abs(index) > stackSize) { throw TypeError('absolute index cannot be greater than stack size, got ' + index); } if (index < 0) { index = stackSize + index; } break; default: throw TypeError('index must be a number if defined, got ' + index); } if (!Utils.isArray(newPages)) { throw TypeError('added pages must be an array, got ' + newPages); } newPages = newPages.slice(); Utils.forEach(newPages, function(page, i) { if (typeof page === 'string') { page = [page, {}]; } else if (Utils.isArray(page)) { page = page.slice(); } else { throw TypeError('page description must be an array (page name, arguments), got ' + page); } if (typeof page[0] !== 'string') { throw TypeError('page name must be a string, got ' + page[0]); } switch (typeof page[1]) { case 'undefined': page[1] = {}; case 'object': break; default: throw TypeError('page arguments must be an object if defined, got ' + page[1]); } switch (typeof page[2]) { case 'undefined': page[2] = {}; case 'object': break; default: throw TypeError('page options must be an object if defined, got ' + page[2]); } newPages[i] = page; }); addToStack(index, newPages); }; App.saveStack = function() { saveStack(); }; App.destroyStack = function() { destroyStack(); }; App.restore = setupRestoreFunction(); return { get: fetchStack, getCurrent: fetchLastStackItem, getPage: fetchPage, pop: popLastStackItem, push: pushNewStackItem, size: fetchStackSize, save: saveStack, destroy: destroyStack }; function saveStack() { try { var storedStack = []; for (var i = 0, l = stack.length; i < l; i++) { if (stack[i][4].restorable === false) { break; } storedStack.push([stack[i][0], stack[i][3], stack[i][2]]); } localStorage[STACK_KEY] = JSON.stringify(storedStack); localStorage[STACK_TIME] = +new Date() + ''; } catch (err) {} } function destroyStack() { delete localStorage[STACK_KEY]; delete localStorage[STACK_TIME]; } function fetchStack() { return stack.slice().map(reorganisePageData); } function fetchStackSize() { return stack.length; } function fetchLastStackItem() { var pageData = stack[stack.length - 1]; if (pageData) { return reorganisePageData(pageData); } } function popLastStackItem() { var pageData = stack.pop(); if (pageData) { return reorganisePageData(pageData); } } function pushNewStackItem(pageData) { stack.push([pageData[0], pageData[3], pageData[4], pageData[1], pageData[2]]); } function fetchPage(index) { var pageData = stack[index]; if (pageData) { return pageData[1]; } } function reorganisePageData(pageData) { var pageArgs = {}; for (var key in pageData[3]) { pageArgs[key] = pageData[3][key]; } return [pageData[0], pageArgs, pageData[4], pageData[1], pageData[2]]; } // you must manually save the stack if you choose to use this method function removeFromStackNow(startIndex, endIndex) { var deadPages = stack.splice(startIndex, endIndex - startIndex); Utils.forEach(deadPages, function(pageData) { Pages.startDestruction(pageData[0], pageData[4], pageData[1], pageData[3]); Pages.finishDestruction(pageData[0], pageData[4], pageData[1], pageData[3]); }); } function removeFromStack(startIndex, endIndex) { App._Navigation.enqueue(function(finish) { removeFromStackNow(startIndex, endIndex); finish(); }); } // you must manually save the stack if you choose to use this method function addToStackNow(index, newPages, restored) { var pageDatas = [], lastPage; Utils.forEach(newPages, function(pageData) { var pageManager = Pages.createManager(true), page = Pages.startGeneration(pageData[0], pageManager, pageData[1]); if (!pageData[2].transition && pageManager.transition) { pageData[2].transition = pageManager.transition; } if (!pageData[2].duration && pageManager.duration) { pageData[2].duration = pageManager.duration; } Pages.populateBackButton(page, lastPage); Pages.finishGeneration(pageData[0], pageManager, page, pageData[1]); Scroll.saveScrollPosition(page); Scroll.saveScrollStyle(page); pageDatas.push([pageData[0], page, pageData[2], pageData[1], pageManager]); lastPage = page; }); pageDatas.unshift(0); pageDatas.unshift(index); Array.prototype.splice.apply(stack, pageDatas); } function addToStack(index, newPages) { App._Navigation.enqueue(function(finish) { addToStackNow(index, newPages); finish(); }); } function setupRestoreFunction(options) { var storedStack, lastPage; try { storedStack = JSON.parse(localStorage[STACK_KEY]); storedTime = parseInt(localStorage[STACK_TIME]); lastPage = storedStack.pop(); } catch (err) { return; } if (!lastPage) { return; } return function(options, callback) { switch (typeof options) { case 'function': callback = options; case 'undefined': options = {}; case 'object': if (options !== null) { break; } default: throw TypeError('restore options must be an object if defined, got ' + options); } switch (typeof callback) { case 'undefined': callback = function() {}; case 'function': break; default: throw TypeError('restore callback must be a function if defined, got ' + callback); } if (+new Date() - storedTime >= options.maxAge) { throw TypeError('restore content is too old'); } if (!Pages.has(lastPage[0])) { throw TypeError(lastPage[0] + ' is not a known page'); } Utils.forEach(storedStack, function(pageData) { if (!Pages.has(pageData[0])) { throw TypeError(pageData[0] + ' is not a known page'); } }); try { addToStackNow(0, storedStack, true); } catch (err) { removeFromStackNow(0, stack.length); throw Error('failed to restore stack'); } saveStack(); try { App.load(lastPage[0], lastPage[1], lastPage[2], callback); } catch (err) { removeFromStackNow(0, stack.length); throw Error('failed to restore stack'); } }; } }(window, document, App, App._Utils, App._Scroll, App._Pages); App._Transitions = function(window, document, Swapper, App, Utils, Scroll, Pages, Stack) { var TRANSITION_CLASS = 'app-transition', DEFAULT_TRANSITION_IOS = 'slide-left', DEFAULT_TRANSITION_ANDROID = 'android-l-in', DEFAULT_TRANSITION_ANDROID_OLD = 'fade-on', DEFAULT_TRANSITION_ANDROID_GHETTO = 'instant', REVERSE_TRANSITION = { 'instant': 'instant', 'fade': 'fade', 'fade-on': 'fade-off', 'fade-off': 'fade-on', 'scale-in': 'scale-out', 'scale-out': 'scale-in', 'rotate-left': 'rotate-right', 'rotate-right': 'rotate-left', 'cube-left': 'cube-right', 'cube-right': 'cube-left', 'swap-left': 'swap-right', 'swap-right': 'swap-left', 'explode-in': 'explode-out', 'explode-out': 'explode-in', 'implode-in': 'implode-out', 'implode-out': 'implode-in', 'slide-left': 'slide-right', 'slide-right': 'slide-left', 'slide-up': 'slide-down', 'slide-down': 'slide-up', 'slideon-left': 'slideoff-left', 'slideon-right': 'slideoff-right', 'slideon-up': 'slideoff-up', 'slideon-down': 'slideoff-down', 'slideoff-left': 'slideon-left', 'slideoff-right': 'slideon-right', 'slideoff-up': 'slideon-up', 'slideoff-down': 'slideon-down', 'slideon-left-ios': 'slideoff-right-ios', 'glideon-right': 'glideoff-right', 'glideoff-right': 'slideon-right', 'glideon-left': 'glideoff-left', 'glideoff-left': 'slideon-left', 'glideon-down': 'glideoff-down', 'glideoff-down': 'slideon-down', 'glideon-up': 'glideoff-up', 'glideoff-up': 'slideon-up', 'android-l-in': 'android-l-out', 'android-l-out': 'android-l-in' }, WALL_RADIUS = 10; var shouldDrag = false, defaultTransition, reverseTransition, dragLock; if (Utils.os.ios) { setDefaultTransition(DEFAULT_TRANSITION_IOS); } else if (Utils.os.android) { if (Utils.os.version >= 4) { setDefaultTransition(DEFAULT_TRANSITION_ANDROID); } else if ((Utils.os.version < 2.3) || /LT15a/i.test(navigator.userAgent)) { setDefaultTransition(DEFAULT_TRANSITION_ANDROID_GHETTO); } else { setDefaultTransition(DEFAULT_TRANSITION_ANDROID_OLD); } } checkForDragTransitionMetaTag(); App.setDefaultTransition = function(transition) { if (typeof transition === 'object') { switch (Utils.os.name) { case 'android': if ((Utils.os.version < 4) && transition.androidFallback) { transition = transition.androidFallback; } else { transition = transition.android; } break; case 'ios': if ((Utils.os.version < 5) && transition.iosFallback) { transition = transition.iosFallback; } else { transition = transition.ios; } break; default: transition = transition.fallback; break; } if (!transition) { return; } } if (typeof transition !== 'string') { throw TypeError('transition must be a string if defined, got ' + transition); } if (!(transition in REVERSE_TRANSITION)) { throw TypeError('invalid transition type, got ' + transition); } setDefaultTransition(transition); }; App.getDefaultTransition = function() { return defaultTransition; }; App.getReverseTransition = function() { return reverseTransition; }; App.enableDragTransition = function() { allowDragging(); }; return { REVERSE_TRANSITION: REVERSE_TRANSITION, run: performTransition, enableDrag: enableIOS7DragTransition, disableDrag: disableIOS7DragTransition }; function setDefaultTransition(transition) { defaultTransition = transition; reverseTransition = REVERSE_TRANSITION[defaultTransition]; } function shouldUseNativeIOSTransition(transition) { if (!Utils.os.ios) { return false; } else if (transition === 'slide-left') { return true; } else if (transition === 'slide-right') { return true; } else { return false; } } function performTransition(oldPage, page, options, callback, reverse) { if (!options.transition) { options.transition = (reverse ? reverseTransition : defaultTransition); } var isIOS7SlideUp = (Utils.os.ios && (Utils.os.version >= 7) && { 'slideon-down': 1, 'slideoff-down': 1 }[options.transition]); if (!options.duration) { if (!Utils.os.ios) { options.duration = 180; } else if (Utils.os.version < 7) { options.duration = 325; } else if (isIOS7SlideUp) { options.duration = 475; } else { options.duration = 425; } } if (!options.easing) { if (Utils.os.ios) { if (isIOS7SlideUp) { options.easing = 'cubic-bezier(0.4,0.6,0.05,1)'; } else if (options.transition === 'slideon-left-ios' || options.transition === 'slideoff-right-ios') { if (Utils.os.version < 7) { options.easing = 'ease-in-out'; } else { options.easing = 'cubic-bezier(0.4,0.6,0.2,1)'; } } } else if (Utils.os.android) { if (options.transition === 'android-l-in') { options.easing = 'ease-out'; } else if (options.transition === 'android-l-out') { options.easing = 'ease-in'; } } } document.body.className += ' ' + TRANSITION_CLASS; if (options.transition === 'instant') { Swapper(oldPage, page, options, function() { //TODO: this is stupid. let it be synchronous if it can be. //TODO: fix the root of the race in core navigation. setTimeout(finish, 0); }); } else if (shouldUseNativeIOSTransition(options.transition)) { performNativeIOSTransition(oldPage, page, options, finish); } else { Swapper(oldPage, page, options, finish); } function finish() { document.body.className = document.body.className.replace(new RegExp('\\b' + TRANSITION_CLASS + '\\b'), ''); callback(); } } function performNativeIOSTransition(oldPage, page, options, callback) { var slideLeft = (options.transition === 'slide-left'), topPage = slideLeft ? page : oldPage, transitions = getNativeIOSTransitionList(page, oldPage, slideLeft); if (!transitions) { // proper iOS transition not possible, fallback to normal Swapper(oldPage, page, options, callback); return; } var oldPosition = topPage.style.position, oldZIndex = topPage.style.zIndex, oldBackground = topPage.style.background; topPage.style.position = 'fixed'; topPage.style.zIndex = '4000'; topPage.style.background = 'none'; if (slideLeft) { oldPage.parentNode.insertBefore(page, oldPage); } else if (oldPage.nextSibling) { oldPage.parentNode.insertBefore(page, oldPage.nextSibling); } else { oldPage.parentNode.appendChild(page); } if (App._Pages) { App._Pages.fixContent(oldPage); App._Pages.fixContent(page); } if (Utils.os.version < 7) { options.easing = 'ease-in-out'; } else { options.easing = 'cubic-bezier(0.4,0.6,0.2,1)'; } Utils.animate(transitions, options.duration, options.easing, function() { oldPage.parentNode.removeChild(oldPage); topPage.style.position = oldPosition; topPage.style.zIndex = oldZIndex; topPage.style.background = oldBackground; callback(); }); } function getNativeIOSTransitionList(page, oldPage, slideLeft) { var currentBar = oldPage.querySelector('.app-topbar'), currentTitle = oldPage.querySelector('.app-topbar .app-title'), currentBack = oldPage.querySelector('.app-topbar .left.app-button'), currentContent = oldPage.querySelector('.app-content'), newBar = page.querySelector('.app-topbar'), newTitle = page.querySelector('.app-topbar .app-title'), newBack = page.querySelector('.app-topbar .left.app-button'), newContent = page.querySelector('.app-content'), transitions = []; if (!currentBar || !newBar || !currentContent || !newContent || !Utils.isVisible(currentBar) || !Utils.isVisible(newBar)) { return; } if (currentBack && (currentBack.getAttribute('data-noslide') !== null)) { currentBack = undefined; } if (newBack && (newBack.getAttribute('data-noslide') !== null)) { newBack = undefined; } // fade topbar if (slideLeft) { transitions.push({ opacityStart: 0, opacityEnd: 1, elem: newBar }); } else { transitions.push({ opacityStart: 1, opacityEnd: 0, elem: currentBar }); } // slide titles if (currentTitle) { transitions.push({ transitionStart: 'translate3d(0,0,0)', transitionEnd: 'translate3d(' + getTitleTransform(newBack, slideLeft) + 'px,0,0)', elem: currentTitle }); } if (newTitle) { transitions.push({ transitionStart: 'translate3d(' + getTitleTransform(currentBack, !slideLeft) + 'px,0,0)', transitionEnd: 'translate3d(0,0,0)', elem: newTitle }); } // slide back button if (Utils.os.version >= 5) { if (currentBack) { transitions.push({ transitionStart: 'translate3d(0,0,0)', transitionEnd: 'translate3d(' + getBackTransform(currentBack, newBack, !slideLeft) + 'px,0,0)', elem: currentBack }); } if (newBack) { transitions.push({ transitionStart: 'translate3d(' + getBackTransform(newBack, currentBack, slideLeft) + 'px,0,0)', transitionEnd: 'translate3d(0,0,0)', elem: newBack }); } } // slide contents if (Utils.os.version < 7) { transitions.push({ transitionStart: 'translate3d(0,0,0)', transitionEnd: 'translate3d(' + (slideLeft ? -100 : 100) + '%,0,0)', elem: currentContent }, { transitionStart: 'translate3d(' + (slideLeft ? 100 : -100) + '%,0,0)', transitionEnd: 'translate3d(0,0,0)', elem: newContent }); } else { transitions.push({ transitionStart: 'translate3d(0,0,0)', transitionEnd: 'translate3d(' + (slideLeft ? -30 : 100) + '%,0,0)', elem: currentContent }, { transitionStart: 'translate3d(' + (slideLeft ? 100 : -30) + '%,0,0)', transitionEnd: 'translate3d(0,0,0)', elem: newContent }); } return transitions; } function getBackTransform(backButton, oldButton, toCenter) { var fullWidth = backButton.textContent.length * (Utils.os.version < 7 ? 10 : 12), oldWidth = oldButton ? (oldButton.textContent.length * 15) : 0; if (!toCenter) { return (oldWidth - window.innerWidth) / 2; } else { return (window.innerWidth - fullWidth) / 2; } } function getTitleTransform(backButton, toLeft) { var fullWidth = 0; if (backButton && (Utils.os.version >= 5)) { fullWidth = backButton.textContent.length * (Utils.os.version < 7 ? 10 : 12); } if (!toLeft) { return (window.innerWidth / 2); } else { return (fullWidth - window.innerWidth) / 2; } } function allowDragging() { shouldDrag = true; } function checkForDragTransitionMetaTag() { var metas = document.querySelectorAll('meta'); for (var i = 0, l = metas.length; i < l; i++) { if ((metas[i].name === 'app-drag-transition') && (metas[i].content === 'true')) { allowDragging(); return; } } } function enableIOS7DragTransition() { if (!shouldDrag || !Utils.os.ios || (Utils.os.version < 7)) { return; } var pages = Stack.get().slice(-2), previousPage = pages[0], currentPage = pages[1], draggingTouch, lastTouch, navigationLock, dead, slideLeft; if (!previousPage || !currentPage) { return; } var currentNode = currentPage[3], currentBar = currentPage[3].querySelector('.app-topbar'), currentTitle = currentPage[3].querySelector('.app-topbar .app-title'), currentBack = currentPage[3].querySelector('.app-topbar .left.app-button'), currentContent = currentPage[3].querySelector('.app-content'), oldNode = previousPage[3], oldBar = previousPage[3].querySelector('.app-topbar'), oldTitle = previousPage[3].querySelector('.app-topbar .app-title'), oldBack = previousPage[3].querySelector('.app-topbar .left.app-button'), oldContent = previousPage[3].querySelector('.app-content'); if (!currentNode || !currentBar || !currentContent || !oldNode || !oldBar || !oldContent) { return; } var dragableTransitions = ['slide-left', 'slideon-left-ios']; if ((dragableTransitions.indexOf(currentPage[4].transition) === -1) && (currentPage[4].transition || dragableTransitions.indexOf(defaultTransition) === -1)) { return; } else if ((currentPage[4].transition === 'slide-left') || (!currentPage[4].transition && 'slide-left' === defaultTransition)) { slideLeft = true; } var oldPosition = currentPage[3].style.position, oldZIndex = currentPage[3].style.zIndex, oldBackground = currentPage[3].style.background; currentPage[3].style.position = 'fixed'; currentPage[3].style.zIndex = '4000'; currentPage[3].style.background = 'none'; //TODO: this sucks if (currentPage[3].nextSibling) { currentPage[3].parentNode.insertBefore(previousPage[3], currentPage[3].nextSibling); } else { currentPage[3].parentNode.appendChild(previousPage[3]); } Pages.fixContent(oldNode); Scroll.restoreScrollPosition(oldNode); window.addEventListener('touchstart', startDrag, false); window.addEventListener('touchmove', dragMove, false); window.addEventListener('touchcancel', finishDrag, false); window.addEventListener('touchend', finishDrag, false); var goBack = false; dragLock = function() { unbindListeners(); cleanupElems(); }; function unbindListeners() { window.removeEventListener('touchstart', startDrag); window.removeEventListener('touchmove', dragMove); window.removeEventListener('touchcancel', finishDrag); window.removeEventListener('touchend', finishDrag); } function cleanupElems() { currentPage[3].style.position = oldPosition; currentPage[3].style.zIndex = oldZIndex; currentPage[3].style.background = oldBackground; if (previousPage[3].parentNode) { previousPage[3].parentNode.removeChild(previousPage[3]); } } function startDrag(e) { if (draggingTouch || navigationLock || dead) { return; } var touch = (e.touches && e.touches[0]); if (!touch || (touch.pageX > WALL_RADIUS)) { return; } if (!Pages.fire(currentPage[2], currentPage[3], Pages.EVENTS.BEFORE_BACK)) { return; } e.preventDefault(); App._Navigation.enqueue(function(unlock) { navigationLock = unlock; //TODO: what if transition is already over? }, true); document.body.className += ' ' + TRANSITION_CLASS; draggingTouch = lastTouch = { x: touch.pageX, y: touch.pageY }; currentBar.style.webkitTransition = 'all 0s linear'; if (currentTitle) { currentTitle.style.webkitTransition = 'all 0s linear'; } if (currentBack) { currentBack.style.webkitTransition = 'all 0s linear'; } currentContent.style.webkitTransition = 'all 0s linear'; oldBar.style.webkitTransition = 'all 0s linear'; if (oldTitle) { oldTitle.style.webkitTransition = 'all 0s linear'; } if (oldBack) { oldBack.style.webkitTransition = 'all 0s linear'; } oldContent.style.webkitTransition = 'all 0s linear'; } function dragMove(e) { if (draggingTouch && e.touches && e.touches[0] && !dead) { if (lastTouch) { goBack = (lastTouch.x < e.touches[0].pageX); } lastTouch = { x: e.touches[0].pageX, y: e.touches[0].pageY }; var progress = Math.min(Math.max(0, (lastTouch.x - draggingTouch.x) / window.innerWidth), 1); setDragPosition(progress); } } function finishDrag(e) { if (!draggingTouch || !navigationLock || dead) { return; } unbindListeners(); lastTouch = (e.touches && e.touches[0]) || lastTouch; var progess = 0; if (lastTouch) { progress = (lastTouch.x - draggingTouch.x) / window.innerWidth; } var dontTransition = ((progress < 0.1 && !goBack) || (0.9 < progress && goBack)); if (!dontTransition) { currentBar.style.webkitTransitionDuration = '0.15s'; if (currentTitle) { currentTitle.style.webkitTransitionDuration = '0.15s'; } if (currentBack) { currentBack.style.webkitTransitionDuration = '0.15s'; } currentContent.style.webkitTransitionDuration = '0.15s'; oldBar.style.webkitTransitionDuration = '0.15s'; if (oldTitle) { oldTitle.style.webkitTransitionDuration = '0.15s'; } if (oldBack) { oldBack.style.webkitTransitionDuration = '0.15s'; } oldContent.style.webkitTransitionDuration = '0.15s'; } if (goBack) { Pages.fire(currentPage[2], currentPage[3], Pages.EVENTS.BACK); setDragPosition(1); } else { setDragPosition(0); } draggingTouch = lastTouch = null; if (!dontTransition) { currentPage[3].addEventListener('webkitTransitionEnd', finishTransition, false); } else { finishTransition(); } function finishTransition() { currentPage[3].removeEventListener('webkitTransitionEnd', finishTransition); if (goBack) { if (currentPage[3].parentNode) { currentPage[3].parentNode.removeChild(currentPage[3]); } } else { if (previousPage[3].parentNode) { previousPage[3].parentNode.removeChild(previousPage[3]); } } currentPage[3].style.position = oldPosition; currentPage[3].style.zIndex = oldZIndex; currentPage[3].style.background = oldBackground; currentBar.style.webkitTransition = ''; currentBar.style.webkitTransform = ''; if (currentTitle) { currentTitle.style.webkitTransition = ''; currentTitle.style.webkitTransform = ''; } if (currentBack) { currentBack.style.webkitTransition = ''; currentBack.style.webkitTransform = ''; } currentContent.style.webkitTransition = ''; currentContent.style.webkitTransform = ''; oldBar.style.webkitTransition = ''; oldBar.style.webkitTransform = ''; if (oldTitle) { oldTitle.style.webkitTransition = ''; oldTitle.style.webkitTransform = ''; } if (oldBack) { oldBack.style.webkitTransition = ''; oldBack.style.webkitTransform = ''; } oldContent.style.webkitTransition = ''; oldContent.style.webkitTransform = ''; document.body.className = document.body.className.replace(new RegExp('\\b' + TRANSITION_CLASS + '\\b'), ''); if (goBack) { Pages.startDestruction(currentPage[0], currentPage[2], currentPage[3], currentPage[1]); Pages.fixContent(oldNode); Scroll.restoreScrollStyle(oldNode); currentPage[2].showing = false Pages.fire(currentPage[2], currentPage[3], Pages.EVENTS.HIDE); previousPage[2].showing = true Pages.fire(previousPage[2], oldNode, Pages.EVENTS.SHOW); Pages.finishDestruction(currentPage[0], currentPage[2], currentPage[3], currentPage[1]); Stack.pop(); App._Navigation.update(); } dragLock = null; navigationLock(); } } function setDragPosition(progress) { if (slideLeft) { currentBar.style.opacity = 1 - progress; if (currentTitle) { currentTitle.style.webkitTransform = 'translate3d(' + (progress * window.innerWidth / 2) + 'px,0,0)'; } if (currentBack) { currentBack.style.webkitTransform = 'translate3d(' + (progress * (window.innerWidth - currentBack.textContent.length * 12) / 2) + 'px,0,0)'; } if (oldTitle) { oldTitle.style.webkitTransform = 'translate3d(' + ((1 - progress) * (window.innerWidth - currentBack.textContent.length * 12) / -2) + 'px,0,0)'; } if (oldBack) { oldBack.style.webkitTransform = 'translate3d(' + ((1 - progress) * -150) + '%,0,0)'; } } else { currentBar.style.webkitTransform = 'translate3d(' + (progress * 100) + '%,0,0)'; oldBar.style.webkitTransform = 'translate3d(' + ((1 - progress) * -30) + '%,0,0)'; } currentContent.style.webkitTransform = 'translate3d(' + (progress * 100) + '%,0,0)'; oldContent.style.webkitTransform = 'translate3d(' + ((1 - progress) * -30) + '%,0,0)'; } } function disableIOS7DragTransition() { if (dragLock) { dragLock(); dragLock = null; } } }(window, document, Swapper, App, App._Utils, App._Scroll, App._Pages, App._Stack); App._Navigation = function(window, document, App, Dialog, Scroll, Pages, Stack, Transitions) { var navQueue = [], navLock = false, current, currentNode; App.current = function() { return current; }; App.load = function(pageName, args, options, callback) { if (typeof pageName !== 'string') { throw TypeError('page name must be a string, got ' + pageName); } switch (typeof args) { case 'function': options = args; args = {}; case 'string': callback = options; options = args; case 'undefined': args = {}; case 'object': break; default: throw TypeError('page arguments must be an object if defined, got ' + args); } switch (typeof options) { case 'function': callback = options; case 'undefined': options = {}; case 'object': break; case 'string': options = { transition: options }; break; default: throw TypeError('options must be an object if defined, got ' + options); } switch (typeof callback) { case 'undefined': callback = function() {}; case 'function': break; default: throw TypeError('callback must be a function if defined, got ' + callback); } return loadPage(pageName, args, options, callback); }; App.back = function(pageName, callback) { switch (typeof pageName) { case 'function': callback = pageName; case 'undefined': pageName = undefined; case 'string': break; default: throw TypeError('pageName must be a string if defined, got ' + pageName); } switch (typeof callback) { case 'undefined': callback = function() {}; case 'function': break; default: throw TypeError('callback must be a function if defined, got ' + callback); } return navigateBack(pageName, callback); }; App.pick = function(pageName, args, options, loadCallback, callback) { if (typeof pageName !== 'string') { throw TypeError('page name must be a string, got ' + pageName); } switch (typeof args) { case 'function': options = args; args = {}; case 'string': callback = loadCallback; loadCallback = options; options = args; case 'undefined': args = {}; case 'object': break; default: throw TypeError('page arguments must be an object if defined, got ' + args); } switch (typeof options) { case 'function': callback = loadCallback; loadCallback = options; case 'undefined': options = {}; case 'object': break; case 'string': options = { transition: options }; break; default: throw TypeError('options must be an object if defined, got ' + options); } if (typeof loadCallback !== 'function') { throw TypeError('callback must be a function, got ' + loadCallback); } switch (typeof callback) { case 'undefined': callback = loadCallback; loadCallback = function() {}; case 'function': break; default: throw TypeError('callback must be a function, got ' + callback); } return pickPage(pageName, args, options, loadCallback, callback); }; App.info = function () { return { platform:App.platform } } return { getCurrentNode: getCurrentNode, update: updateCurrentNode, enqueue: navigate }; function navigate(handler, dragTransition) { if (navLock) { navQueue.push(handler); return false; } navLock = true; if (!dragTransition) { Transitions.disableDrag(); } handler(function() { Stack.save(); navLock = false; if (!processNavigationQueue()) { Transitions.enableDrag(); } }); return true; } function processNavigationQueue() { if (navQueue.length) { navigate(navQueue.shift()); return true; } else { return false; } } function getCurrentNode() { return currentNode; } function updateCurrentNode() { var lastStackItem = Stack.getCurrent(); current = lastStackItem[0] currentNode = lastStackItem[3]; } function loadPage(pageName, args, options, callback, setupPickerMode) { navigate(function(unlock) { var oldNode = currentNode, pageManager = Pages.createManager(false); if (setupPickerMode) { setupPickerMode(pageManager); } var page = Pages.startGeneration(pageName, pageManager, args), restoreData = Stack.getCurrent(), restoreNode = restoreData && restoreData[3], restoreManager = restoreData && restoreData[2]; if (!options.transition && pageManager.transition) { options.transition = pageManager.transition; } if (!options.duration && pageManager.duration) { options.duration = pageManager.duration; } Pages.populateBackButton(page, oldNode || restoreNode); if (!current) { App.restore = null; document.body.appendChild(page); Pages.fire(pageManager, page, Pages.EVENTS.LAYOUT); updatePageData(); finish(); } else { Scroll.saveScrollPosition(currentNode); var newOptions = {}; for (var key in options) { newOptions[key] = options[key]; } uiBlockedTask(function(unlockUI) { Transitions.run(currentNode, page, newOptions, function() { Pages.fixContent(page); unlockUI(); finish(); }); Pages.fire(pageManager, page, Pages.EVENTS.LAYOUT); }); //TODO: what if instant swap? updatePageData(); } function updatePageData() { current = pageName; currentNode = page; Stack.push([pageName, args, pageManager, page, options]); if (oldNode && restoreManager) { Pages.fire(restoreManager, oldNode, Pages.EVENTS.FORWARD); } } function finish() { Scroll.saveScrollStyle(oldNode); Pages.finishGeneration(pageName, pageManager, page, args); unlock(); callback(); if (oldNode && restoreManager) { restoreManager.showing = false Pages.fire(restoreManager, oldNode, Pages.EVENTS.HIDE); } pageManager.showing = true; Pages.fire(pageManager, page, Pages.EVENTS.SHOW); } }); if (!Pages.has(pageName)) { return false; } } function navigateBack(backPageName, callback) { if (Dialog.status() && Dialog.close() && !backPageName) { callback(); return; } var stack = Stack.get().map(function(page) { return page[0]; }); if (!stack.length) { throw Error(backPageName + ' is not currently in the stack, cannot go back to it'); } if (backPageName) { var index = -1; for (var i = stack.length - 1; i >= 0; i--) { if (stack[i] === backPageName) { index = i; break; } } if (index === -1) { throw Error(backPageName + ' is not currently in the stack, cannot go back to it'); } if (index !== stack.length - 2) { App.removeFromStack(index + 1); } } var stackLength = stack.length, cancelled = false; var navigatedImmediately = navigate(function(unlock) { if (Stack.size() < 2) { unlock(); callback(); return; } var oldPage = Stack.getCurrent(); if (!Pages.fire(oldPage[2], oldPage[3], Pages.EVENTS.BEFORE_BACK)) { cancelled = true; unlock(); callback(); return; } else { Stack.pop(); } var data = Stack.getCurrent(), pageName = data[0], page = data[3], oldOptions = oldPage[4]; Pages.fire(oldPage[2], oldPage[3], Pages.EVENTS.BACK); Pages.fixContent(page); Pages.startDestruction(oldPage[0], oldPage[2], oldPage[3], oldPage[1]); Scroll.restoreScrollPosition(page); var newOptions = {}; for (var key in oldOptions) { if (key === 'transition') { newOptions[key] = Transitions.REVERSE_TRANSITION[oldOptions[key]] || oldOptions[key]; } else { newOptions[key] = oldOptions[key]; } } uiBlockedTask(function(unlockUI) { Transitions.run(currentNode, page, newOptions, function() { Pages.fixContent(page); Scroll.restoreScrollStyle(page); unlockUI(); oldPage[2].showing = false Pages.fire(oldPage[2], oldPage[3], Pages.EVENTS.HIDE); data[2].showing = true Pages.fire(data[2], page, Pages.EVENTS.SHOW); setTimeout(function() { Pages.finishDestruction(oldPage[0], oldPage[2], oldPage[3], oldPage[1]); unlock(); callback(); }, 0); }, true); Pages.fixContent(page); Pages.fire(data[2], page, Pages.EVENTS.LAYOUT); }); current = pageName; currentNode = page; }); if (cancelled || (navigatedImmediately && (stackLength < 2))) { return false; } } function pickPage(pageName, args, options, loadCallback, callback) { var finished = false; loadPage(pageName, args, options, loadCallback, function(pageManager) { pageManager.restorable = false; pageManager.reply = function() { if (!finished) { finished = true; if (!pageManager._appNoBack) { navigateBack(undefined, function() {}); } callback.apply(App, arguments); } }; }); } // blocks UI interaction during some aysnchronous task // is not locked because multiple calls dont effect eachother function uiBlockedTask(task) { var taskComplete = false; var clickBlocker = document.createElement('div'); clickBlocker.className = 'app-clickblocker'; document.body.appendChild(clickBlocker); clickBlocker.addEventListener('touchstart', function(e) { e.preventDefault(); }, false); task(function() { if (taskComplete) { return; } taskComplete = true; document.body.removeChild(clickBlocker); }); } }(window, document, App, App._Dialog, App._Scroll, App._Pages, App._Stack, App._Transitions); // Hack to fix Android L issue where touch events don't get propagated (function(document, App, Utils) { if (App.platform !== 'android' || App.platformVersion < 5) { return; } Utils.ready(function() { setTimeout(function() { var nodes = [].slice.call(document.body.childNodes); nodes.forEach(function(node) { document.body.removeChild(node); }); nodes.forEach(function(node) { document.body.appendChild(node); }); }, 200); }); })(document, App, App._Utils);