5760 lines
184 KiB
JavaScript
5760 lines
184 KiB
JavaScript
/**
|
|
** ==============================
|
|
** 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);
|