PageRenderTime 134ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 3ms

/libs/amazeui/1.0.0-beta2/js/amazeui.js

https://gitlab.com/mba811/static
JavaScript | 10987 lines | 8640 code | 50 blank | 2297 comment | 2113 complexity | 17456fbc241fbc8714d7d7bbbbf956aa MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. /*! Amaze UI v1.0.0-beta2 | by Amaze UI Team | (c) 2014 AllMobilize, Inc. | Licensed under MIT | 2014-09-28T02:09:21 UTC */
  2. /*! Sea.js 2.2.1 | seajs.org/LICENSE.md */
  3. !function(a,b){function c(a){return function(b){return{}.toString.call(b)=="[object "+a+"]"}}function d(){return A++}function e(a){return a.match(D)[0]}function f(a){for(a=a.replace(E,"/");a.match(F);)a=a.replace(F,"/");return a=a.replace(G,"$1/")}function g(a){var b=a.length-1,c=a.charAt(b);return"#"===c?a.substring(0,b):".js"===a.substring(b-2)||a.indexOf("?")>0||".css"===a.substring(b-3)||"/"===c?a:a+".js"}function h(a){var b=v.alias;return b&&x(b[a])?b[a]:a}function i(a){var b=v.paths,c;return b&&(c=a.match(H))&&x(b[c[1]])&&(a=b[c[1]]+c[2]),a}function j(a){var b=v.vars;return b&&a.indexOf("{")>-1&&(a=a.replace(I,function(a,c){return x(b[c])?b[c]:a})),a}function k(a){var b=v.map,c=a;if(b)for(var d=0,e=b.length;e>d;d++){var f=b[d];if(c=z(f)?f(a)||a:a.replace(f[0],f[1]),c!==a)break}return c}function l(a,b){var c,d=a.charAt(0);if(J.test(a))c=a;else if("."===d)c=f((b?e(b):v.cwd)+a);else if("/"===d){var g=v.cwd.match(K);c=g?g[0]+a.substring(1):a}else c=v.base+a;return 0===c.indexOf("//")&&(c=location.protocol+c),c}function m(a,b){if(!a)return"";a=h(a),a=i(a),a=j(a),a=g(a);var c=l(a,b);return c=k(c)}function n(a){return a.hasAttribute?a.src:a.getAttribute("src",4)}function o(a,b,c){var d=S.test(a),e=L.createElement(d?"link":"script");if(c){var f=z(c)?c(a):c;f&&(e.charset=f)}p(e,b,d,a),d?(e.rel="stylesheet",e.href=a):(e.async=!0,e.src=a),T=e,R?Q.insertBefore(e,R):Q.appendChild(e),T=null}function p(a,c,d,e){function f(){a.onload=a.onerror=a.onreadystatechange=null,d||v.debug||Q.removeChild(a),a=null,c()}var g="onload"in a;return!d||!V&&g?(g?(a.onload=f,a.onerror=function(){C("error",{uri:e,node:a}),f()}):a.onreadystatechange=function(){/loaded|complete/.test(a.readyState)&&f()},b):(setTimeout(function(){q(a,c)},1),b)}function q(a,b){var c=a.sheet,d;if(V)c&&(d=!0);else if(c)try{c.cssRules&&(d=!0)}catch(e){"NS_ERROR_DOM_SECURITY_ERR"===e.name&&(d=!0)}setTimeout(function(){d?b():q(a,b)},20)}function r(){if(T)return T;if(U&&"interactive"===U.readyState)return U;for(var a=Q.getElementsByTagName("script"),b=a.length-1;b>=0;b--){var c=a[b];if("interactive"===c.readyState)return U=c}}function s(a){var b=[];return a.replace(X,"").replace(W,function(a,c,d){d&&b.push(d)}),b}function t(a,b){this.uri=a,this.dependencies=b||[],this.exports=null,this.status=0,this._waitings={},this._remain=0}if(!a.seajs){var u=a.seajs={version:"2.2.1"},v=u.data={},w=c("Object"),x=c("String"),y=Array.isArray||c("Array"),z=c("Function"),A=0,B=v.events={};u.on=function(a,b){var c=B[a]||(B[a]=[]);return c.push(b),u},u.off=function(a,b){if(!a&&!b)return B=v.events={},u;var c=B[a];if(c)if(b)for(var d=c.length-1;d>=0;d--)c[d]===b&&c.splice(d,1);else delete B[a];return u};var C=u.emit=function(a,b){var c=B[a],d;if(c)for(c=c.slice();d=c.shift();)d(b);return u},D=/[^?#]*\//,E=/\/\.\//g,F=/\/[^/]+\/\.\.\//,G=/([^:/])\/\//g,H=/^([^/:]+)(\/.+)$/,I=/{([^{]+)}/g,J=/^\/\/.|:\//,K=/^.*?\/\/.*?\//,L=document,M=e(L.URL),N=L.scripts,O=L.getElementById("seajsnode")||N[N.length-1],P=e(n(O)||M);u.resolve=m;var Q=L.head||L.getElementsByTagName("head")[0]||L.documentElement,R=Q.getElementsByTagName("base")[0],S=/\.css(?:\?|$)/i,T,U,V=+navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+).*/,"$1")<536;u.request=o;var W=/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g,X=/\\\\/g,Y=u.cache={},Z,$={},_={},ab={},bb=t.STATUS={FETCHING:1,SAVED:2,LOADING:3,LOADED:4,EXECUTING:5,EXECUTED:6};t.prototype.resolve=function(){for(var a=this,b=a.dependencies,c=[],d=0,e=b.length;e>d;d++)c[d]=t.resolve(b[d],a.uri);return c},t.prototype.load=function(){var a=this;if(!(a.status>=bb.LOADING)){a.status=bb.LOADING;var c=a.resolve();C("load",c);for(var d=a._remain=c.length,e,f=0;d>f;f++)e=t.get(c[f]),e.status<bb.LOADED?e._waitings[a.uri]=(e._waitings[a.uri]||0)+1:a._remain--;if(0===a._remain)return a.onload(),b;var g={};for(f=0;d>f;f++)e=Y[c[f]],e.status<bb.FETCHING?e.fetch(g):e.status===bb.SAVED&&e.load();for(var h in g)g.hasOwnProperty(h)&&g[h]()}},t.prototype.onload=function(){var a=this;a.status=bb.LOADED,a.callback&&a.callback();var b=a._waitings,c,d;for(c in b)b.hasOwnProperty(c)&&(d=Y[c],d._remain-=b[c],0===d._remain&&d.onload());delete a._waitings,delete a._remain},t.prototype.fetch=function(a){function c(){u.request(g.requestUri,g.onRequest,g.charset)}function d(){delete $[h],_[h]=!0,Z&&(t.save(f,Z),Z=null);var a,b=ab[h];for(delete ab[h];a=b.shift();)a.load()}var e=this,f=e.uri;e.status=bb.FETCHING;var g={uri:f};C("fetch",g);var h=g.requestUri||f;return!h||_[h]?(e.load(),b):$[h]?(ab[h].push(e),b):($[h]=!0,ab[h]=[e],C("request",g={uri:f,requestUri:h,onRequest:d,charset:v.charset}),g.requested||(a?a[g.requestUri]=c:c()),b)},t.prototype.exec=function(){function a(b){return t.get(a.resolve(b)).exec()}var c=this;if(c.status>=bb.EXECUTING)return c.exports;c.status=bb.EXECUTING;var e=c.uri;a.resolve=function(a){return t.resolve(a,e)},a.async=function(b,c){return t.use(b,c,e+"_async_"+d()),a};var f=c.factory,g=z(f)?f(a,c.exports={},c):f;return g===b&&(g=c.exports),delete c.factory,c.exports=g,c.status=bb.EXECUTED,C("exec",c),g},t.resolve=function(a,b){var c={id:a,refUri:b};return C("resolve",c),c.uri||u.resolve(c.id,b)},t.define=function(a,c,d){var e=arguments.length;1===e?(d=a,a=b):2===e&&(d=c,y(a)?(c=a,a=b):c=b),!y(c)&&z(d)&&(c=s(""+d));var f={id:a,uri:t.resolve(a),deps:c,factory:d};if(!f.uri&&L.attachEvent){var g=r();g&&(f.uri=g.src)}C("define",f),f.uri?t.save(f.uri,f):Z=f},t.save=function(a,b){var c=t.get(a);c.status<bb.SAVED&&(c.id=b.id||a,c.dependencies=b.deps||[],c.factory=b.factory,c.status=bb.SAVED)},t.get=function(a,b){return Y[a]||(Y[a]=new t(a,b))},t.use=function(b,c,d){var e=t.get(d,y(b)?b:[b]);e.callback=function(){for(var b=[],d=e.resolve(),f=0,g=d.length;g>f;f++)b[f]=Y[d[f]].exec();c&&c.apply(a,b),delete e.callback},e.load()},t.preload=function(a){var b=v.preload,c=b.length;c?t.use(b,function(){b.splice(0,c),t.preload(a)},v.cwd+"_preload_"+d()):a()},u.use=function(a,b){return t.preload(function(){t.use(a,b,v.cwd+"_use_"+d())}),u},t.define.cmd={},a.define=t.define,u.Module=t,v.fetchedList=_,v.cid=d,u.require=function(a){var b=t.get(t.resolve(a));return b.status<bb.EXECUTING&&(b.onload(),b.exec()),b.exports};var cb=/^(.+?\/)(\?\?)?(seajs\/)+/;v.base=(P.match(cb)||["",P])[1],v.dir=P,v.cwd=M,v.charset="utf-8",v.preload=function(){var a=[],b=location.search.replace(/(seajs-\w+)(&|$)/g,"$1=1$2");return b+=" "+L.cookie,b.replace(/(seajs-\w+)=1/g,function(b,c){a.push(c)}),a}(),u.config=function(a){for(var b in a){var c=a[b],d=v[b];if(d&&w(d))for(var e in c)d[e]=c[e];else y(d)?c=d.concat(c):"base"===b&&("/"!==c.slice(-1)&&(c+="/"),c=l(c)),v[b]=c}return C("config",a),u}}}(this);
  4. define("core", [ "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  5. // Zepto animate extend
  6. require("zepto.extend.fx");
  7. // Zepto data extend
  8. require("zepto.extend.data");
  9. // Zepto selector extend
  10. require("zepto.extend.selector");
  11. var $ = window.Zepto, UI = $.AMUI || {}, $win = $(window), doc = window.document, $html = $("html");
  12. UI.support = {};
  13. UI.support.transition = function() {
  14. var transitionEnd = function() {
  15. // https://developer.mozilla.org/en-US/docs/Web/Events/transitionend#Browser_compatibility
  16. var element = doc.body || doc.documentElement, transEndEventNames = {
  17. WebkitTransition: "webkitTransitionEnd",
  18. MozTransition: "transitionend",
  19. OTransition: "oTransitionEnd otransitionend",
  20. transition: "transitionend"
  21. }, name;
  22. for (name in transEndEventNames) {
  23. if (element.style[name] !== undefined) return transEndEventNames[name];
  24. }
  25. }();
  26. return transitionEnd && {
  27. end: transitionEnd
  28. };
  29. }();
  30. UI.support.animation = function() {
  31. var animationEnd = function() {
  32. var element = doc.body || doc.documentElement, animEndEventNames = {
  33. WebkitAnimation: "webkitAnimationEnd",
  34. MozAnimation: "animationend",
  35. OAnimation: "oAnimationEnd oanimationend",
  36. animation: "animationend"
  37. }, name;
  38. for (name in animEndEventNames) {
  39. if (element.style[name] !== undefined) return animEndEventNames[name];
  40. }
  41. }();
  42. return animationEnd && {
  43. end: animationEnd
  44. };
  45. }();
  46. UI.support.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function(callback) {
  47. window.setTimeout(callback, 1e3 / 60);
  48. };
  49. UI.support.touch = "ontouchstart" in window && navigator.userAgent.toLowerCase().match(/mobile|tablet/) || window.DocumentTouch && document instanceof window.DocumentTouch || window.navigator["msPointerEnabled"] && window.navigator["msMaxTouchPoints"] > 0 || //IE 10
  50. window.navigator["pointerEnabled"] && window.navigator["maxTouchPoints"] > 0 || //IE >=11
  51. false;
  52. // https://developer.mozilla.org/zh-CN/docs/DOM/MutationObserver
  53. UI.support.mutationobserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver || null;
  54. UI.utils = {};
  55. /**
  56. * Debounce function
  57. * @param {function} func Function to be debounced
  58. * @param {number} wait Function execution threshold in milliseconds
  59. * @param {bool} immediate Whether the function should be called at
  60. * the beginning of the delay instead of the
  61. * end. Default is false.
  62. * @desc Executes a function when it stops being invoked for n seconds
  63. * @via _.debounce() http://underscorejs.org
  64. */
  65. UI.utils.debounce = function(func, wait, immediate) {
  66. var timeout;
  67. return function() {
  68. var context = this, args = arguments;
  69. var later = function() {
  70. timeout = null;
  71. if (!immediate) func.apply(context, args);
  72. };
  73. var callNow = immediate && !timeout;
  74. clearTimeout(timeout);
  75. timeout = setTimeout(later, wait);
  76. if (callNow) func.apply(context, args);
  77. };
  78. };
  79. UI.utils.isInView = function(element, options) {
  80. var $element = $(element);
  81. var visible = !!($element.width() || $element.height()) && $element.css("display") !== "none";
  82. if (!visible) {
  83. return false;
  84. }
  85. var window_left = $win.scrollLeft(), window_top = $win.scrollTop(), offset = $element.offset(), left = offset.left, top = offset.top;
  86. options = $.extend({
  87. topOffset: 0,
  88. leftOffset: 0
  89. }, options);
  90. return top + $element.height() >= window_top && top - options.topOffset <= window_top + $win.height() && left + $element.width() >= window_left && left - options.leftOffset <= window_left + $win.width();
  91. };
  92. UI.utils.parseOptions = UI.utils.options = function(string) {
  93. if ($.isPlainObject(string)) return string;
  94. var start = string ? string.indexOf("{") : -1, options = {};
  95. if (start != -1) {
  96. try {
  97. options = new Function("", "var json = " + string.substr(start) + "; return JSON.parse(JSON.stringify(json));")();
  98. } catch (e) {}
  99. }
  100. return options;
  101. };
  102. UI.utils.generateGUID = function(namespace) {
  103. var uid = namespace + "-" || "am-";
  104. do {
  105. uid += Math.random().toString(36).substring(2, 7);
  106. } while (document.getElementById(uid));
  107. return uid;
  108. };
  109. $.AMUI = UI;
  110. // http://blog.alexmaccaw.com/css-transitions
  111. $.fn.emulateTransitionEnd = function(duration) {
  112. var called = false, $el = this;
  113. $(this).one(UI.support.transition.end, function() {
  114. called = true;
  115. });
  116. var callback = function() {
  117. if (!called) {
  118. $($el).trigger(UI.support.transition.end);
  119. }
  120. };
  121. setTimeout(callback, duration);
  122. return this;
  123. };
  124. $.fn.redraw = function() {
  125. $(this).each(function() {
  126. var redraw = this.offsetHeight;
  127. });
  128. return this;
  129. };
  130. $.fn.transitionEnd = function(callback) {
  131. var endEvent = UI.support.transition.end, dom = this;
  132. function fireCallBack(e) {
  133. callback.call(this, e);
  134. endEvent && dom.off(endEvent, fireCallBack);
  135. }
  136. if (callback && endEvent) {
  137. dom.on(endEvent, fireCallBack);
  138. }
  139. return this;
  140. };
  141. $.fn.removeClassRegEx = function() {
  142. return this.each(function(regex) {
  143. var classes = $(this).attr("class");
  144. if (!classes || !regex) return false;
  145. var classArray = [];
  146. classes = classes.split(" ");
  147. for (var i = 0, len = classes.length; i < len; i++) if (!classes[i].match(regex)) classArray.push(classes[i]);
  148. $(this).attr("class", classArray.join(" "));
  149. });
  150. };
  151. //
  152. $.fn.alterClass = function(removals, additions) {
  153. var self = this;
  154. if (removals.indexOf("*") === -1) {
  155. // Use native jQuery methods if there is no wildcard matching
  156. self.removeClass(removals);
  157. return !additions ? self : self.addClass(additions);
  158. }
  159. var classPattern = new RegExp("\\s" + removals.replace(/\*/g, "[A-Za-z0-9-_]+").split(" ").join("\\s|\\s") + "\\s", "g");
  160. self.each(function(i, it) {
  161. var cn = " " + it.className + " ";
  162. while (classPattern.test(cn)) {
  163. cn = cn.replace(classPattern, " ");
  164. }
  165. it.className = $.trim(cn);
  166. });
  167. return !additions ? self : self.addClass(additions);
  168. };
  169. $.fn.getHeight = function() {
  170. var $ele = $(this), height = "auto";
  171. if ($ele.is(":visible")) {
  172. height = $ele.height();
  173. } else {
  174. var tmp = {
  175. position: $ele.css("position"),
  176. visibility: $ele.css("visibility"),
  177. display: $ele.css("display")
  178. };
  179. height = $ele.css({
  180. position: "absolute",
  181. visibility: "hidden",
  182. display: "block"
  183. }).height();
  184. $ele.css(tmp);
  185. }
  186. return height;
  187. };
  188. $.fn.getSize = function() {
  189. var $el = $(this);
  190. if ($el.css("display") !== "none") {
  191. return {
  192. width: $el.width(),
  193. height: $el.height()
  194. };
  195. }
  196. var old = {
  197. position: $el.css("position"),
  198. visibility: $el.css("visibility"),
  199. display: $el.css("display")
  200. }, tmpStyle = {
  201. display: "block",
  202. position: "absolute",
  203. visibility: "hidden"
  204. };
  205. $el.css(tmpStyle);
  206. var width = $el.width(), height = $el.height();
  207. $el.css(old);
  208. return {
  209. width: width,
  210. height: height
  211. };
  212. };
  213. // adding :visible and :hidden to zepto
  214. // https://github.com/jquery/jquery/blob/73e120116ce13b992d5229b3e10fcc19f9505a15/src/css/hiddenVisibleSelectors.js
  215. var _is = $.fn.is, _filter = $.fn.filter;
  216. function visible(elem) {
  217. elem = $(elem);
  218. return !!(elem.width() || elem.height()) && elem.css("display") !== "none";
  219. }
  220. $.fn.is = function(sel) {
  221. if (sel === ":visible") {
  222. return visible(this);
  223. }
  224. if (sel === ":hidden") {
  225. return !visible(this);
  226. }
  227. return _is.call(this, sel);
  228. };
  229. $.fn.filter = function(sel) {
  230. if (sel === ":visible") {
  231. return $([].filter.call(this, visible));
  232. }
  233. if (sel === ":hidden") {
  234. return $([].filter.call(this, function(elem) {
  235. return !visible(elem);
  236. }));
  237. }
  238. return _filter.call(this, sel);
  239. };
  240. // handle multiple browsers for requestAnimationFrame()
  241. // http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
  242. // https://github.com/gnarf/jquery-requestAnimationFrame
  243. UI.utils.rAF = function() {
  244. return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || //window.oRequestAnimationFrame ||
  245. // if all else fails, use setTimeout
  246. function(callback) {
  247. return window.setTimeout(callback, 1e3 / 60);
  248. };
  249. }();
  250. // handle multiple browsers for cancelAnimationFrame()
  251. UI.utils.cancelAF = function() {
  252. return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || //window.oCancelAnimationFrame ||
  253. function(id) {
  254. window.clearTimeout(id);
  255. };
  256. }();
  257. // Require fastclick.js on touch devices
  258. if (UI.support.touch) {
  259. require.async([ "util.fastclick" ], function(FastClick) {
  260. $(function() {
  261. FastClick && FastClick.attach(document.body);
  262. $html.addClass("am-touch");
  263. });
  264. });
  265. }
  266. // via http://davidwalsh.name/detect-scrollbar-width
  267. UI.utils.measureScrollbar = function() {
  268. if (document.body.clientWidth >= window.innerWidth) return 0;
  269. // if ($html.width() >= window.innerWidth) return;
  270. // var scrollbarWidth = window.innerWidth - $html.width();
  271. var $measure = $('<div style="width: 100px;height: 100px;overflow: scroll;position: absolute;top: -9999px;"></div>');
  272. $(document.body).append($measure);
  273. var scrollbarWidth = $measure[0].offsetWidth - $measure[0].clientWidth;
  274. $measure.remove();
  275. return scrollbarWidth;
  276. };
  277. UI.utils.imageLoader = function($image, callback) {
  278. function loaded() {
  279. callback($image[0]);
  280. }
  281. function bindLoad() {
  282. this.one("load", loaded);
  283. if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
  284. var src = this.attr("src"), param = src.match(/\?/) ? "&" : "?";
  285. param += "random=" + new Date().getTime();
  286. this.attr("src", src + param);
  287. }
  288. }
  289. if (!$image.attr("src")) {
  290. loaded();
  291. return;
  292. }
  293. if ($image[0].complete || $image[0].readyState === 4) {
  294. loaded();
  295. } else {
  296. bindLoad.call($image);
  297. }
  298. };
  299. $(function() {
  300. var $body = $("body");
  301. // trigger domready event
  302. $(document).trigger("domready:amui");
  303. $html.removeClass("no-js").addClass("js");
  304. UI.support.animation && $html.addClass("cssanimations");
  305. $(".am-topbar-fixed-top").length && $body.addClass("am-with-topbar-fixed-top");
  306. $(".am-topbar-fixed-bottom").length && $body.addClass("am-with-topbar-fixed-bottom");
  307. // Remove responsive classes in .am-layout
  308. var $layout = $(".am-layout");
  309. $layout.find('[class*="md-block-grid"]').alterClass("md-block-grid-*");
  310. $layout.find('[class*="lg-block-grid"]').alterClass("lg-block-grid");
  311. // widgets not in .am-layout
  312. $("[data-am-widget]").each(function() {
  313. var $widget = $(this);
  314. // console.log($widget.parents('.am-layout').length)
  315. if ($widget.parents(".am-layout").length === 0) {
  316. $widget.addClass("am-no-layout");
  317. }
  318. });
  319. });
  320. module.exports = UI;
  321. });
  322. define("util.fastclick", [], function(require, exports, module) {
  323. var $ = window.Zepto;
  324. /**
  325. * FastClick: polyfill to remove click delays on browsers with touch UIs.
  326. *
  327. * @version 1.0.2
  328. * @codingstandard ftlabs-jsv2
  329. * @copyright The Financial Times Limited [All Rights Reserved]
  330. * @license MIT License (see LICENSE.txt)
  331. */
  332. /*jslint browser:true, node:true*/
  333. /*global define, Event, Node*/
  334. /**
  335. * Instantiate fast-clicking listeners on the specified layer.
  336. *
  337. * @constructor
  338. * @param {Element} layer The layer to listen on
  339. * @param {Object} options The options to override the defaults
  340. */
  341. function FastClick(layer, options) {
  342. "use strict";
  343. var oldOnClick;
  344. options = options || {};
  345. /**
  346. * Whether a click is currently being tracked.
  347. *
  348. * @type boolean
  349. */
  350. this.trackingClick = false;
  351. /**
  352. * Timestamp for when click tracking started.
  353. *
  354. * @type number
  355. */
  356. this.trackingClickStart = 0;
  357. /**
  358. * The element being tracked for a click.
  359. *
  360. * @type EventTarget
  361. */
  362. this.targetElement = null;
  363. /**
  364. * X-coordinate of touch start event.
  365. *
  366. * @type number
  367. */
  368. this.touchStartX = 0;
  369. /**
  370. * Y-coordinate of touch start event.
  371. *
  372. * @type number
  373. */
  374. this.touchStartY = 0;
  375. /**
  376. * ID of the last touch, retrieved from Touch.identifier.
  377. *
  378. * @type number
  379. */
  380. this.lastTouchIdentifier = 0;
  381. /**
  382. * Touchmove boundary, beyond which a click will be cancelled.
  383. *
  384. * @type number
  385. */
  386. this.touchBoundary = options.touchBoundary || 10;
  387. /**
  388. * The FastClick layer.
  389. *
  390. * @type Element
  391. */
  392. this.layer = layer;
  393. /**
  394. * The minimum time between tap(touchstart and touchend) events
  395. *
  396. * @type number
  397. */
  398. this.tapDelay = options.tapDelay || 200;
  399. if (FastClick.notNeeded(layer)) {
  400. return;
  401. }
  402. // Some old versions of Android don't have Function.prototype.bind
  403. function bind(method, context) {
  404. return function() {
  405. return method.apply(context, arguments);
  406. };
  407. }
  408. var methods = [ "onMouse", "onClick", "onTouchStart", "onTouchMove", "onTouchEnd", "onTouchCancel" ];
  409. var context = this;
  410. for (var i = 0, l = methods.length; i < l; i++) {
  411. context[methods[i]] = bind(context[methods[i]], context);
  412. }
  413. // Set up event handlers as required
  414. if (deviceIsAndroid) {
  415. layer.addEventListener("mouseover", this.onMouse, true);
  416. layer.addEventListener("mousedown", this.onMouse, true);
  417. layer.addEventListener("mouseup", this.onMouse, true);
  418. }
  419. layer.addEventListener("click", this.onClick, true);
  420. layer.addEventListener("touchstart", this.onTouchStart, false);
  421. layer.addEventListener("touchmove", this.onTouchMove, false);
  422. layer.addEventListener("touchend", this.onTouchEnd, false);
  423. layer.addEventListener("touchcancel", this.onTouchCancel, false);
  424. // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
  425. // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
  426. // layer when they are cancelled.
  427. if (!Event.prototype.stopImmediatePropagation) {
  428. layer.removeEventListener = function(type, callback, capture) {
  429. var rmv = Node.prototype.removeEventListener;
  430. if (type === "click") {
  431. rmv.call(layer, type, callback.hijacked || callback, capture);
  432. } else {
  433. rmv.call(layer, type, callback, capture);
  434. }
  435. };
  436. layer.addEventListener = function(type, callback, capture) {
  437. var adv = Node.prototype.addEventListener;
  438. if (type === "click") {
  439. adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
  440. if (!event.propagationStopped) {
  441. callback(event);
  442. }
  443. }), capture);
  444. } else {
  445. adv.call(layer, type, callback, capture);
  446. }
  447. };
  448. }
  449. // If a handler is already declared in the element's onclick attribute, it will be fired before
  450. // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
  451. // adding it as listener.
  452. if (typeof layer.onclick === "function") {
  453. // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
  454. // - the old one won't work if passed to addEventListener directly.
  455. oldOnClick = layer.onclick;
  456. layer.addEventListener("click", function(event) {
  457. oldOnClick(event);
  458. }, false);
  459. layer.onclick = null;
  460. }
  461. }
  462. /**
  463. * Android requires exceptions.
  464. *
  465. * @type boolean
  466. */
  467. var deviceIsAndroid = navigator.userAgent.indexOf("Android") > 0;
  468. /**
  469. * iOS requires exceptions.
  470. *
  471. * @type boolean
  472. */
  473. var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);
  474. /**
  475. * iOS 4 requires an exception for select elements.
  476. *
  477. * @type boolean
  478. */
  479. var deviceIsIOS4 = deviceIsIOS && /OS 4_\d(_\d)?/.test(navigator.userAgent);
  480. /**
  481. * iOS 6.0(+?) requires the target element to be manually derived
  482. *
  483. * @type boolean
  484. */
  485. var deviceIsIOSWithBadTarget = deviceIsIOS && /OS ([6-9]|\d{2})_\d/.test(navigator.userAgent);
  486. /**
  487. * Determine whether a given element requires a native click.
  488. *
  489. * @param {EventTarget|Element} target Target DOM element
  490. * @returns {boolean} Returns true if the element needs a native click
  491. */
  492. FastClick.prototype.needsClick = function(target) {
  493. "use strict";
  494. switch (target.nodeName.toLowerCase()) {
  495. // Don't send a synthetic click to disabled inputs (issue #62)
  496. case "button":
  497. case "select":
  498. case "textarea":
  499. if (target.disabled) {
  500. return true;
  501. }
  502. break;
  503. case "input":
  504. // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
  505. if (deviceIsIOS && target.type === "file" || target.disabled) {
  506. return true;
  507. }
  508. break;
  509. case "label":
  510. case "video":
  511. return true;
  512. }
  513. return /\bneedsclick\b/.test(target.className);
  514. };
  515. /**
  516. * Determine whether a given element requires a call to focus to simulate click into element.
  517. *
  518. * @param {EventTarget|Element} target Target DOM element
  519. * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
  520. */
  521. FastClick.prototype.needsFocus = function(target) {
  522. "use strict";
  523. switch (target.nodeName.toLowerCase()) {
  524. case "textarea":
  525. return true;
  526. case "select":
  527. return !deviceIsAndroid;
  528. case "input":
  529. switch (target.type) {
  530. case "button":
  531. case "checkbox":
  532. case "file":
  533. case "image":
  534. case "radio":
  535. case "submit":
  536. return false;
  537. }
  538. // No point in attempting to focus disabled inputs
  539. return !target.disabled && !target.readOnly;
  540. default:
  541. return /\bneedsfocus\b/.test(target.className);
  542. }
  543. };
  544. /**
  545. * Send a click event to the specified element.
  546. *
  547. * @param {EventTarget|Element} targetElement
  548. * @param {Event} event
  549. */
  550. FastClick.prototype.sendClick = function(targetElement, event) {
  551. "use strict";
  552. var clickEvent, touch;
  553. // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
  554. if (document.activeElement && document.activeElement !== targetElement) {
  555. document.activeElement.blur();
  556. }
  557. touch = event.changedTouches[0];
  558. // Synthesise a click event, with an extra attribute so it can be tracked
  559. clickEvent = document.createEvent("MouseEvents");
  560. clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
  561. clickEvent.forwardedTouchEvent = true;
  562. targetElement.dispatchEvent(clickEvent);
  563. };
  564. FastClick.prototype.determineEventType = function(targetElement) {
  565. "use strict";
  566. //Issue #159: Android Chrome Select Box does not open with a synthetic click event
  567. if (deviceIsAndroid && targetElement.tagName.toLowerCase() === "select") {
  568. return "mousedown";
  569. }
  570. return "click";
  571. };
  572. /**
  573. * @param {EventTarget|Element} targetElement
  574. */
  575. FastClick.prototype.focus = function(targetElement) {
  576. "use strict";
  577. var length;
  578. // Issue #160: on iOS 7, some input elements (e.g. date datetime) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
  579. if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf("date") !== 0 && targetElement.type !== "time") {
  580. length = targetElement.value.length;
  581. targetElement.setSelectionRange(length, length);
  582. } else {
  583. targetElement.focus();
  584. }
  585. };
  586. /**
  587. * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
  588. *
  589. * @param {EventTarget|Element} targetElement
  590. */
  591. FastClick.prototype.updateScrollParent = function(targetElement) {
  592. "use strict";
  593. var scrollParent, parentElement;
  594. scrollParent = targetElement.fastClickScrollParent;
  595. // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
  596. // target element was moved to another parent.
  597. if (!scrollParent || !scrollParent.contains(targetElement)) {
  598. parentElement = targetElement;
  599. do {
  600. if (parentElement.scrollHeight > parentElement.offsetHeight) {
  601. scrollParent = parentElement;
  602. targetElement.fastClickScrollParent = parentElement;
  603. break;
  604. }
  605. parentElement = parentElement.parentElement;
  606. } while (parentElement);
  607. }
  608. // Always update the scroll top tracker if possible.
  609. if (scrollParent) {
  610. scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
  611. }
  612. };
  613. /**
  614. * @param {EventTarget} targetElement
  615. * @returns {Element|EventTarget}
  616. */
  617. FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
  618. "use strict";
  619. // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
  620. if (eventTarget.nodeType === Node.TEXT_NODE) {
  621. return eventTarget.parentNode;
  622. }
  623. return eventTarget;
  624. };
  625. /**
  626. * On touch start, record the position and scroll offset.
  627. *
  628. * @param {Event} event
  629. * @returns {boolean}
  630. */
  631. FastClick.prototype.onTouchStart = function(event) {
  632. "use strict";
  633. var targetElement, touch, selection;
  634. // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
  635. if (event.targetTouches.length > 1) {
  636. return true;
  637. }
  638. targetElement = this.getTargetElementFromEventTarget(event.target);
  639. touch = event.targetTouches[0];
  640. if (deviceIsIOS) {
  641. // Only trusted events will deselect text on iOS (issue #49)
  642. selection = window.getSelection();
  643. if (selection.rangeCount && !selection.isCollapsed) {
  644. return true;
  645. }
  646. if (!deviceIsIOS4) {
  647. // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
  648. // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
  649. // with the same identifier as the touch event that previously triggered the click that triggered the alert.
  650. // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
  651. // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
  652. if (touch.identifier === this.lastTouchIdentifier) {
  653. event.preventDefault();
  654. return false;
  655. }
  656. this.lastTouchIdentifier = touch.identifier;
  657. // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
  658. // 1) the user does a fling scroll on the scrollable layer
  659. // 2) the user stops the fling scroll with another tap
  660. // then the event.target of the last 'touchend' event will be the element that was under the user's finger
  661. // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
  662. // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
  663. this.updateScrollParent(targetElement);
  664. }
  665. }
  666. this.trackingClick = true;
  667. this.trackingClickStart = event.timeStamp;
  668. this.targetElement = targetElement;
  669. this.touchStartX = touch.pageX;
  670. this.touchStartY = touch.pageY;
  671. // Prevent phantom clicks on fast double-tap (issue #36)
  672. if (event.timeStamp - this.lastClickTime < this.tapDelay) {
  673. event.preventDefault();
  674. }
  675. return true;
  676. };
  677. /**
  678. * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
  679. *
  680. * @param {Event} event
  681. * @returns {boolean}
  682. */
  683. FastClick.prototype.touchHasMoved = function(event) {
  684. "use strict";
  685. var touch = event.changedTouches[0], boundary = this.touchBoundary;
  686. if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
  687. return true;
  688. }
  689. return false;
  690. };
  691. /**
  692. * Update the last position.
  693. *
  694. * @param {Event} event
  695. * @returns {boolean}
  696. */
  697. FastClick.prototype.onTouchMove = function(event) {
  698. "use strict";
  699. if (!this.trackingClick) {
  700. return true;
  701. }
  702. // If the touch has moved, cancel the click tracking
  703. if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
  704. this.trackingClick = false;
  705. this.targetElement = null;
  706. }
  707. return true;
  708. };
  709. /**
  710. * Attempt to find the labelled control for the given label element.
  711. *
  712. * @param {EventTarget|HTMLLabelElement} labelElement
  713. * @returns {Element|null}
  714. */
  715. FastClick.prototype.findControl = function(labelElement) {
  716. "use strict";
  717. // Fast path for newer browsers supporting the HTML5 control attribute
  718. if (labelElement.control !== undefined) {
  719. return labelElement.control;
  720. }
  721. // All browsers under test that support touch events also support the HTML5 htmlFor attribute
  722. if (labelElement.htmlFor) {
  723. return document.getElementById(labelElement.htmlFor);
  724. }
  725. // If no for attribute exists, attempt to retrieve the first labellable descendant element
  726. // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
  727. return labelElement.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea");
  728. };
  729. /**
  730. * On touch end, determine whether to send a click event at once.
  731. *
  732. * @param {Event} event
  733. * @returns {boolean}
  734. */
  735. FastClick.prototype.onTouchEnd = function(event) {
  736. "use strict";
  737. var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
  738. if (!this.trackingClick) {
  739. return true;
  740. }
  741. // Prevent phantom clicks on fast double-tap (issue #36)
  742. if (event.timeStamp - this.lastClickTime < this.tapDelay) {
  743. this.cancelNextClick = true;
  744. return true;
  745. }
  746. // Reset to prevent wrong click cancel on input (issue #156).
  747. this.cancelNextClick = false;
  748. this.lastClickTime = event.timeStamp;
  749. trackingClickStart = this.trackingClickStart;
  750. this.trackingClick = false;
  751. this.trackingClickStart = 0;
  752. // On some iOS devices, the targetElement supplied with the event is invalid if the layer
  753. // is performing a transition or scroll, and has to be re-detected manually. Note that
  754. // for this to function correctly, it must be called *after* the event target is checked!
  755. // See issue #57; also filed as rdar://13048589 .
  756. if (deviceIsIOSWithBadTarget) {
  757. touch = event.changedTouches[0];
  758. // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
  759. targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
  760. targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
  761. }
  762. targetTagName = targetElement.tagName.toLowerCase();
  763. if (targetTagName === "label") {
  764. forElement = this.findControl(targetElement);
  765. if (forElement) {
  766. this.focus(targetElement);
  767. if (deviceIsAndroid) {
  768. return false;
  769. }
  770. targetElement = forElement;
  771. }
  772. } else if (this.needsFocus(targetElement)) {
  773. // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
  774. // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
  775. if (event.timeStamp - trackingClickStart > 100 || deviceIsIOS && window.top !== window && targetTagName === "input") {
  776. this.targetElement = null;
  777. return false;
  778. }
  779. this.focus(targetElement);
  780. this.sendClick(targetElement, event);
  781. // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
  782. // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
  783. if (!deviceIsIOS || targetTagName !== "select") {
  784. this.targetElement = null;
  785. event.preventDefault();
  786. }
  787. return false;
  788. }
  789. if (deviceIsIOS && !deviceIsIOS4) {
  790. // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
  791. // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
  792. scrollParent = targetElement.fastClickScrollParent;
  793. if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
  794. return true;
  795. }
  796. }
  797. // Prevent the actual click from going though - unless the target node is marked as requiring
  798. // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
  799. if (!this.needsClick(targetElement)) {
  800. event.preventDefault();
  801. this.sendClick(targetElement, event);
  802. }
  803. return false;
  804. };
  805. /**
  806. * On touch cancel, stop tracking the click.
  807. *
  808. * @returns {void}
  809. */
  810. FastClick.prototype.onTouchCancel = function() {
  811. "use strict";
  812. this.trackingClick = false;
  813. this.targetElement = null;
  814. };
  815. /**
  816. * Determine mouse events which should be permitted.
  817. *
  818. * @param {Event} event
  819. * @returns {boolean}
  820. */
  821. FastClick.prototype.onMouse = function(event) {
  822. "use strict";
  823. // If a target element was never set (because a touch event was never fired) allow the event
  824. if (!this.targetElement) {
  825. return true;
  826. }
  827. if (event.forwardedTouchEvent) {
  828. return true;
  829. }
  830. // Programmatically generated events targeting a specific element should be permitted
  831. if (!event.cancelable) {
  832. return true;
  833. }
  834. // Derive and check the target element to see whether the mouse event needs to be permitted;
  835. // unless explicitly enabled, prevent non-touch click events from triggering actions,
  836. // to prevent ghost/doubleclicks.
  837. if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
  838. // Prevent any user-added listeners declared on FastClick element from being fired.
  839. if (event.stopImmediatePropagation) {
  840. event.stopImmediatePropagation();
  841. } else {
  842. // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
  843. event.propagationStopped = true;
  844. }
  845. // Cancel the event
  846. event.stopPropagation();
  847. event.preventDefault();
  848. return false;
  849. }
  850. // If the mouse event is permitted, return true for the action to go through.
  851. return true;
  852. };
  853. /**
  854. * On actual clicks, determine whether this is a touch-generated click, a click action occurring
  855. * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
  856. * an actual click which should be permitted.
  857. *
  858. * @param {Event} event
  859. * @returns {boolean}
  860. */
  861. FastClick.prototype.onClick = function(event) {
  862. "use strict";
  863. var permitted;
  864. // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
  865. if (this.trackingClick) {
  866. this.targetElement = null;
  867. this.trackingClick = false;
  868. return true;
  869. }
  870. // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
  871. if (event.target.type === "submit" && event.detail === 0) {
  872. return true;
  873. }
  874. permitted = this.onMouse(event);
  875. // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
  876. if (!permitted) {
  877. this.targetElement = null;
  878. }
  879. // If clicks are permitted, return true for the action to go through.
  880. return permitted;
  881. };
  882. /**
  883. * Remove all FastClick's event listeners.
  884. *
  885. * @returns {void}
  886. */
  887. FastClick.prototype.destroy = function() {
  888. "use strict";
  889. var layer = this.layer;
  890. if (deviceIsAndroid) {
  891. layer.removeEventListener("mouseover", this.onMouse, true);
  892. layer.removeEventListener("mousedown", this.onMouse, true);
  893. layer.removeEventListener("mouseup", this.onMouse, true);
  894. }
  895. layer.removeEventListener("click", this.onClick, true);
  896. layer.removeEventListener("touchstart", this.onTouchStart, false);
  897. layer.removeEventListener("touchmove", this.onTouchMove, false);
  898. layer.removeEventListener("touchend", this.onTouchEnd, false);
  899. layer.removeEventListener("touchcancel", this.onTouchCancel, false);
  900. };
  901. /**
  902. * Check whether FastClick is needed.
  903. *
  904. * @param {Element} layer The layer to listen on
  905. */
  906. FastClick.notNeeded = function(layer) {
  907. "use strict";
  908. var metaViewport;
  909. var chromeVersion;
  910. // Devices that don't support touch don't need FastClick
  911. if (typeof window.ontouchstart === "undefined") {
  912. return true;
  913. }
  914. // Chrome version - zero for other browsers
  915. chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [ , 0 ])[1];
  916. if (chromeVersion) {
  917. if (deviceIsAndroid) {
  918. metaViewport = document.querySelector("meta[name=viewport]");
  919. if (metaViewport) {
  920. // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
  921. if (metaViewport.content.indexOf("user-scalable=no") !== -1) {
  922. return true;
  923. }
  924. // Chrome 32 and above with width=device-width or less don't need FastClick
  925. if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
  926. return true;
  927. }
  928. }
  929. } else {
  930. return true;
  931. }
  932. }
  933. // IE10 with -ms-touch-action: none, which disables double-tap-to-zoom (issue #97)
  934. if (layer.style.msTouchAction === "none") {
  935. return true;
  936. }
  937. return false;
  938. };
  939. /**
  940. * Factory method for creating a FastClick object
  941. *
  942. * @param {Element} layer The layer to listen on
  943. * @param {Object} options The options to override the defaults
  944. */
  945. FastClick.attach = function(layer, options) {
  946. "use strict";
  947. return new FastClick(layer, options);
  948. };
  949. module.exports = FastClick;
  950. });
  951. define("util.hammer", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  952. require("core");
  953. var $ = window.Zepto, UI = $.AMUI;
  954. /**
  955. * Hammer.js
  956. * @via https://github.com/hammerjs/hammer.js
  957. * @copyright Copyright (C) 2011-2014 by Jorik Tangelder (Eight Media)
  958. * @license https://github.com/hammerjs/hammer.js/blob/master/LICENSE.md
  959. */
  960. var VENDOR_PREFIXES = [ "", "webkit", "moz", "MS", "ms", "o" ];
  961. var TEST_ELEMENT = document.createElement("div");
  962. var TYPE_FUNCTION = "function";
  963. var round = Math.round;
  964. var abs = Math.abs;
  965. var now = Date.now;
  966. /**
  967. * set a timeout with a given scope
  968. * @param {Function} fn
  969. * @param {Number} timeout
  970. * @param {Object} context
  971. * @returns {number}
  972. */
  973. function setTimeoutContext(fn, timeout, context) {
  974. return setTimeout(bindFn(fn, context), timeout);
  975. }
  976. /**
  977. * if the argument is an array, we want to execute the fn on each entry
  978. * if it aint an array we don't want to do a thing.
  979. * this is used by all the methods that accept a single and array argument.
  980. * @param {*|Array} arg
  981. * @param {String} fn
  982. * @param {Object} [context]
  983. * @returns {Boolean}
  984. */
  985. function invokeArrayArg(arg, fn, context) {
  986. if (Array.isArray(arg)) {
  987. each(arg, context[fn], context);
  988. return true;
  989. }
  990. return false;
  991. }
  992. /**
  993. * walk objects and arrays
  994. * @param {Object} obj
  995. * @param {Function} iterator
  996. * @param {Object} context
  997. */
  998. function each(obj, iterator, context) {
  999. var i;
  1000. if (!obj) {
  1001. return;
  1002. }
  1003. if (obj.forEach) {
  1004. obj.forEach(iterator, context);
  1005. } else if (obj.length !== undefined) {
  1006. i = 0;
  1007. while (i < obj.length) {
  1008. iterator.call(context, obj[i], i, obj);
  1009. i++;
  1010. }
  1011. } else {
  1012. for (i in obj) {
  1013. obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
  1014. }
  1015. }
  1016. }
  1017. /**
  1018. * extend object.
  1019. * means that properties in dest will be overwritten by the ones in src.
  1020. * @param {Object} dest
  1021. * @param {Object} src
  1022. * @param {Boolean} [merge]
  1023. * @returns {Object} dest
  1024. */
  1025. function extend(dest, src, merge) {
  1026. var keys = Object.keys(src);
  1027. var i = 0;
  1028. while (i < keys.length) {
  1029. if (!merge || merge && dest[keys[i]] === undefined) {
  1030. dest[keys[i]] = src[keys[i]];
  1031. }
  1032. i++;
  1033. }
  1034. return dest;
  1035. }
  1036. /**
  1037. * merge the values from src in the dest.
  1038. * means that properties that exist in dest will not be overwritten by src
  1039. * @param {Object} dest
  1040. * @param {Object} src
  1041. * @returns {Object} dest
  1042. */
  1043. function merge(dest, src) {
  1044. return extend(dest, src, true);
  1045. }
  1046. /**
  1047. * simple class inheritance
  1048. * @param {Function} child
  1049. * @param {Function} base
  1050. * @param {Object} [properties]
  1051. */
  1052. function inherit(child, base, properties) {
  1053. var baseP = base.prototype, childP;
  1054. childP = child.prototype = Object.create(baseP);
  1055. childP.constructor = child;
  1056. childP._super = baseP;
  1057. if (properties) {
  1058. extend(childP, properties);
  1059. }
  1060. }
  1061. /**
  1062. * simple function bind
  1063. * @param {Function} fn
  1064. * @param {Object} context
  1065. * @returns {Function}
  1066. */
  1067. function bindFn(fn, context) {
  1068. return function boundFn() {
  1069. return fn.apply(context, arguments);
  1070. };
  1071. }
  1072. /**
  1073. * let a boolean value also be a function that must return a boolean
  1074. * this first item in args will be used as the context
  1075. * @param {Boolean|Function} val
  1076. * @param {Array} [args]
  1077. * @returns {Boolean}
  1078. */
  1079. function boolOrFn(val, args) {
  1080. if (typeof val == TYPE_FUNCTION) {
  1081. return val.apply(args ? args[0] || undefined : undefined, args);
  1082. }
  1083. return val;
  1084. }
  1085. /**
  1086. * use the val2 when val1 is undefined
  1087. * @param {*} val1
  1088. * @param {*} val2
  1089. * @returns {*}
  1090. */
  1091. function ifUndefined(val1, val2) {
  1092. return val1 === undefined ? val2 : val1;
  1093. }
  1094. /**
  1095. * addEventListener with multiple events at once
  1096. * @param {EventTarget} target
  1097. * @param {String} types
  1098. * @param {Function} handler
  1099. */
  1100. function addEventListeners(target, types, handler) {
  1101. each(splitStr(types), function(type) {
  1102. target.addEventListener(type, handler, false);
  1103. });
  1104. }
  1105. /**
  1106. * removeEventListener with multiple events at once
  1107. * @param {EventTarget} target
  1108. * @param {String} types
  1109. * @param {Function} handler
  1110. */
  1111. function removeEventListeners(target, types, handler) {
  1112. each(splitStr(types), function(type) {
  1113. target.removeEventListener(type, handler, false);
  1114. });
  1115. }
  1116. /**
  1117. * find if a node is in the given parent
  1118. * @method hasParent
  1119. * @param {HTMLElement} node
  1120. * @param {HTMLElement} parent
  1121. * @return {Boolean} found
  1122. */
  1123. function hasParent(node, parent) {
  1124. while (node) {
  1125. if (node == parent) {
  1126. return true;
  1127. }
  1128. node = node.parentNode;
  1129. }
  1130. return false;
  1131. }
  1132. /**
  1133. * small indexOf wrapper
  1134. * @param {String} str
  1135. * @param {String} find
  1136. * @returns {Boolean} found
  1137. */
  1138. function inStr(str, find) {
  1139. return str.indexOf(find) > -1;
  1140. }
  1141. /**
  1142. * split string on whitespace
  1143. * @param {String} str
  1144. * @returns {Array} words
  1145. */
  1146. function splitStr(str) {
  1147. return str.trim().split(/\s+/g);
  1148. }
  1149. /**
  1150. * find if a array contains the object using indexOf or a simple polyFill
  1151. * @param {Array} src
  1152. * @param {String} find
  1153. * @param {String} [findByKey]
  1154. * @return {Boolean|Number} false when not found, or the index
  1155. */
  1156. function inArray(src, find, findByKey) {
  1157. if (src.indexOf && !findByKey) {
  1158. return src.indexOf(find);
  1159. } else {
  1160. var i = 0;
  1161. while (i < src.length) {
  1162. if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) {
  1163. return i;
  1164. }
  1165. i++;
  1166. }
  1167. return -1;
  1168. }
  1169. }
  1170. /**
  1171. * convert array-like objects to real arrays
  1172. * @param {Object} obj
  1173. * @returns {Array}
  1174. */
  1175. function toArray(obj) {
  1176. return Array.prototype.slice.call(obj, 0);
  1177. }
  1178. /**
  1179. * unique array with objects based on a key (like 'id') or just by the array's value
  1180. * @param {Array} src [{id:1},{id:2},{id:1}]
  1181. * @param {String} [key]
  1182. * @param {Boolean} [sort=False]
  1183. * @returns {Array} [{id:1},{id:2}]
  1184. */
  1185. function uniqueArray(src, key, sort) {
  1186. var results = [];
  1187. var values = [];
  1188. var i = 0;
  1189. while (i < src.length) {
  1190. var val = key ? src[i][key] : src[i];
  1191. if (inArray(values, val) < 0) {
  1192. results.push(src[i]);
  1193. }
  1194. values[i] = val;
  1195. i++;
  1196. }
  1197. if (sort) {
  1198. if (!key) {
  1199. results = results.sort();
  1200. } else {
  1201. results = results.sort(function sortUniqueArray(a, b) {
  1202. return a[key] > b[key];
  1203. });
  1204. }
  1205. }
  1206. return results;
  1207. }
  1208. /**
  1209. * get the prefixed property
  1210. * @param {Object} obj
  1211. * @param {String} property
  1212. * @returns {String|Undefined} prefixed
  1213. */
  1214. function prefixed(obj, property) {
  1215. var prefix, prop;
  1216. var camelProp = property[0].toUpperCase() + property.slice(1);
  1217. var i = 0;
  1218. while (i < VENDOR_PREFIXES.length) {
  1219. prefix = VENDOR_PREFIXES[i];
  1220. prop = prefix ? prefix + camelProp : property;
  1221. if (prop in obj) {
  1222. return prop;
  1223. }
  1224. i++;
  1225. }
  1226. return undefined;
  1227. }
  1228. /**
  1229. * get a unique id
  1230. * @returns {number} uniqueId
  1231. */
  1232. var _uniqueId = 1;
  1233. function uniqueId() {
  1234. return _uniqueId++;
  1235. }
  1236. /**
  1237. * get the window object of an element
  1238. * @param {HTMLElement} element
  1239. * @returns {DocumentView|Window}
  1240. */
  1241. function getWindowForElement(element) {
  1242. var doc = element.ownerDocument;
  1243. return doc.defaultView || doc.parentWindow;
  1244. }
  1245. var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
  1246. var SUPPORT_TOUCH = "ontouchstart" in window;
  1247. var SUPPORT_POINTER_EVENTS = prefixed(window, "PointerEvent") !== undefined;
  1248. var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
  1249. var INPUT_TYPE_TOUCH = "touch";
  1250. var INPUT_TYPE_PEN = "pen";
  1251. var INPUT_TYPE_MOUSE = "mouse";
  1252. var INPUT_TYPE_KINECT = "kinect";
  1253. var COMPUTE_INTERVAL = 25;
  1254. var INPUT_START = 1;
  1255. var INPUT_MOVE = 2;
  1256. var INPUT_END = 4;
  1257. var INPUT_CANCEL = 8;
  1258. var DIRECTION_NONE = 1;
  1259. var DIRECTION_LEFT = 2;
  1260. var DIRECTION_RIGHT = 4;
  1261. var DIRECTION_UP = 8;
  1262. var DIRECTION_DOWN = 16;
  1263. var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
  1264. var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
  1265. var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
  1266. var PROPS_XY = [ "x", "y" ];
  1267. var PROPS_CLIENT_XY = [ "clientX", "clientY" ];
  1268. /**
  1269. * create new input type manager
  1270. * @param {Manager} manager
  1271. * @param {Function} callback
  1272. * @returns {Input}
  1273. * @constructor
  1274. */
  1275. function Input(manager, callback) {
  1276. var self = this;
  1277. this.manager = manager;
  1278. this.callback = callback;
  1279. this.element = manager.element;
  1280. this.target = manager.options.inputTarget;
  1281. // smaller wrapper around the handler, for the scope and the enabled state of the manager,
  1282. // so when disabled the input events are completely bypassed.
  1283. this.domHandler = function(ev) {
  1284. if (boolOrFn(manager.options.enable, [ manager ])) {
  1285. self.handler(ev);
  1286. }
  1287. };
  1288. this.init();
  1289. }
  1290. Input.prototype = {
  1291. /**
  1292. * should handle the inputEvent data and trigger the callback
  1293. * @virtual
  1294. */
  1295. handler: function() {},
  1296. /**
  1297. * bind the events
  1298. */
  1299. init: function() {
  1300. this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
  1301. this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
  1302. this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1303. },
  1304. /**
  1305. * unbind the events
  1306. */
  1307. destroy: function() {
  1308. this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
  1309. this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
  1310. this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
  1311. }
  1312. };
  1313. /**
  1314. * create new input type manager
  1315. * called by the Manager constructor
  1316. * @param {Hammer} manager
  1317. * @returns {Input}
  1318. */
  1319. function createInputInstance(manager) {
  1320. var Type;
  1321. var inputClass = manager.options.inputClass;
  1322. if (inputClass) {
  1323. Type = inputClass;
  1324. } else if (SUPPORT_POINTER_EVENTS) {
  1325. Type = PointerEventInput;
  1326. } else if (SUPPORT_ONLY_TOUCH) {
  1327. Type = TouchInput;
  1328. } else if (!SUPPORT_TOUCH) {
  1329. Type = MouseInput;
  1330. } else {
  1331. Type = TouchMouseInput;
  1332. }
  1333. return new Type(manager, inputHandler);
  1334. }
  1335. /**
  1336. * handle input events
  1337. * @param {Manager} manager
  1338. * @param {String} eventType
  1339. * @param {Object} input
  1340. */
  1341. function inputHandler(manager, eventType, input) {
  1342. var pointersLen = input.pointers.length;
  1343. var changedPointersLen = input.changedPointers.length;
  1344. var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0;
  1345. var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0;
  1346. input.isFirst = !!isFirst;
  1347. input.isFinal = !!isFinal;
  1348. if (isFirst) {
  1349. manager.session = {};
  1350. }
  1351. // source event is the normalized value of the domEvents
  1352. // like 'touchstart, mouseup, pointerdown'
  1353. input.eventType = eventType;
  1354. // compute scale, rotation etc
  1355. computeInputData(manager, input);
  1356. // emit secret event
  1357. manager.emit("hammer.input", input);
  1358. manager.recognize(input);
  1359. manager.session.prevInput = input;
  1360. }
  1361. /**
  1362. * extend the data with some usable properties like scale, rotate, velocity etc
  1363. * @param {Object} manager
  1364. * @param {Object} input
  1365. */
  1366. function computeInputData(manager, input) {
  1367. var session = manager.session;
  1368. var pointers = input.pointers;
  1369. var pointersLength = pointers.length;
  1370. // store the first input to calculate the distance and direction
  1371. if (!session.firstInput) {
  1372. session.firstInput = simpleCloneInputData(input);
  1373. }
  1374. // to compute scale and rotation we need to store the multiple touches
  1375. if (pointersLength > 1 && !session.firstMultiple) {
  1376. session.firstMultiple = simpleCloneInputData(input);
  1377. } else if (pointersLength === 1) {
  1378. session.firstMultiple = false;
  1379. }
  1380. var firstInput = session.firstInput;
  1381. var firstMultiple = session.firstMultiple;
  1382. var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
  1383. var center = input.center = getCenter(pointers);
  1384. input.timeStamp = now();
  1385. input.deltaTime = input.timeStamp - firstInput.timeStamp;
  1386. input.angle = getAngle(offsetCenter, center);
  1387. input.distance = getDistance(offsetCenter, center);
  1388. computeDeltaXY(session, input);
  1389. input.offsetDirection = getDirection(input.deltaX, input.deltaY);
  1390. input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
  1391. input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
  1392. computeIntervalInputData(session, input);
  1393. // find the correct target
  1394. var target = manager.element;
  1395. if (hasParent(input.srcEvent.target, target)) {
  1396. target = input.srcEvent.target;
  1397. }
  1398. input.target = target;
  1399. }
  1400. function computeDeltaXY(session, input) {
  1401. var center = input.center;
  1402. var offset = session.offsetDelta || {};
  1403. var prevDelta = session.prevDelta || {};
  1404. var prevInput = session.prevInput || {};
  1405. if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
  1406. prevDelta = session.prevDelta = {
  1407. x: prevInput.deltaX || 0,
  1408. y: prevInput.deltaY || 0
  1409. };
  1410. offset = session.offsetDelta = {
  1411. x: center.x,
  1412. y: center.y
  1413. };
  1414. }
  1415. input.deltaX = prevDelta.x + (center.x - offset.x);
  1416. input.deltaY = prevDelta.y + (center.y - offset.y);
  1417. }
  1418. /**
  1419. * velocity is calculated every x ms
  1420. * @param {Object} session
  1421. * @param {Object} input
  1422. */
  1423. function computeIntervalInputData(session, input) {
  1424. var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction;
  1425. if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
  1426. var deltaX = last.deltaX - input.deltaX;
  1427. var deltaY = last.deltaY - input.deltaY;
  1428. var v = getVelocity(deltaTime, deltaX, deltaY);
  1429. velocityX = v.x;
  1430. velocityY = v.y;
  1431. velocity = abs(v.x) > abs(v.y) ? v.x : v.y;
  1432. direction = getDirection(deltaX, deltaY);
  1433. session.lastInterval = input;
  1434. } else {
  1435. // use latest velocity info if it doesn't overtake a minimum period
  1436. velocity = last.velocity;
  1437. velocityX = last.velocityX;
  1438. velocityY = last.velocityY;
  1439. direction = last.direction;
  1440. }
  1441. input.velocity = velocity;
  1442. input.velocityX = velocityX;
  1443. input.velocityY = velocityY;
  1444. input.direction = direction;
  1445. }
  1446. /**
  1447. * create a simple clone from the input used for storage of firstInput and firstMultiple
  1448. * @param {Object} input
  1449. * @returns {Object} clonedInputData
  1450. */
  1451. function simpleCloneInputData(input) {
  1452. // make a simple copy of the pointers because we will get a reference if we don't
  1453. // we only need clientXY for the calculations
  1454. var pointers = [];
  1455. var i = 0;
  1456. while (i < input.pointers.length) {
  1457. pointers[i] = {
  1458. clientX: round(input.pointers[i].clientX),
  1459. clientY: round(input.pointers[i].clientY)
  1460. };
  1461. i++;
  1462. }
  1463. return {
  1464. timeStamp: now(),
  1465. pointers: pointers,
  1466. center: getCenter(pointers),
  1467. deltaX: input.deltaX,
  1468. deltaY: input.deltaY
  1469. };
  1470. }
  1471. /**
  1472. * get the center of all the pointers
  1473. * @param {Array} pointers
  1474. * @return {Object} center contains `x` and `y` properties
  1475. */
  1476. function getCenter(pointers) {
  1477. var pointersLength = pointers.length;
  1478. // no need to loop when only one touch
  1479. if (pointersLength === 1) {
  1480. return {
  1481. x: round(pointers[0].clientX),
  1482. y: round(pointers[0].clientY)
  1483. };
  1484. }
  1485. var x = 0, y = 0, i = 0;
  1486. while (i < pointersLength) {
  1487. x += pointers[i].clientX;
  1488. y += pointers[i].clientY;
  1489. i++;
  1490. }
  1491. return {
  1492. x: round(x / pointersLength),
  1493. y: round(y / pointersLength)
  1494. };
  1495. }
  1496. /**
  1497. * calculate the velocity between two points. unit is in px per ms.
  1498. * @param {Number} deltaTime
  1499. * @param {Number} x
  1500. * @param {Number} y
  1501. * @return {Object} velocity `x` and `y`
  1502. */
  1503. function getVelocity(deltaTime, x, y) {
  1504. return {
  1505. x: x / deltaTime || 0,
  1506. y: y / deltaTime || 0
  1507. };
  1508. }
  1509. /**
  1510. * get the direction between two points
  1511. * @param {Number} x
  1512. * @param {Number} y
  1513. * @return {Number} direction
  1514. */
  1515. function getDirection(x, y) {
  1516. if (x === y) {
  1517. return DIRECTION_NONE;
  1518. }
  1519. if (abs(x) >= abs(y)) {
  1520. return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  1521. }
  1522. return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
  1523. }
  1524. /**
  1525. * calculate the absolute distance between two points
  1526. * @param {Object} p1 {x, y}
  1527. * @param {Object} p2 {x, y}
  1528. * @param {Array} [props] containing x and y keys
  1529. * @return {Number} distance
  1530. */
  1531. function getDistance(p1, p2, props) {
  1532. if (!props) {
  1533. props = PROPS_XY;
  1534. }
  1535. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1536. return Math.sqrt(x * x + y * y);
  1537. }
  1538. /**
  1539. * calculate the angle between two coordinates
  1540. * @param {Object} p1
  1541. * @param {Object} p2
  1542. * @param {Array} [props] containing x and y keys
  1543. * @return {Number} angle
  1544. */
  1545. function getAngle(p1, p2, props) {
  1546. if (!props) {
  1547. props = PROPS_XY;
  1548. }
  1549. var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
  1550. return Math.atan2(y, x) * 180 / Math.PI;
  1551. }
  1552. /**
  1553. * calculate the rotation degrees between two pointersets
  1554. * @param {Array} start array of pointers
  1555. * @param {Array} end array of pointers
  1556. * @return {Number} rotation
  1557. */
  1558. function getRotation(start, end) {
  1559. return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
  1560. }
  1561. /**
  1562. * calculate the scale factor between two pointersets
  1563. * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
  1564. * @param {Array} start array of pointers
  1565. * @param {Array} end array of pointers
  1566. * @return {Number} scale
  1567. */
  1568. function getScale(start, end) {
  1569. return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
  1570. }
  1571. var MOUSE_INPUT_MAP = {
  1572. mousedown: INPUT_START,
  1573. mousemove: INPUT_MOVE,
  1574. mouseup: INPUT_END
  1575. };
  1576. var MOUSE_ELEMENT_EVENTS = "mousedown";
  1577. var MOUSE_WINDOW_EVENTS = "mousemove mouseup";
  1578. /**
  1579. * Mouse events input
  1580. * @constructor
  1581. * @extends Input
  1582. */
  1583. function MouseInput() {
  1584. this.evEl = MOUSE_ELEMENT_EVENTS;
  1585. this.evWin = MOUSE_WINDOW_EVENTS;
  1586. this.allow = true;
  1587. // used by Input.TouchMouse to disable mouse events
  1588. this.pressed = false;
  1589. // mousedown state
  1590. Input.apply(this, arguments);
  1591. }
  1592. inherit(MouseInput, Input, {
  1593. /**
  1594. * handle mouse events
  1595. * @param {Object} ev
  1596. */
  1597. handler: function MEhandler(ev) {
  1598. var eventType = MOUSE_INPUT_MAP[ev.type];
  1599. // on start we want to have the left mouse button down
  1600. if (eventType & INPUT_START && ev.button === 0) {
  1601. this.pressed = true;
  1602. }
  1603. if (eventType & INPUT_MOVE && ev.which !== 1) {
  1604. eventType = INPUT_END;
  1605. }
  1606. // mouse must be down, and mouse events are allowed (see the TouchMouse input)
  1607. if (!this.pressed || !this.allow) {
  1608. return;
  1609. }
  1610. if (eventType & INPUT_END) {
  1611. this.pressed = false;
  1612. }
  1613. this.callback(this.manager, eventType, {
  1614. pointers: [ ev ],
  1615. changedPointers: [ ev ],
  1616. pointerType: INPUT_TYPE_MOUSE,
  1617. srcEvent: ev
  1618. });
  1619. }
  1620. });
  1621. var POINTER_INPUT_MAP = {
  1622. pointerdown: INPUT_START,
  1623. pointermove: INPUT_MOVE,
  1624. pointerup: INPUT_END,
  1625. pointercancel: INPUT_CANCEL,
  1626. pointerout: INPUT_CANCEL
  1627. };
  1628. // in IE10 the pointer types is defined as an enum
  1629. var IE10_POINTER_TYPE_ENUM = {
  1630. 2: INPUT_TYPE_TOUCH,
  1631. 3: INPUT_TYPE_PEN,
  1632. 4: INPUT_TYPE_MOUSE,
  1633. 5: INPUT_TYPE_KINECT
  1634. };
  1635. var POINTER_ELEMENT_EVENTS = "pointerdown";
  1636. var POINTER_WINDOW_EVENTS = "pointermove pointerup pointercancel";
  1637. // IE10 has prefixed support, and case-sensitive
  1638. if (window.MSPointerEvent) {
  1639. POINTER_ELEMENT_EVENTS = "MSPointerDown";
  1640. POINTER_WINDOW_EVENTS = "MSPointerMove MSPointerUp MSPointerCancel";
  1641. }
  1642. /**
  1643. * Pointer events input
  1644. * @constructor
  1645. * @extends Input
  1646. */
  1647. function PointerEventInput() {
  1648. this.evEl = POINTER_ELEMENT_EVENTS;
  1649. this.evWin = POINTER_WINDOW_EVENTS;
  1650. Input.apply(this, arguments);
  1651. this.store = this.manager.session.pointerEvents = [];
  1652. }
  1653. inherit(PointerEventInput, Input, {
  1654. /**
  1655. * handle mouse events
  1656. * @param {Object} ev
  1657. */
  1658. handler: function PEhandler(ev) {
  1659. var store = this.store;
  1660. var removePointer = false;
  1661. var eventTypeNormalized = ev.type.toLowerCase().replace("ms", "");
  1662. var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
  1663. var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
  1664. var isTouch = pointerType == INPUT_TYPE_TOUCH;
  1665. // start and mouse must be down
  1666. if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
  1667. store.push(ev);
  1668. } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
  1669. removePointer = true;
  1670. }
  1671. // get index of the event in the store
  1672. // it not found, so the pointer hasn't been down (so it's probably a hover)
  1673. var storeIndex = inArray(store, ev.pointerId, "pointerId");
  1674. if (storeIndex < 0) {
  1675. return;
  1676. }
  1677. // update the event in the store
  1678. store[storeIndex] = ev;
  1679. this.callback(this.manager, eventType, {
  1680. pointers: store,
  1681. changedPointers: [ ev ],
  1682. pointerType: pointerType,
  1683. srcEvent: ev
  1684. });
  1685. if (removePointer) {
  1686. // remove from the store
  1687. store.splice(storeIndex, 1);
  1688. }
  1689. }
  1690. });
  1691. var TOUCH_INPUT_MAP = {
  1692. touchstart: INPUT_START,
  1693. touchmove: INPUT_MOVE,
  1694. touchend: INPUT_END,
  1695. touchcancel: INPUT_CANCEL
  1696. };
  1697. var TOUCH_TARGET_EVENTS = "touchstart touchmove touchend touchcancel";
  1698. /**
  1699. * Touch events input
  1700. * @constructor
  1701. * @extends Input
  1702. */
  1703. function TouchInput() {
  1704. this.evTarget = TOUCH_TARGET_EVENTS;
  1705. this.targetIds = {};
  1706. Input.apply(this, arguments);
  1707. }
  1708. inherit(TouchInput, Input, {
  1709. /**
  1710. * handle touch events
  1711. * @param {Object} ev
  1712. */
  1713. handler: function TEhandler(ev) {
  1714. var type = TOUCH_INPUT_MAP[ev.type];
  1715. var touches = getTouches.call(this, ev, type);
  1716. if (!touches) {
  1717. return;
  1718. }
  1719. this.callback(this.manager, type, {
  1720. pointers: touches[0],
  1721. changedPointers: touches[1],
  1722. pointerType: INPUT_TYPE_TOUCH,
  1723. srcEvent: ev
  1724. });
  1725. }
  1726. });
  1727. /**
  1728. * @this {TouchInput}
  1729. * @param {Object} ev
  1730. * @param {Number} type flag
  1731. * @returns {undefined|Array} [all, changed]
  1732. */
  1733. function getTouches(ev, type) {
  1734. var allTouches = toArray(ev.touches);
  1735. var targetIds = this.targetIds;
  1736. // when there is only one touch, the process can be simplified
  1737. if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
  1738. targetIds[allTouches[0].identifier] = true;
  1739. return [ allTouches, allTouches ];
  1740. }
  1741. var i, targetTouches = toArray(ev.targetTouches), changedTouches = toArray(ev.changedTouches), changedTargetTouches = [];
  1742. // collect touches
  1743. if (type === INPUT_START) {
  1744. i = 0;
  1745. while (i < targetTouches.length) {
  1746. targetIds[targetTouches[i].identifier] = true;
  1747. i++;
  1748. }
  1749. }
  1750. // filter changed touches to only contain touches that exist in the collected target ids
  1751. i = 0;
  1752. while (i < changedTouches.length) {
  1753. if (targetIds[changedTouches[i].identifier]) {
  1754. changedTargetTouches.push(changedTouches[i]);
  1755. }
  1756. // cleanup removed touches
  1757. if (type & (INPUT_END | INPUT_CANCEL)) {
  1758. delete targetIds[changedTouches[i].identifier];
  1759. }
  1760. i++;
  1761. }
  1762. if (!changedTargetTouches.length) {
  1763. return;
  1764. }
  1765. return [ // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
  1766. uniqueArray(targetTouches.concat(changedTargetTouches), "identifier", true), changedTargetTouches ];
  1767. }
  1768. /**
  1769. * Combined touch and mouse input
  1770. *
  1771. * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
  1772. * This because touch devices also emit mouse events while doing a touch.
  1773. *
  1774. * @constructor
  1775. * @extends Input
  1776. */
  1777. function TouchMouseInput() {
  1778. Input.apply(this, arguments);
  1779. var handler = bindFn(this.handler, this);
  1780. this.touch = new TouchInput(this.manager, handler);
  1781. this.mouse = new MouseInput(this.manager, handler);
  1782. }
  1783. inherit(TouchMouseInput, Input, {
  1784. /**
  1785. * handle mouse and touch events
  1786. * @param {Hammer} manager
  1787. * @param {String} inputEvent
  1788. * @param {Object} inputData
  1789. */
  1790. handler: function TMEhandler(manager, inputEvent, inputData) {
  1791. var isTouch = inputData.pointerType == INPUT_TYPE_TOUCH, isMouse = inputData.pointerType == INPUT_TYPE_MOUSE;
  1792. // when we're in a touch event, so block all upcoming mouse events
  1793. // most mobile browser also emit mouseevents, right after touchstart
  1794. if (isTouch) {
  1795. this.mouse.allow = false;
  1796. } else if (isMouse && !this.mouse.allow) {
  1797. return;
  1798. }
  1799. // reset the allowMouse when we're done
  1800. if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
  1801. this.mouse.allow = true;
  1802. }
  1803. this.callback(manager, inputEvent, inputData);
  1804. },
  1805. /**
  1806. * remove the event listeners
  1807. */
  1808. destroy: function destroy() {
  1809. this.touch.destroy();
  1810. this.mouse.destroy();
  1811. }
  1812. });
  1813. var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, "touchAction");
  1814. var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
  1815. // magical touchAction value
  1816. var TOUCH_ACTION_COMPUTE = "compute";
  1817. var TOUCH_ACTION_AUTO = "auto";
  1818. var TOUCH_ACTION_MANIPULATION = "manipulation";
  1819. // not implemented
  1820. var TOUCH_ACTION_NONE = "none";
  1821. var TOUCH_ACTION_PAN_X = "pan-x";
  1822. var TOUCH_ACTION_PAN_Y = "pan-y";
  1823. /**
  1824. * Touch Action
  1825. * sets the touchAction property or uses the js alternative
  1826. * @param {Manager} manager
  1827. * @param {String} value
  1828. * @constructor
  1829. */
  1830. function TouchAction(manager, value) {
  1831. this.manager = manager;
  1832. this.set(value);
  1833. }
  1834. TouchAction.prototype = {
  1835. /**
  1836. * set the touchAction value on the element or enable the polyfill
  1837. * @param {String} value
  1838. */
  1839. set: function(value) {
  1840. // find out the touch-action by the event handlers
  1841. if (value == TOUCH_ACTION_COMPUTE) {
  1842. value = this.compute();
  1843. }
  1844. if (NATIVE_TOUCH_ACTION) {
  1845. this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
  1846. }
  1847. this.actions = value.toLowerCase().trim();
  1848. },
  1849. /**
  1850. * just re-set the touchAction value
  1851. */
  1852. update: function() {
  1853. this.set(this.manager.options.touchAction);
  1854. },
  1855. /**
  1856. * compute the value for the touchAction property based on the recognizer's settings
  1857. * @returns {String} value
  1858. */
  1859. compute: function() {
  1860. var actions = [];
  1861. each(this.manager.recognizers, function(recognizer) {
  1862. if (boolOrFn(recognizer.options.enable, [ recognizer ])) {
  1863. actions = actions.concat(recognizer.getTouchAction());
  1864. }
  1865. });
  1866. return cleanTouchActions(actions.join(" "));
  1867. },
  1868. /**
  1869. * this method is called on each input cycle and provides the preventing of the browser behavior
  1870. * @param {Object} input
  1871. */
  1872. preventDefaults: function(input) {
  1873. // not needed with native support for the touchAction property
  1874. if (NATIVE_TOUCH_ACTION) {
  1875. return;
  1876. }
  1877. var srcEvent = input.srcEvent;
  1878. var direction = input.offsetDirection;
  1879. // if the touch action did prevented once this session
  1880. if (this.manager.session.prevented) {
  1881. srcEvent.preventDefault();
  1882. return;
  1883. }
  1884. var actions = this.actions;
  1885. var hasNone = inStr(actions, TOUCH_ACTION_NONE);
  1886. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  1887. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  1888. if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) {
  1889. return this.preventSrc(srcEvent);
  1890. }
  1891. },
  1892. /**
  1893. * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
  1894. * @param {Object} srcEvent
  1895. */
  1896. preventSrc: function(srcEvent) {
  1897. this.manager.session.prevented = true;
  1898. srcEvent.preventDefault();
  1899. }
  1900. };
  1901. /**
  1902. * when the touchActions are collected they are not a valid value, so we need to clean things up. *
  1903. * @param {String} actions
  1904. * @returns {*}
  1905. */
  1906. function cleanTouchActions(actions) {
  1907. // none
  1908. if (inStr(actions, TOUCH_ACTION_NONE)) {
  1909. return TOUCH_ACTION_NONE;
  1910. }
  1911. var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
  1912. var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
  1913. // pan-x and pan-y can be combined
  1914. if (hasPanX && hasPanY) {
  1915. return TOUCH_ACTION_PAN_X + " " + TOUCH_ACTION_PAN_Y;
  1916. }
  1917. // pan-x OR pan-y
  1918. if (hasPanX || hasPanY) {
  1919. return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
  1920. }
  1921. // manipulation
  1922. if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
  1923. return TOUCH_ACTION_MANIPULATION;
  1924. }
  1925. return TOUCH_ACTION_AUTO;
  1926. }
  1927. /**
  1928. * Recognizer flow explained; *
  1929. * All recognizers have the initial state of POSSIBLE when a input session starts.
  1930. * The definition of a input session is from the first input until the last input, with all it's movement in it. *
  1931. * Example session for mouse-input: mousedown -> mousemove -> mouseup
  1932. *
  1933. * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
  1934. * which determines with state it should be.
  1935. *
  1936. * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
  1937. * POSSIBLE to give it another change on the next cycle.
  1938. *
  1939. * Possible
  1940. * |
  1941. * +-----+---------------+
  1942. * | |
  1943. * +-----+-----+ |
  1944. * | | |
  1945. * Failed Cancelled |
  1946. * +-------+------+
  1947. * | |
  1948. * Recognized Began
  1949. * |
  1950. * Changed
  1951. * |
  1952. * Ended/Recognized
  1953. */
  1954. var STATE_POSSIBLE = 1;
  1955. var STATE_BEGAN = 2;
  1956. var STATE_CHANGED = 4;
  1957. var STATE_ENDED = 8;
  1958. var STATE_RECOGNIZED = STATE_ENDED;
  1959. var STATE_CANCELLED = 16;
  1960. var STATE_FAILED = 32;
  1961. /**
  1962. * Recognizer
  1963. * Every recognizer needs to extend from this class.
  1964. * @constructor
  1965. * @param {Object} options
  1966. */
  1967. function Recognizer(options) {
  1968. this.id = uniqueId();
  1969. this.manager = null;
  1970. this.options = merge(options || {}, this.defaults);
  1971. // default is enable true
  1972. this.options.enable = ifUndefined(this.options.enable, true);
  1973. this.state = STATE_POSSIBLE;
  1974. this.simultaneous = {};
  1975. this.requireFail = [];
  1976. }
  1977. Recognizer.prototype = {
  1978. /**
  1979. * @virtual
  1980. * @type {Object}
  1981. */
  1982. defaults: {},
  1983. /**
  1984. * set options
  1985. * @param {Object} options
  1986. * @return {Recognizer}
  1987. */
  1988. set: function(options) {
  1989. extend(this.options, options);
  1990. // also update the touchAction, in case something changed about the directions/enabled state
  1991. this.manager && this.manager.touchAction.update();
  1992. return this;
  1993. },
  1994. /**
  1995. * recognize simultaneous with an other recognizer.
  1996. * @param {Recognizer} otherRecognizer
  1997. * @returns {Recognizer} this
  1998. */
  1999. recognizeWith: function(otherRecognizer) {
  2000. if (invokeArrayArg(otherRecognizer, "recognizeWith", this)) {
  2001. return this;
  2002. }
  2003. var simultaneous = this.simultaneous;
  2004. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2005. if (!simultaneous[otherRecognizer.id]) {
  2006. simultaneous[otherRecognizer.id] = otherRecognizer;
  2007. otherRecognizer.recognizeWith(this);
  2008. }
  2009. return this;
  2010. },
  2011. /**
  2012. * drop the simultaneous link. it doesnt remove the link on the other recognizer.
  2013. * @param {Recognizer} otherRecognizer
  2014. * @returns {Recognizer} this
  2015. */
  2016. dropRecognizeWith: function(otherRecognizer) {
  2017. if (invokeArrayArg(otherRecognizer, "dropRecognizeWith", this)) {
  2018. return this;
  2019. }
  2020. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2021. delete this.simultaneous[otherRecognizer.id];
  2022. return this;
  2023. },
  2024. /**
  2025. * recognizer can only run when an other is failing
  2026. * @param {Recognizer} otherRecognizer
  2027. * @returns {Recognizer} this
  2028. */
  2029. requireFailure: function(otherRecognizer) {
  2030. if (invokeArrayArg(otherRecognizer, "requireFailure", this)) {
  2031. return this;
  2032. }
  2033. var requireFail = this.requireFail;
  2034. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2035. if (inArray(requireFail, otherRecognizer) === -1) {
  2036. requireFail.push(otherRecognizer);
  2037. otherRecognizer.requireFailure(this);
  2038. }
  2039. return this;
  2040. },
  2041. /**
  2042. * drop the requireFailure link. it does not remove the link on the other recognizer.
  2043. * @param {Recognizer} otherRecognizer
  2044. * @returns {Recognizer} this
  2045. */
  2046. dropRequireFailure: function(otherRecognizer) {
  2047. if (invokeArrayArg(otherRecognizer, "dropRequireFailure", this)) {
  2048. return this;
  2049. }
  2050. otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
  2051. var index = inArray(this.requireFail, otherRecognizer);
  2052. if (index > -1) {
  2053. this.requireFail.splice(index, 1);
  2054. }
  2055. return this;
  2056. },
  2057. /**
  2058. * has require failures boolean
  2059. * @returns {boolean}
  2060. */
  2061. hasRequireFailures: function() {
  2062. return this.requireFail.length > 0;
  2063. },
  2064. /**
  2065. * if the recognizer can recognize simultaneous with an other recognizer
  2066. * @param {Recognizer} otherRecognizer
  2067. * @returns {Boolean}
  2068. */
  2069. canRecognizeWith: function(otherRecognizer) {
  2070. return !!this.simultaneous[otherRecognizer.id];
  2071. },
  2072. /**
  2073. * You should use `tryEmit` instead of `emit` directly to check
  2074. * that all the needed recognizers has failed before emitting.
  2075. * @param {Object} input
  2076. */
  2077. emit: function(input) {
  2078. var self = this;
  2079. var state = this.state;
  2080. function emit(withState) {
  2081. self.manager.emit(self.options.event + (withState ? stateStr(state) : ""), input);
  2082. }
  2083. // 'panstart' and 'panmove'
  2084. if (state < STATE_ENDED) {
  2085. emit(true);
  2086. }
  2087. emit();
  2088. // simple 'eventName' events
  2089. // panend and pancancel
  2090. if (state >= STATE_ENDED) {
  2091. emit(true);
  2092. }
  2093. },
  2094. /**
  2095. * Check that all the require failure recognizers has failed,
  2096. * if true, it emits a gesture event,
  2097. * otherwise, setup the state to FAILED.
  2098. * @param {Object} input
  2099. */
  2100. tryEmit: function(input) {
  2101. if (this.canEmit()) {
  2102. return this.emit(input);
  2103. }
  2104. // it's failing anyway
  2105. this.state = STATE_FAILED;
  2106. },
  2107. /**
  2108. * can we emit?
  2109. * @returns {boolean}
  2110. */
  2111. canEmit: function() {
  2112. var i = 0;
  2113. while (i < this.requireFail.length) {
  2114. if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
  2115. return false;
  2116. }
  2117. i++;
  2118. }
  2119. return true;
  2120. },
  2121. /**
  2122. * update the recognizer
  2123. * @param {Object} inputData
  2124. */
  2125. recognize: function(inputData) {
  2126. // make a new copy of the inputData
  2127. // so we can change the inputData without messing up the other recognizers
  2128. var inputDataClone = extend({}, inputData);
  2129. // is is enabled and allow recognizing?
  2130. if (!boolOrFn(this.options.enable, [ this, inputDataClone ])) {
  2131. this.reset();
  2132. this.state = STATE_FAILED;
  2133. return;
  2134. }
  2135. // reset when we've reached the end
  2136. if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
  2137. this.state = STATE_POSSIBLE;
  2138. }
  2139. this.state = this.process(inputDataClone);
  2140. // the recognizer has recognized a gesture
  2141. // so trigger an event
  2142. if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
  2143. this.tryEmit(inputDataClone);
  2144. }
  2145. },
  2146. /**
  2147. * return the state of the recognizer
  2148. * the actual recognizing happens in this method
  2149. * @virtual
  2150. * @param {Object} inputData
  2151. * @returns {Const} STATE
  2152. */
  2153. process: function(inputData) {},
  2154. // jshint ignore:line
  2155. /**
  2156. * return the preferred touch-action
  2157. * @virtual
  2158. * @returns {Array}
  2159. */
  2160. getTouchAction: function() {},
  2161. /**
  2162. * called when the gesture isn't allowed to recognize
  2163. * like when another is being recognized or it is disabled
  2164. * @virtual
  2165. */
  2166. reset: function() {}
  2167. };
  2168. /**
  2169. * get a usable string, used as event postfix
  2170. * @param {Const} state
  2171. * @returns {String} state
  2172. */
  2173. function stateStr(state) {
  2174. if (state & STATE_CANCELLED) {
  2175. return "cancel";
  2176. } else if (state & STATE_ENDED) {
  2177. return "end";
  2178. } else if (state & STATE_CHANGED) {
  2179. return "move";
  2180. } else if (state & STATE_BEGAN) {
  2181. return "start";
  2182. }
  2183. return "";
  2184. }
  2185. /**
  2186. * direction cons to string
  2187. * @param {Const} direction
  2188. * @returns {String}
  2189. */
  2190. function directionStr(direction) {
  2191. if (direction == DIRECTION_DOWN) {
  2192. return "down";
  2193. } else if (direction == DIRECTION_UP) {
  2194. return "up";
  2195. } else if (direction == DIRECTION_LEFT) {
  2196. return "left";
  2197. } else if (direction == DIRECTION_RIGHT) {
  2198. return "right";
  2199. }
  2200. return "";
  2201. }
  2202. /**
  2203. * get a recognizer by name if it is bound to a manager
  2204. * @param {Recognizer|String} otherRecognizer
  2205. * @param {Recognizer} recognizer
  2206. * @returns {Recognizer}
  2207. */
  2208. function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
  2209. var manager = recognizer.manager;
  2210. if (manager) {
  2211. return manager.get(otherRecognizer);
  2212. }
  2213. return otherRecognizer;
  2214. }
  2215. /**
  2216. * This recognizer is just used as a base for the simple attribute recognizers.
  2217. * @constructor
  2218. * @extends Recognizer
  2219. */
  2220. function AttrRecognizer() {
  2221. Recognizer.apply(this, arguments);
  2222. }
  2223. inherit(AttrRecognizer, Recognizer, {
  2224. /**
  2225. * @namespace
  2226. * @memberof AttrRecognizer
  2227. */
  2228. defaults: {
  2229. /**
  2230. * @type {Number}
  2231. * @default 1
  2232. */
  2233. pointers: 1
  2234. },
  2235. /**
  2236. * Used to check if it the recognizer receives valid input, like input.distance > 10.
  2237. * @memberof AttrRecognizer
  2238. * @param {Object} input
  2239. * @returns {Boolean} recognized
  2240. */
  2241. attrTest: function(input) {
  2242. var optionPointers = this.options.pointers;
  2243. return optionPointers === 0 || input.pointers.length === optionPointers;
  2244. },
  2245. /**
  2246. * Process the input and return the state for the recognizer
  2247. * @memberof AttrRecognizer
  2248. * @param {Object} input
  2249. * @returns {*} State
  2250. */
  2251. process: function(input) {
  2252. var state = this.state;
  2253. var eventType = input.eventType;
  2254. var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
  2255. var isValid = this.attrTest(input);
  2256. // on cancel input and we've recognized before, return STATE_CANCELLED
  2257. if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
  2258. return state | STATE_CANCELLED;
  2259. } else if (isRecognized || isValid) {
  2260. if (eventType & INPUT_END) {
  2261. return state | STATE_ENDED;
  2262. } else if (!(state & STATE_BEGAN)) {
  2263. return STATE_BEGAN;
  2264. }
  2265. return state | STATE_CHANGED;
  2266. }
  2267. return STATE_FAILED;
  2268. }
  2269. });
  2270. /**
  2271. * Pan
  2272. * Recognized when the pointer is down and moved in the allowed direction.
  2273. * @constructor
  2274. * @extends AttrRecognizer
  2275. */
  2276. function PanRecognizer() {
  2277. AttrRecognizer.apply(this, arguments);
  2278. this.pX = null;
  2279. this.pY = null;
  2280. }
  2281. inherit(PanRecognizer, AttrRecognizer, {
  2282. /**
  2283. * @namespace
  2284. * @memberof PanRecognizer
  2285. */
  2286. defaults: {
  2287. event: "pan",
  2288. threshold: 10,
  2289. pointers: 1,
  2290. direction: DIRECTION_ALL
  2291. },
  2292. getTouchAction: function() {
  2293. var direction = this.options.direction;
  2294. var actions = [];
  2295. if (direction & DIRECTION_HORIZONTAL) {
  2296. actions.push(TOUCH_ACTION_PAN_Y);
  2297. }
  2298. if (direction & DIRECTION_VERTICAL) {
  2299. actions.push(TOUCH_ACTION_PAN_X);
  2300. }
  2301. return actions;
  2302. },
  2303. directionTest: function(input) {
  2304. var options = this.options;
  2305. var hasMoved = true;
  2306. var distance = input.distance;
  2307. var direction = input.direction;
  2308. var x = input.deltaX;
  2309. var y = input.deltaY;
  2310. // lock to axis?
  2311. if (!(direction & options.direction)) {
  2312. if (options.direction & DIRECTION_HORIZONTAL) {
  2313. direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  2314. hasMoved = x != this.pX;
  2315. distance = Math.abs(input.deltaX);
  2316. } else {
  2317. direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
  2318. hasMoved = y != this.pY;
  2319. distance = Math.abs(input.deltaY);
  2320. }
  2321. }
  2322. input.direction = direction;
  2323. return hasMoved && distance > options.threshold && direction & options.direction;
  2324. },
  2325. attrTest: function(input) {
  2326. return AttrRecognizer.prototype.attrTest.call(this, input) && (this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input));
  2327. },
  2328. emit: function(input) {
  2329. this.pX = input.deltaX;
  2330. this.pY = input.deltaY;
  2331. var direction = directionStr(input.direction);
  2332. if (direction) {
  2333. this.manager.emit(this.options.event + direction, input);
  2334. }
  2335. this._super.emit.call(this, input);
  2336. }
  2337. });
  2338. /**
  2339. * Pinch
  2340. * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
  2341. * @constructor
  2342. * @extends AttrRecognizer
  2343. */
  2344. function PinchRecognizer() {
  2345. AttrRecognizer.apply(this, arguments);
  2346. }
  2347. inherit(PinchRecognizer, AttrRecognizer, {
  2348. /**
  2349. * @namespace
  2350. * @memberof PinchRecognizer
  2351. */
  2352. defaults: {
  2353. event: "pinch",
  2354. threshold: 0,
  2355. pointers: 2
  2356. },
  2357. getTouchAction: function() {
  2358. return [ TOUCH_ACTION_NONE ];
  2359. },
  2360. attrTest: function(input) {
  2361. return this._super.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
  2362. },
  2363. emit: function(input) {
  2364. this._super.emit.call(this, input);
  2365. if (input.scale !== 1) {
  2366. var inOut = input.scale < 1 ? "in" : "out";
  2367. this.manager.emit(this.options.event + inOut, input);
  2368. }
  2369. }
  2370. });
  2371. /**
  2372. * Press
  2373. * Recognized when the pointer is down for x ms without any movement.
  2374. * @constructor
  2375. * @extends Recognizer
  2376. */
  2377. function PressRecognizer() {
  2378. Recognizer.apply(this, arguments);
  2379. this._timer = null;
  2380. this._input = null;
  2381. }
  2382. inherit(PressRecognizer, Recognizer, {
  2383. /**
  2384. * @namespace
  2385. * @memberof PressRecognizer
  2386. */
  2387. defaults: {
  2388. event: "press",
  2389. pointers: 1,
  2390. time: 500,
  2391. // minimal time of the pointer to be pressed
  2392. threshold: 5
  2393. },
  2394. getTouchAction: function() {
  2395. return [ TOUCH_ACTION_AUTO ];
  2396. },
  2397. process: function(input) {
  2398. var options = this.options;
  2399. var validPointers = input.pointers.length === options.pointers;
  2400. var validMovement = input.distance < options.threshold;
  2401. var validTime = input.deltaTime > options.time;
  2402. this._input = input;
  2403. // we only allow little movement
  2404. // and we've reached an end event, so a tap is possible
  2405. if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) {
  2406. this.reset();
  2407. } else if (input.eventType & INPUT_START) {
  2408. this.reset();
  2409. this._timer = setTimeoutContext(function() {
  2410. this.state = STATE_RECOGNIZED;
  2411. this.tryEmit();
  2412. }, options.time, this);
  2413. } else if (input.eventType & INPUT_END) {
  2414. return STATE_RECOGNIZED;
  2415. }
  2416. return STATE_FAILED;
  2417. },
  2418. reset: function() {
  2419. clearTimeout(this._timer);
  2420. },
  2421. emit: function(input) {
  2422. if (this.state !== STATE_RECOGNIZED) {
  2423. return;
  2424. }
  2425. if (input && input.eventType & INPUT_END) {
  2426. this.manager.emit(this.options.event + "up", input);
  2427. } else {
  2428. this._input.timeStamp = now();
  2429. this.manager.emit(this.options.event, this._input);
  2430. }
  2431. }
  2432. });
  2433. /**
  2434. * Rotate
  2435. * Recognized when two or more pointer are moving in a circular motion.
  2436. * @constructor
  2437. * @extends AttrRecognizer
  2438. */
  2439. function RotateRecognizer() {
  2440. AttrRecognizer.apply(this, arguments);
  2441. }
  2442. inherit(RotateRecognizer, AttrRecognizer, {
  2443. /**
  2444. * @namespace
  2445. * @memberof RotateRecognizer
  2446. */
  2447. defaults: {
  2448. event: "rotate",
  2449. threshold: 0,
  2450. pointers: 2
  2451. },
  2452. getTouchAction: function() {
  2453. return [ TOUCH_ACTION_NONE ];
  2454. },
  2455. attrTest: function(input) {
  2456. return this._super.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
  2457. }
  2458. });
  2459. /**
  2460. * Swipe
  2461. * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
  2462. * @constructor
  2463. * @extends AttrRecognizer
  2464. */
  2465. function SwipeRecognizer() {
  2466. AttrRecognizer.apply(this, arguments);
  2467. }
  2468. inherit(SwipeRecognizer, AttrRecognizer, {
  2469. /**
  2470. * @namespace
  2471. * @memberof SwipeRecognizer
  2472. */
  2473. defaults: {
  2474. event: "swipe",
  2475. threshold: 10,
  2476. velocity: .65,
  2477. direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
  2478. pointers: 1
  2479. },
  2480. getTouchAction: function() {
  2481. return PanRecognizer.prototype.getTouchAction.call(this);
  2482. },
  2483. attrTest: function(input) {
  2484. var direction = this.options.direction;
  2485. var velocity;
  2486. if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
  2487. velocity = input.velocity;
  2488. } else if (direction & DIRECTION_HORIZONTAL) {
  2489. velocity = input.velocityX;
  2490. } else if (direction & DIRECTION_VERTICAL) {
  2491. velocity = input.velocityY;
  2492. }
  2493. return this._super.attrTest.call(this, input) && direction & input.direction && input.distance > this.options.threshold && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
  2494. },
  2495. emit: function(input) {
  2496. var direction = directionStr(input.direction);
  2497. if (direction) {
  2498. this.manager.emit(this.options.event + direction, input);
  2499. }
  2500. this.manager.emit(this.options.event, input);
  2501. }
  2502. });
  2503. /**
  2504. * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
  2505. * between the given interval and position. The delay option can be used to recognize multi-taps without firing
  2506. * a single tap.
  2507. *
  2508. * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
  2509. * multi-taps being recognized.
  2510. * @constructor
  2511. * @extends Recognizer
  2512. */
  2513. function TapRecognizer() {
  2514. Recognizer.apply(this, arguments);
  2515. // previous time and center,
  2516. // used for tap counting
  2517. this.pTime = false;
  2518. this.pCenter = false;
  2519. this._timer = null;
  2520. this._input = null;
  2521. this.count = 0;
  2522. }
  2523. inherit(TapRecognizer, Recognizer, {
  2524. /**
  2525. * @namespace
  2526. * @memberof PinchRecognizer
  2527. */
  2528. defaults: {
  2529. event: "tap",
  2530. pointers: 1,
  2531. taps: 1,
  2532. interval: 300,
  2533. // max time between the multi-tap taps
  2534. time: 250,
  2535. // max time of the pointer to be down (like finger on the screen)
  2536. threshold: 2,
  2537. // a minimal movement is ok, but keep it low
  2538. posThreshold: 10
  2539. },
  2540. getTouchAction: function() {
  2541. return [ TOUCH_ACTION_MANIPULATION ];
  2542. },
  2543. process: function(input) {
  2544. var options = this.options;
  2545. var validPointers = input.pointers.length === options.pointers;
  2546. var validMovement = input.distance < options.threshold;
  2547. var validTouchTime = input.deltaTime < options.time;
  2548. this.reset();
  2549. if (input.eventType & INPUT_START && this.count === 0) {
  2550. return this.failTimeout();
  2551. }
  2552. // we only allow little movement
  2553. // and we've reached an end event, so a tap is possible
  2554. if (validMovement && validTouchTime && validPointers) {
  2555. if (input.eventType != INPUT_END) {
  2556. return this.failTimeout();
  2557. }
  2558. var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true;
  2559. var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
  2560. this.pTime = input.timeStamp;
  2561. this.pCenter = input.center;
  2562. if (!validMultiTap || !validInterval) {
  2563. this.count = 1;
  2564. } else {
  2565. this.count += 1;
  2566. }
  2567. this._input = input;
  2568. // if tap count matches we have recognized it,
  2569. // else it has began recognizing...
  2570. var tapCount = this.count % options.taps;
  2571. if (tapCount === 0) {
  2572. // no failing requirements, immediately trigger the tap event
  2573. // or wait as long as the multitap interval to trigger
  2574. if (!this.hasRequireFailures()) {
  2575. return STATE_RECOGNIZED;
  2576. } else {
  2577. this._timer = setTimeoutContext(function() {
  2578. this.state = STATE_RECOGNIZED;
  2579. this.tryEmit();
  2580. }, options.interval, this);
  2581. return STATE_BEGAN;
  2582. }
  2583. }
  2584. }
  2585. return STATE_FAILED;
  2586. },
  2587. failTimeout: function() {
  2588. this._timer = setTimeoutContext(function() {
  2589. this.state = STATE_FAILED;
  2590. }, this.options.interval, this);
  2591. return STATE_FAILED;
  2592. },
  2593. reset: function() {
  2594. clearTimeout(this._timer);
  2595. },
  2596. emit: function() {
  2597. if (this.state == STATE_RECOGNIZED) {
  2598. this._input.tapCount = this.count;
  2599. this.manager.emit(this.options.event, this._input);
  2600. }
  2601. }
  2602. });
  2603. /**
  2604. * Simple way to create an manager with a default set of recognizers.
  2605. * @param {HTMLElement} element
  2606. * @param {Object} [options]
  2607. * @constructor
  2608. */
  2609. function Hammer(element, options) {
  2610. options = options || {};
  2611. options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
  2612. return new Manager(element, options);
  2613. }
  2614. /**
  2615. * @const {string}
  2616. */
  2617. Hammer.VERSION = "2.0.3";
  2618. /**
  2619. * default settings
  2620. * @namespace
  2621. */
  2622. Hammer.defaults = {
  2623. /**
  2624. * set if DOM events are being triggered.
  2625. * But this is slower and unused by simple implementations, so disabled by default.
  2626. * @type {Boolean}
  2627. * @default false
  2628. */
  2629. domEvents: false,
  2630. /**
  2631. * The value for the touchAction property/fallback.
  2632. * When set to `compute` it will magically set the correct value based on the added recognizers.
  2633. * @type {String}
  2634. * @default compute
  2635. */
  2636. touchAction: TOUCH_ACTION_COMPUTE,
  2637. /**
  2638. * @type {Boolean}
  2639. * @default true
  2640. */
  2641. enable: true,
  2642. /**
  2643. * EXPERIMENTAL FEATURE -- can be removed/changed
  2644. * Change the parent input target element.
  2645. * If Null, then it is being set the to main element.
  2646. * @type {Null|EventTarget}
  2647. * @default null
  2648. */
  2649. inputTarget: null,
  2650. /**
  2651. * force an input class
  2652. * @type {Null|Function}
  2653. * @default null
  2654. */
  2655. inputClass: null,
  2656. /**
  2657. * Default recognizer setup when calling `Hammer()`
  2658. * When creating a new Manager these will be skipped.
  2659. * @type {Array}
  2660. */
  2661. preset: [ // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
  2662. [ RotateRecognizer, {
  2663. enable: false
  2664. } ], [ PinchRecognizer, {
  2665. enable: false
  2666. }, [ "rotate" ] ], [ SwipeRecognizer, {
  2667. direction: DIRECTION_HORIZONTAL
  2668. } ], [ PanRecognizer, {
  2669. direction: DIRECTION_HORIZONTAL
  2670. }, [ "swipe" ] ], [ TapRecognizer ], [ TapRecognizer, {
  2671. event: "doubletap",
  2672. taps: 2
  2673. }, [ "tap" ] ], [ PressRecognizer ] ],
  2674. /**
  2675. * Some CSS properties can be used to improve the working of Hammer.
  2676. * Add them to this method and they will be set when creating a new Manager.
  2677. * @namespace
  2678. */
  2679. cssProps: {
  2680. /**
  2681. * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
  2682. * @type {String}
  2683. * @default 'none'
  2684. */
  2685. userSelect: "none",
  2686. /**
  2687. * Disable the Windows Phone grippers when pressing an element.
  2688. * @type {String}
  2689. * @default 'none'
  2690. */
  2691. touchSelect: "none",
  2692. /**
  2693. * Disables the default callout shown when you touch and hold a touch target.
  2694. * On iOS, when you touch and hold a touch target such as a link, Safari displays
  2695. * a callout containing information about the link. This property allows you to disable that callout.
  2696. * @type {String}
  2697. * @default 'none'
  2698. */
  2699. touchCallout: "none",
  2700. /**
  2701. * Specifies whether zooming is enabled. Used by IE10>
  2702. * @type {String}
  2703. * @default 'none'
  2704. */
  2705. contentZooming: "none",
  2706. /**
  2707. * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
  2708. * @type {String}
  2709. * @default 'none'
  2710. */
  2711. userDrag: "none",
  2712. /**
  2713. * Overrides the highlight color shown when the user taps a link or a JavaScript
  2714. * clickable element in iOS. This property obeys the alpha value, if specified.
  2715. * @type {String}
  2716. * @default 'rgba(0,0,0,0)'
  2717. */
  2718. tapHighlightColor: "rgba(0,0,0,0)"
  2719. }
  2720. };
  2721. var STOP = 1;
  2722. var FORCED_STOP = 2;
  2723. /**
  2724. * Manager
  2725. * @param {HTMLElement} element
  2726. * @param {Object} [options]
  2727. * @constructor
  2728. */
  2729. function Manager(element, options) {
  2730. options = options || {};
  2731. this.options = merge(options, Hammer.defaults);
  2732. this.options.inputTarget = this.options.inputTarget || element;
  2733. this.handlers = {};
  2734. this.session = {};
  2735. this.recognizers = [];
  2736. this.element = element;
  2737. this.input = createInputInstance(this);
  2738. this.touchAction = new TouchAction(this, this.options.touchAction);
  2739. toggleCssProps(this, true);
  2740. each(options.recognizers, function(item) {
  2741. var recognizer = this.add(new item[0](item[1]));
  2742. item[2] && recognizer.recognizeWith(item[2]);
  2743. item[3] && recognizer.requireFailure(item[3]);
  2744. }, this);
  2745. }
  2746. Manager.prototype = {
  2747. /**
  2748. * set options
  2749. * @param {Object} options
  2750. * @returns {Manager}
  2751. */
  2752. set: function(options) {
  2753. extend(this.options, options);
  2754. // Options that need a little more setup
  2755. if (options.touchAction) {
  2756. this.touchAction.update();
  2757. }
  2758. if (options.inputTarget) {
  2759. // Clean up existing event listeners and reinitialize
  2760. this.input.destroy();
  2761. this.input.target = options.inputTarget;
  2762. this.input.init();
  2763. }
  2764. return this;
  2765. },
  2766. /**
  2767. * stop recognizing for this session.
  2768. * This session will be discarded, when a new [input]start event is fired.
  2769. * When forced, the recognizer cycle is stopped immediately.
  2770. * @param {Boolean} [force]
  2771. */
  2772. stop: function(force) {
  2773. this.session.stopped = force ? FORCED_STOP : STOP;
  2774. },
  2775. /**
  2776. * run the recognizers!
  2777. * called by the inputHandler function on every movement of the pointers (touches)
  2778. * it walks through all the recognizers and tries to detect the gesture that is being made
  2779. * @param {Object} inputData
  2780. */
  2781. recognize: function(inputData) {
  2782. var session = this.session;
  2783. if (session.stopped) {
  2784. return;
  2785. }
  2786. // run the touch-action polyfill
  2787. this.touchAction.preventDefaults(inputData);
  2788. var recognizer;
  2789. var recognizers = this.recognizers;
  2790. // this holds the recognizer that is being recognized.
  2791. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
  2792. // if no recognizer is detecting a thing, it is set to `null`
  2793. var curRecognizer = session.curRecognizer;
  2794. // reset when the last recognizer is recognized
  2795. // or when we're in a new session
  2796. if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) {
  2797. curRecognizer = session.curRecognizer = null;
  2798. }
  2799. var i = 0;
  2800. while (i < recognizers.length) {
  2801. recognizer = recognizers[i];
  2802. // find out if we are allowed try to recognize the input for this one.
  2803. // 1. allow if the session is NOT forced stopped (see the .stop() method)
  2804. // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
  2805. // that is being recognized.
  2806. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
  2807. // this can be setup with the `recognizeWith()` method on the recognizer.
  2808. if (session.stopped !== FORCED_STOP && (// 1
  2809. !curRecognizer || recognizer == curRecognizer || // 2
  2810. recognizer.canRecognizeWith(curRecognizer))) {
  2811. // 3
  2812. recognizer.recognize(inputData);
  2813. } else {
  2814. recognizer.reset();
  2815. }
  2816. // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
  2817. // current active recognizer. but only if we don't already have an active recognizer
  2818. if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
  2819. curRecognizer = session.curRecognizer = recognizer;
  2820. }
  2821. i++;
  2822. }
  2823. },
  2824. /**
  2825. * get a recognizer by its event name.
  2826. * @param {Recognizer|String} recognizer
  2827. * @returns {Recognizer|Null}
  2828. */
  2829. get: function(recognizer) {
  2830. if (recognizer instanceof Recognizer) {
  2831. return recognizer;
  2832. }
  2833. var recognizers = this.recognizers;
  2834. for (var i = 0; i < recognizers.length; i++) {
  2835. if (recognizers[i].options.event == recognizer) {
  2836. return recognizers[i];
  2837. }
  2838. }
  2839. return null;
  2840. },
  2841. /**
  2842. * add a recognizer to the manager
  2843. * existing recognizers with the same event name will be removed
  2844. * @param {Recognizer} recognizer
  2845. * @returns {Recognizer|Manager}
  2846. */
  2847. add: function(recognizer) {
  2848. if (invokeArrayArg(recognizer, "add", this)) {
  2849. return this;
  2850. }
  2851. // remove existing
  2852. var existing = this.get(recognizer.options.event);
  2853. if (existing) {
  2854. this.remove(existing);
  2855. }
  2856. this.recognizers.push(recognizer);
  2857. recognizer.manager = this;
  2858. this.touchAction.update();
  2859. return recognizer;
  2860. },
  2861. /**
  2862. * remove a recognizer by name or instance
  2863. * @param {Recognizer|String} recognizer
  2864. * @returns {Manager}
  2865. */
  2866. remove: function(recognizer) {
  2867. if (invokeArrayArg(recognizer, "remove", this)) {
  2868. return this;
  2869. }
  2870. var recognizers = this.recognizers;
  2871. recognizer = this.get(recognizer);
  2872. recognizers.splice(inArray(recognizers, recognizer), 1);
  2873. this.touchAction.update();
  2874. return this;
  2875. },
  2876. /**
  2877. * bind event
  2878. * @param {String} events
  2879. * @param {Function} handler
  2880. * @returns {EventEmitter} this
  2881. */
  2882. on: function(events, handler) {
  2883. var handlers = this.handlers;
  2884. each(splitStr(events), function(event) {
  2885. handlers[event] = handlers[event] || [];
  2886. handlers[event].push(handler);
  2887. });
  2888. return this;
  2889. },
  2890. /**
  2891. * unbind event, leave emit blank to remove all handlers
  2892. * @param {String} events
  2893. * @param {Function} [handler]
  2894. * @returns {EventEmitter} this
  2895. */
  2896. off: function(events, handler) {
  2897. var handlers = this.handlers;
  2898. each(splitStr(events), function(event) {
  2899. if (!handler) {
  2900. delete handlers[event];
  2901. } else {
  2902. handlers[event].splice(inArray(handlers[event], handler), 1);
  2903. }
  2904. });
  2905. return this;
  2906. },
  2907. /**
  2908. * emit event to the listeners
  2909. * @param {String} event
  2910. * @param {Object} data
  2911. */
  2912. emit: function(event, data) {
  2913. // we also want to trigger dom events
  2914. if (this.options.domEvents) {
  2915. triggerDomEvent(event, data);
  2916. }
  2917. // no handlers, so skip it all
  2918. var handlers = this.handlers[event] && this.handlers[event].slice();
  2919. if (!handlers || !handlers.length) {
  2920. return;
  2921. }
  2922. data.type = event;
  2923. data.preventDefault = function() {
  2924. data.srcEvent.preventDefault();
  2925. };
  2926. var i = 0;
  2927. while (i < handlers.length) {
  2928. handlers[i](data);
  2929. i++;
  2930. }
  2931. },
  2932. /**
  2933. * destroy the manager and unbinds all events
  2934. * it doesn't unbind dom events, that is the user own responsibility
  2935. */
  2936. destroy: function() {
  2937. this.element && toggleCssProps(this, false);
  2938. this.handlers = {};
  2939. this.session = {};
  2940. this.input.destroy();
  2941. this.element = null;
  2942. }
  2943. };
  2944. /**
  2945. * add/remove the css properties as defined in manager.options.cssProps
  2946. * @param {Manager} manager
  2947. * @param {Boolean} add
  2948. */
  2949. function toggleCssProps(manager, add) {
  2950. var element = manager.element;
  2951. each(manager.options.cssProps, function(value, name) {
  2952. element.style[prefixed(element.style, name)] = add ? value : "";
  2953. });
  2954. }
  2955. /**
  2956. * trigger dom event
  2957. * @param {String} event
  2958. * @param {Object} data
  2959. */
  2960. function triggerDomEvent(event, data) {
  2961. var gestureEvent = document.createEvent("Event");
  2962. gestureEvent.initEvent(event, true, true);
  2963. gestureEvent.gesture = data;
  2964. data.target.dispatchEvent(gestureEvent);
  2965. }
  2966. extend(Hammer, {
  2967. INPUT_START: INPUT_START,
  2968. INPUT_MOVE: INPUT_MOVE,
  2969. INPUT_END: INPUT_END,
  2970. INPUT_CANCEL: INPUT_CANCEL,
  2971. STATE_POSSIBLE: STATE_POSSIBLE,
  2972. STATE_BEGAN: STATE_BEGAN,
  2973. STATE_CHANGED: STATE_CHANGED,
  2974. STATE_ENDED: STATE_ENDED,
  2975. STATE_RECOGNIZED: STATE_RECOGNIZED,
  2976. STATE_CANCELLED: STATE_CANCELLED,
  2977. STATE_FAILED: STATE_FAILED,
  2978. DIRECTION_NONE: DIRECTION_NONE,
  2979. DIRECTION_LEFT: DIRECTION_LEFT,
  2980. DIRECTION_RIGHT: DIRECTION_RIGHT,
  2981. DIRECTION_UP: DIRECTION_UP,
  2982. DIRECTION_DOWN: DIRECTION_DOWN,
  2983. DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
  2984. DIRECTION_VERTICAL: DIRECTION_VERTICAL,
  2985. DIRECTION_ALL: DIRECTION_ALL,
  2986. Manager: Manager,
  2987. Input: Input,
  2988. TouchAction: TouchAction,
  2989. TouchInput: TouchInput,
  2990. MouseInput: MouseInput,
  2991. PointerEventInput: PointerEventInput,
  2992. TouchMouseInput: TouchMouseInput,
  2993. Recognizer: Recognizer,
  2994. AttrRecognizer: AttrRecognizer,
  2995. Tap: TapRecognizer,
  2996. Pan: PanRecognizer,
  2997. Swipe: SwipeRecognizer,
  2998. Pinch: PinchRecognizer,
  2999. Rotate: RotateRecognizer,
  3000. Press: PressRecognizer,
  3001. on: addEventListeners,
  3002. off: removeEventListeners,
  3003. each: each,
  3004. merge: merge,
  3005. extend: extend,
  3006. inherit: inherit,
  3007. bindFn: bindFn,
  3008. prefixed: prefixed
  3009. });
  3010. module.exports = Hammer;
  3011. function hammerify(el, options) {
  3012. var $el = $(el);
  3013. if (!$el.data("hammer")) {
  3014. $el.data("hammer", new Hammer($el[0], options));
  3015. }
  3016. }
  3017. // extend the emit method to also trigger jQuery events
  3018. Hammer.Manager.prototype.emit = function(originalEmit) {
  3019. return function(type, data) {
  3020. originalEmit.call(this, type, data);
  3021. // console.log('trigger....%s', type);
  3022. $(this.element).trigger({
  3023. type: type,
  3024. gesture: data
  3025. });
  3026. };
  3027. }(Hammer.Manager.prototype.emit);
  3028. $.fn.hammer = function(options) {
  3029. return this.each(function() {
  3030. hammerify(this, options);
  3031. });
  3032. };
  3033. });
  3034. define("zepto.outerdemension", [], function(require, exports, module) {
  3035. var $ = window.Zepto;
  3036. // Create outerHeight and outerWidth methods
  3037. [ "width", "height" ].forEach(function(dimension) {
  3038. var offset, Dimension = dimension.replace(/./, function(m) {
  3039. return m[0].toUpperCase();
  3040. });
  3041. $.fn["outer" + Dimension] = function(margin) {
  3042. var elem = this;
  3043. if (elem) {
  3044. var size = elem[dimension]();
  3045. var sides = {
  3046. width: [ "left", "right" ],
  3047. height: [ "top", "bottom" ]
  3048. };
  3049. sides[dimension].forEach(function(side) {
  3050. if (margin) size += parseInt(elem.css("margin-" + side), 10);
  3051. });
  3052. return size;
  3053. } else {
  3054. return null;
  3055. }
  3056. };
  3057. });
  3058. });
  3059. define("zepto.extend.data", [], function(require, exports, module) {
  3060. var $ = window.Zepto;
  3061. // Zepto.js
  3062. // (c) 2010-2014 Thomas Fuchs
  3063. // Zepto.js may be freely distributed under the MIT license.
  3064. // The following code is heavily inspired by jQuery's $.fn.data()
  3065. var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, exp = $.expando = "Zepto" + +new Date(), emptyArray = [];
  3066. // Get value from node:
  3067. // 1. first try key as given,
  3068. // 2. then try camelized key,
  3069. // 3. fall back to reading "data-*" attribute.
  3070. function getData(node, name) {
  3071. var id = node[exp], store = id && data[id];
  3072. if (name === undefined) return store || setData(node); else {
  3073. if (store) {
  3074. if (name in store) return store[name];
  3075. var camelName = camelize(name);
  3076. if (camelName in store) return store[camelName];
  3077. }
  3078. return dataAttr.call($(node), name);
  3079. }
  3080. }
  3081. // Store value under camelized key on node
  3082. function setData(node, name, value) {
  3083. var id = node[exp] || (node[exp] = ++$.uuid), store = data[id] || (data[id] = attributeData(node));
  3084. if (name !== undefined) store[camelize(name)] = value;
  3085. return store;
  3086. }
  3087. // Read all "data-*" attributes from a node
  3088. function attributeData(node) {
  3089. var store = {};
  3090. $.each(node.attributes || emptyArray, function(i, attr) {
  3091. if (attr.name.indexOf("data-") == 0) store[camelize(attr.name.replace("data-", ""))] = $.zepto.deserializeValue(attr.value);
  3092. });
  3093. return store;
  3094. }
  3095. $.fn.data = function(name, value) {
  3096. return value === undefined ? // set multiple values via object
  3097. $.isPlainObject(name) ? this.each(function(i, node) {
  3098. $.each(name, function(key, value) {
  3099. setData(node, key, value);
  3100. });
  3101. }) : // get value from first element
  3102. 0 in this ? getData(this[0], name) : undefined : // set value on all elements
  3103. this.each(function() {
  3104. setData(this, name, value);
  3105. });
  3106. };
  3107. $.fn.removeData = function(names) {
  3108. if (typeof names == "string") names = names.split(/\s+/);
  3109. return this.each(function() {
  3110. var id = this[exp], store = id && data[id];
  3111. if (store) $.each(names || store, function(key) {
  3112. delete store[names ? camelize(this) : key];
  3113. });
  3114. });
  3115. };
  3116. [ "remove", "empty" ].forEach(function(methodName) {
  3117. var origFn = $.fn[methodName];
  3118. $.fn[methodName] = function() {
  3119. var elements = this.find("*");
  3120. if (methodName === "remove") elements = elements.add(this);
  3121. elements.removeData();
  3122. return origFn.call(this);
  3123. };
  3124. });
  3125. });
  3126. define("zepto.extend.fx", [], function(require, exports, module) {
  3127. var $ = window.Zepto;
  3128. // Zepto.js
  3129. // (c) 2010-2014 Thomas Fuchs
  3130. // Zepto.js may be freely distributed under the MIT license.
  3131. var prefix = "", eventPrefix, endEventName, endAnimationName, vendors = {
  3132. Webkit: "webkit",
  3133. Moz: "",
  3134. O: "o"
  3135. }, document = window.document, testEl = document.createElement("div"), supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, transform, transitionProperty, transitionDuration, transitionTiming, transitionDelay, animationName, animationDuration, animationTiming, animationDelay, cssReset = {};
  3136. function dasherize(str) {
  3137. return str.replace(/([a-z])([A-Z])/, "$1-$2").toLowerCase();
  3138. }
  3139. function normalizeEvent(name) {
  3140. return eventPrefix ? eventPrefix + name : name.toLowerCase();
  3141. }
  3142. $.each(vendors, function(vendor, event) {
  3143. if (testEl.style[vendor + "TransitionProperty"] !== undefined) {
  3144. prefix = "-" + vendor.toLowerCase() + "-";
  3145. eventPrefix = event;
  3146. return false;
  3147. }
  3148. });
  3149. transform = prefix + "transform";
  3150. cssReset[transitionProperty = prefix + "transition-property"] = cssReset[transitionDuration = prefix + "transition-duration"] = cssReset[transitionDelay = prefix + "transition-delay"] = cssReset[transitionTiming = prefix + "transition-timing-function"] = cssReset[animationName = prefix + "animation-name"] = cssReset[animationDuration = prefix + "animation-duration"] = cssReset[animationDelay = prefix + "animation-delay"] = cssReset[animationTiming = prefix + "animation-timing-function"] = "";
  3151. $.fx = {
  3152. off: eventPrefix === undefined && testEl.style.transitionProperty === undefined,
  3153. speeds: {
  3154. _default: 400,
  3155. fast: 200,
  3156. slow: 600
  3157. },
  3158. cssPrefix: prefix,
  3159. transitionEnd: normalizeEvent("TransitionEnd"),
  3160. animationEnd: normalizeEvent("AnimationEnd")
  3161. };
  3162. $.fn.animate = function(properties, duration, ease, callback, delay) {
  3163. if ($.isFunction(duration)) callback = duration, ease = undefined, duration = undefined;
  3164. if ($.isFunction(ease)) callback = ease, ease = undefined;
  3165. if ($.isPlainObject(duration)) ease = duration.easing, callback = duration.complete,
  3166. delay = duration.delay, duration = duration.duration;
  3167. if (duration) duration = (typeof duration == "number" ? duration : $.fx.speeds[duration] || $.fx.speeds._default) / 1e3;
  3168. if (delay) delay = parseFloat(delay) / 1e3;
  3169. return this.anim(properties, duration, ease, callback, delay);
  3170. };
  3171. $.fn.anim = function(properties, duration, ease, callback, delay) {
  3172. var key, cssValues = {}, cssProperties, transforms = "", that = this, wrappedCallback, endEvent = $.fx.transitionEnd, fired = false;
  3173. if (duration === undefined) duration = $.fx.speeds._default / 1e3;
  3174. if (delay === undefined) delay = 0;
  3175. if ($.fx.off) duration = 0;
  3176. if (typeof properties == "string") {
  3177. // keyframe animation
  3178. cssValues[animationName] = properties;
  3179. cssValues[animationDuration] = duration + "s";
  3180. cssValues[animationDelay] = delay + "s";
  3181. cssValues[animationTiming] = ease || "linear";
  3182. endEvent = $.fx.animationEnd;
  3183. } else {
  3184. cssProperties = [];
  3185. // CSS transitions
  3186. for (key in properties) if (supportedTransforms.test(key)) transforms += key + "(" + properties[key] + ") "; else cssValues[key] = properties[key],
  3187. cssProperties.push(dasherize(key));
  3188. if (transforms) cssValues[transform] = transforms, cssProperties.push(transform);
  3189. if (duration > 0 && typeof properties === "object") {
  3190. cssValues[transitionProperty] = cssProperties.join(", ");
  3191. cssValues[transitionDuration] = duration + "s";
  3192. cssValues[transitionDelay] = delay + "s";
  3193. cssValues[transitionTiming] = ease || "linear";
  3194. }
  3195. }
  3196. wrappedCallback = function(event) {
  3197. if (typeof event !== "undefined") {
  3198. if (event.target !== event.currentTarget) return;
  3199. // makes sure the event didn't bubble from "below"
  3200. $(event.target).unbind(endEvent, wrappedCallback);
  3201. } else $(this).unbind(endEvent, wrappedCallback);
  3202. // triggered by setTimeout
  3203. fired = true;
  3204. $(this).css(cssReset);
  3205. callback && callback.call(this);
  3206. };
  3207. if (duration > 0) {
  3208. this.bind(endEvent, wrappedCallback);
  3209. // transitionEnd is not always firing on older Android phones
  3210. // so make sure it gets fired
  3211. setTimeout(function() {
  3212. if (fired) return;
  3213. wrappedCallback.call(that);
  3214. }, duration * 1e3 + 25);
  3215. }
  3216. // trigger page reflow so new elements can animate
  3217. this.size() && this.get(0).clientLeft;
  3218. this.css(cssValues);
  3219. if (duration <= 0) setTimeout(function() {
  3220. that.each(function() {
  3221. wrappedCallback.call(this);
  3222. });
  3223. }, 0);
  3224. return this;
  3225. };
  3226. testEl = null;
  3227. });
  3228. define("zepto.extend.selector", [], function(require, exports, module) {
  3229. var $ = window.Zepto;
  3230. // Zepto.js
  3231. // (c) 2010-2014 Thomas Fuchs
  3232. // Zepto.js may be freely distributed under the MIT license.
  3233. var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches;
  3234. function visible(elem) {
  3235. elem = $(elem);
  3236. return !!(elem.width() || elem.height()) && elem.css("display") !== "none";
  3237. }
  3238. // Implements a subset from:
  3239. // http://api.jquery.com/category/selectors/jquery-selector-extensions/
  3240. //
  3241. // Each filter function receives the current index, all nodes in the
  3242. // considered set, and a value if there were parentheses. The value
  3243. // of `this` is the node currently being considered. The function returns the
  3244. // resulting node(s), null, or undefined.
  3245. //
  3246. // Complex selectors are not supported:
  3247. // li:has(label:contains("foo")) + li:has(label:contains("bar"))
  3248. // ul.inner:first > li
  3249. var filters = $.expr[":"] = {
  3250. visible: function() {
  3251. if (visible(this)) return this;
  3252. },
  3253. hidden: function() {
  3254. if (!visible(this)) return this;
  3255. },
  3256. selected: function() {
  3257. if (this.selected) return this;
  3258. },
  3259. checked: function() {
  3260. if (this.checked) return this;
  3261. },
  3262. parent: function() {
  3263. return this.parentNode;
  3264. },
  3265. first: function(idx) {
  3266. if (idx === 0) return this;
  3267. },
  3268. last: function(idx, nodes) {
  3269. if (idx === nodes.length - 1) return this;
  3270. },
  3271. eq: function(idx, _, value) {
  3272. if (idx === value) return this;
  3273. },
  3274. contains: function(idx, _, text) {
  3275. if ($(this).text().indexOf(text) > -1) return this;
  3276. },
  3277. has: function(idx, _, sel) {
  3278. if (zepto.qsa(this, sel).length) return this;
  3279. }
  3280. };
  3281. var filterRe = new RegExp("(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*"), childRe = /^\s*>/, classTag = "Zepto" + +new Date();
  3282. function process(sel, fn) {
  3283. // quote the hash in `a[href^=#]` expression
  3284. sel = sel.replace(/=#\]/g, '="#"]');
  3285. var filter, arg, match = filterRe.exec(sel);
  3286. if (match && match[2] in filters) {
  3287. filter = filters[match[2]], arg = match[3];
  3288. sel = match[1];
  3289. if (arg) {
  3290. var num = Number(arg);
  3291. if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, ""); else arg = num;
  3292. }
  3293. }
  3294. return fn(sel, filter, arg);
  3295. }
  3296. zepto.qsa = function(node, selector) {
  3297. return process(selector, function(sel, filter, arg) {
  3298. try {
  3299. var taggedParent;
  3300. if (!sel && filter) sel = "*"; else if (childRe.test(sel)) // support "> *" child queries by tagging the parent node with a
  3301. // unique class and prepending that classname onto the selector
  3302. taggedParent = $(node).addClass(classTag), sel = "." + classTag + " " + sel;
  3303. var nodes = oldQsa(node, sel);
  3304. } catch (e) {
  3305. console.error("error performing selector: %o", selector);
  3306. throw e;
  3307. } finally {
  3308. if (taggedParent) taggedParent.removeClass(classTag);
  3309. }
  3310. return !filter ? nodes : zepto.uniq($.map(nodes, function(n, i) {
  3311. return filter.call(n, i, nodes, arg);
  3312. }));
  3313. });
  3314. };
  3315. zepto.matches = function(node, selector) {
  3316. return process(selector, function(sel, filter, arg) {
  3317. return (!sel || oldMatches(node, sel)) && (!filter || filter.call(node, null, arg) === node);
  3318. });
  3319. };
  3320. });
  3321. define("ui.add2home", [], function(require, exports, module) {
  3322. var $ = window.Zepto;
  3323. /**
  3324. * Add to Homescreen v3.0.7
  3325. * @copyright (c) 2014 Matteo Spinelli
  3326. * @license: http://cubiq.org/license
  3327. */
  3328. // Check if document is loaded, needed by autostart
  3329. var _DOMReady = false;
  3330. if (document.readyState === "complete") {
  3331. _DOMReady = true;
  3332. } else {
  3333. window.addEventListener("load", loaded, false);
  3334. }
  3335. function loaded() {
  3336. window.removeEventListener("load", loaded, false);
  3337. _DOMReady = true;
  3338. }
  3339. // regex used to detect if app has been added to the homescreen
  3340. var _reSmartURL = /\/ath(\/)?$/;
  3341. var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/;
  3342. // singleton
  3343. var _instance;
  3344. function ath(options) {
  3345. _instance = _instance || new ath.Class(options);
  3346. return _instance;
  3347. }
  3348. // message in all supported languages
  3349. ath.intl = {
  3350. en_us: {
  3351. message: "To add this web app to the home screen: tap %icon and then <strong>%action</strong>.",
  3352. action: {
  3353. ios: "Add to Home Screen",
  3354. android: "Add to homescreen",
  3355. windows: "pin to start"
  3356. }
  3357. },
  3358. zh_cn: {
  3359. message: "如要把应用程式加至主屏幕,请点击%icon, 然后<strong>%action</strong>",
  3360. action: {
  3361. ios: "加至主屏幕",
  3362. android: "加至主屏幕",
  3363. windows: "按住启动"
  3364. }
  3365. },
  3366. zh_tw: {
  3367. message: "如要把應用程式加至主屏幕, 請點擊%icon, 然後<strong>%action</strong>.",
  3368. action: {
  3369. ios: "加至主屏幕",
  3370. android: "加至主屏幕",
  3371. windows: "按住啟動"
  3372. }
  3373. }
  3374. };
  3375. // Add 2 characters language support (Android mostly)
  3376. for (var lang in ath.intl) {
  3377. ath.intl[lang.substr(0, 2)] = ath.intl[lang];
  3378. }
  3379. // default options
  3380. ath.defaults = {
  3381. appID: "org.cubiq.addtohome",
  3382. // local storage name (no need to change)
  3383. fontSize: 15,
  3384. // base font size, used to properly resize the popup based on viewport scale factor
  3385. debug: false,
  3386. // override browser checks
  3387. modal: false,
  3388. // prevent further actions until the message is closed
  3389. mandatory: false,
  3390. // you can't proceed if you don't add the app to the homescreen
  3391. autostart: true,
  3392. // show the message automatically
  3393. skipFirstVisit: false,
  3394. // show only to returning visitors (ie: skip the first time you visit)
  3395. startDelay: 1,
  3396. // display the message after that many seconds from page load
  3397. lifespan: 15,
  3398. // life of the message in seconds
  3399. displayPace: 1440,
  3400. // minutes before the message is shown again (0: display every time, default 24 hours)
  3401. maxDisplayCount: 0,
  3402. // absolute maximum number of times the message will be shown to the user (0: no limit)
  3403. icon: true,
  3404. // add touch icon to the message
  3405. message: "",
  3406. // the message can be customized
  3407. validLocation: [],
  3408. // list of pages where the message will be shown (array of regexes)
  3409. onInit: null,
  3410. // executed on instance creation
  3411. onShow: null,
  3412. // executed when the message is shown
  3413. onRemove: null,
  3414. // executed when the message is removed
  3415. onAdd: null,
  3416. // when the application is launched the first time from the homescreen (guesstimate)
  3417. onPrivate: null,
  3418. // executed if user is in private mode
  3419. detectHomescreen: false
  3420. };
  3421. // browser info and capability
  3422. var _ua = window.navigator.userAgent;
  3423. var _nav = window.navigator;
  3424. _extend(ath, {
  3425. hasToken: document.location.hash == "#ath" || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search),
  3426. isRetina: window.devicePixelRatio && window.devicePixelRatio > 1,
  3427. isIDevice: /iphone|ipod|ipad/i.test(_ua),
  3428. isMobileChrome: _ua.indexOf("Android") > -1 && /Chrome\/[.0-9]*/.test(_ua),
  3429. isMobileIE: _ua.indexOf("Windows Phone") > -1,
  3430. language: _nav.language && _nav.language.toLowerCase().replace("-", "_") || ""
  3431. });
  3432. // falls back to en_us if language is unsupported
  3433. ath.language = ath.language && ath.language in ath.intl ? ath.language : "en_us";
  3434. ath.isMobileSafari = ath.isIDevice && _ua.indexOf("Safari") > -1 && _ua.indexOf("CriOS") < 0;
  3435. ath.OS = ath.isIDevice ? "ios" : ath.isMobileChrome ? "android" : ath.isMobileIE ? "windows" : "unsupported";
  3436. ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/);
  3437. ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace("_", ".") : 0;
  3438. ath.isStandalone = window.navigator.standalone || ath.isMobileChrome && screen.height - document.documentElement.clientHeight < 40;
  3439. // TODO: check the lame polyfill
  3440. ath.isTablet = ath.isMobileSafari && _ua.indexOf("iPad") > -1 || ath.isMobileChrome && _ua.indexOf("Mobile") < 0;
  3441. ath.isCompatible = ath.isMobileSafari && ath.OSVersion >= 6 || ath.isMobileChrome;
  3442. // TODO: add winphone
  3443. var _defaultSession = {
  3444. lastDisplayTime: 0,
  3445. // last time we displayed the message
  3446. returningVisitor: false,
  3447. // is this the first time you visit
  3448. displayCount: 0,
  3449. // number of times the message has been shown
  3450. optedout: false,
  3451. // has the user opted out
  3452. added: false
  3453. };
  3454. ath.removeSession = function(appID) {
  3455. try {
  3456. localStorage.removeItem(appID || ath.defaults.appID);
  3457. } catch (e) {}
  3458. };
  3459. ath.Class = function(options) {
  3460. // merge default options with user config
  3461. this.options = _extend({}, ath.defaults);
  3462. _extend(this.options, options);
  3463. // normalize some options
  3464. this.options.mandatory = this.options.mandatory && ("standalone" in window.navigator || this.options.debug);
  3465. this.options.modal = this.options.modal || this.options.mandatory;
  3466. if (this.options.mandatory) {
  3467. this.options.startDelay = -.5;
  3468. }
  3469. this.options.detectHomescreen = this.options.detectHomescreen === true ? "hash" : this.options.detectHomescreen;
  3470. // setup the debug environment
  3471. if (this.options.debug) {
  3472. ath.isCompatible = true;
  3473. ath.OS = typeof this.options.debug == "string" ? this.options.debug : ath.OS == "unsupported" ? "android" : ath.OS;
  3474. ath.OSVersion = ath.OS == "ios" ? "7" : "4";
  3475. }
  3476. // the element the message will be appended to
  3477. this.container = document.documentElement;
  3478. // load session
  3479. this.session = JSON.parse(localStorage.getItem(this.options.appID));
  3480. // user most likely came from a direct link containing our token, we don't need it and we remove it
  3481. if (ath.hasToken && (!ath.isCompatible || !this.session)) {
  3482. ath.hasToken = false;
  3483. _removeToken();
  3484. }
  3485. // the device is not supported
  3486. if (!ath.isCompatible) {
  3487. return;
  3488. }
  3489. this.session = this.session || _defaultSession;
  3490. // check if we can use the local storage
  3491. try {
  3492. localStorage.setItem(this.options.appID, JSON.stringify(this.session));
  3493. ath.hasLocalStorage = true;
  3494. } catch (e) {
  3495. // we are most likely in private mode
  3496. ath.hasLocalStorage = false;
  3497. if (this.options.onPrivate) {
  3498. this.options.onPrivate.call(this);
  3499. }
  3500. }
  3501. // check if this is a valid location
  3502. var isValidLocation = !this.options.validLocation.length;
  3503. for (var i = this.options.validLocation.length; i--; ) {
  3504. if (this.options.validLocation[i].test(document.location.href)) {
  3505. isValidLocation = true;
  3506. break;
  3507. }
  3508. }
  3509. // check compatibility with old versions of add to homescreen. Opt-out if an old session is found
  3510. if (localStorage.getItem("addToHome")) {
  3511. this.optOut();
  3512. }
  3513. // critical errors:
  3514. // user opted out, already added to the homescreen, not a valid location
  3515. if (this.session.optedout || this.session.added || !isValidLocation) {
  3516. return;
  3517. }
  3518. // check if the app is in stand alone mode
  3519. if (ath.isStandalone) {
  3520. // execute the onAdd event if we haven't already
  3521. if (!this.session.added) {
  3522. this.session.added = true;
  3523. this.updateSession();
  3524. if (this.options.onAdd && ath.hasLocalStorage) {
  3525. // double check on localstorage to avoid multiple calls to the custom event
  3526. this.options.onAdd.call(this);
  3527. }
  3528. }
  3529. return;
  3530. }
  3531. // (try to) check if the page has been added to the homescreen
  3532. if (this.options.detectHomescreen) {
  3533. // the URL has the token, we are likely coming from the homescreen
  3534. if (ath.hasToken) {
  3535. _removeToken();
  3536. // we don't actually need the token anymore, we remove it to prevent redistribution
  3537. // this is called the first time the user opens the app from the homescreen
  3538. if (!this.session.added) {
  3539. this.session.added = true;
  3540. this.updateSession();
  3541. if (this.options.onAdd && ath.hasLocalStorage) {
  3542. // double check on localstorage to avoid multiple calls to the custom event
  3543. this.options.onAdd.call(this);
  3544. }
  3545. }
  3546. return;
  3547. }
  3548. // URL doesn't have the token, so add it
  3549. if (this.options.detectHomescreen == "hash") {
  3550. history.replaceState("", window.document.title, document.location.href + "#ath");
  3551. } else if (this.options.detectHomescreen == "smartURL") {
  3552. history.replaceState("", window.document.title, document.location.href.replace(/(\/)?$/, "/ath$1"));
  3553. } else {
  3554. history.replaceState("", window.document.title, document.location.href + (document.location.search ? "&" : "?") + "ath=");
  3555. }
  3556. }
  3557. // check if this is a returning visitor
  3558. if (!this.session.returningVisitor) {
  3559. this.session.returningVisitor = true;
  3560. this.updateSession();
  3561. // we do not show the message if this is your first visit
  3562. if (this.options.skipFirstVisit) {
  3563. return;
  3564. }
  3565. }
  3566. // we do no show the message in private mode
  3567. if (!ath.hasLocalStorage) {
  3568. return;
  3569. }
  3570. // all checks passed, ready to display
  3571. this.ready = true;
  3572. if (this.options.onInit) {
  3573. this.options.onInit.call(this);
  3574. }
  3575. if (this.options.autostart) {
  3576. this.show();
  3577. }
  3578. };
  3579. ath.Class.prototype = {
  3580. // event type to method conversion
  3581. events: {
  3582. load: "_delayedShow",
  3583. error: "_delayedShow",
  3584. orientationchange: "resize",
  3585. resize: "resize",
  3586. scroll: "resize",
  3587. click: "remove",
  3588. touchmove: "_preventDefault",
  3589. transitionend: "_removeElements",
  3590. webkitTransitionEnd: "_removeElements",
  3591. MSTransitionEnd: "_removeElements"
  3592. },
  3593. handleEvent: function(e) {
  3594. var type = this.events[e.type];
  3595. if (type) {
  3596. this[type](e);
  3597. }
  3598. },
  3599. show: function(force) {
  3600. // in autostart mode wait for the document to be ready
  3601. if (this.options.autostart && !_DOMReady) {
  3602. setTimeout(this.show.bind(this), 50);
  3603. return;
  3604. }
  3605. // message already on screen
  3606. if (this.shown) {
  3607. return;
  3608. }
  3609. var now = Date.now();
  3610. var lastDisplayTime = this.session.lastDisplayTime;
  3611. if (force !== true) {
  3612. // this is needed if autostart is disabled and you programmatically call the show() method
  3613. if (!this.ready) {
  3614. return;
  3615. }
  3616. // we obey the display pace (prevent the message to popup too often)
  3617. if (now - lastDisplayTime < this.options.displayPace * 6e4) {
  3618. return;
  3619. }
  3620. // obey the maximum number of display count
  3621. if (this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount) {
  3622. return;
  3623. }
  3624. }
  3625. this.shown = true;
  3626. // increment the display count
  3627. this.session.lastDisplayTime = now;
  3628. this.session.displayCount++;
  3629. this.updateSession();
  3630. // try to get the highest resolution application icon
  3631. if (!this.applicationIcon) {
  3632. if (ath.OS == "ios") {
  3633. this.applicationIcon = document.querySelector('head link[rel^=apple-touch-icon][sizes="152x152"],head link[rel^=apple-touch-icon][sizes="144x144"],head link[rel^=apple-touch-icon][sizes="120x120"],head link[rel^=apple-touch-icon][sizes="114x114"],head link[rel^=apple-touch-icon]');
  3634. } else {
  3635. this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]');
  3636. }
  3637. }
  3638. var message = "";
  3639. if (this.options.message in ath.intl) {
  3640. // you can force the locale
  3641. message = ath.intl[this.options.message].message.replace("%action", ath.intl[this.options.message].action[ath.OS]);
  3642. } else if (this.options.message !== "") {
  3643. // or use a custom message
  3644. message = this.options.message;
  3645. } else {
  3646. // otherwise we use our message
  3647. message = ath.intl[ath.language].message.replace("%action", ath.intl[ath.language].action[ath.OS]);
  3648. }
  3649. // add the action icon
  3650. message = "<p>" + message.replace("%icon", '<span class="ath-action-icon">icon</span>') + "</p>";
  3651. // create the message container
  3652. this.viewport = document.createElement("div");
  3653. this.viewport.className = "ath-viewport";
  3654. if (this.options.modal) {
  3655. this.viewport.className += " ath-modal";
  3656. }
  3657. if (this.options.mandatory) {
  3658. this.viewport.className += " ath-mandatory";
  3659. }
  3660. this.viewport.style.position = "absolute";
  3661. // create the actual message element
  3662. this.element = document.createElement("div");
  3663. this.element.className = "ath-container ath-" + ath.OS + " ath-" + ath.OS + (ath.OSVersion + "").substr(0, 1) + " ath-" + (ath.isTablet ? "tablet" : "phone");
  3664. this.element.style.cssText = "-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0;-webkit-transform:translate3d(0,0,0);transition-property:transform,opacity;transition-duration:0;transform:translate3d(0,0,0);-webkit-transition-timing-function:ease-out";
  3665. this.element.style.webkitTransform = "translate3d(0,-" + window.innerHeight + "px,0)";
  3666. this.element.style.webkitTransitionDuration = "0s";
  3667. // add the application icon
  3668. if (this.options.icon && this.applicationIcon) {
  3669. this.element.className += " ath-icon";
  3670. this.img = document.createElement("img");
  3671. this.img.className = "ath-application-icon";
  3672. this.img.addEventListener("load", this, false);
  3673. this.img.addEventListener("error", this, false);
  3674. this.img.src = this.applicationIcon.href;
  3675. this.element.appendChild(this.img);
  3676. }
  3677. this.element.innerHTML += message;
  3678. // we are not ready to show, place the message out of sight
  3679. this.viewport.style.left = "-99999em";
  3680. // attach all elements to the DOM
  3681. this.viewport.appendChild(this.element);
  3682. this.container.appendChild(this.viewport);
  3683. // if we don't have to wait for an image to load, show the message right away
  3684. if (!this.img) {
  3685. this._delayedShow();
  3686. }
  3687. },
  3688. _delayedShow: function(e) {
  3689. setTimeout(this._show.bind(this), this.options.startDelay * 1e3 + 500);
  3690. },
  3691. _show: function() {
  3692. var that = this;
  3693. // update the viewport size and orientation
  3694. this.updateViewport();
  3695. // reposition/resize the message on orientation change
  3696. window.addEventListener("resize", this, false);
  3697. window.addEventListener("scroll", this, false);
  3698. window.addEventListener("orientationchange", this, false);
  3699. if (this.options.modal) {
  3700. // lock any other interaction
  3701. document.addEventListener("touchmove", this, true);
  3702. }
  3703. // Enable closing after 1 second
  3704. if (!this.options.mandatory) {
  3705. setTimeout(function() {
  3706. that.element.addEventListener("click", that, true);
  3707. }, 1e3);
  3708. }
  3709. // kick the animation
  3710. setTimeout(function() {
  3711. that.element.style.webkitTransform = "translate3d(0,0,0)";
  3712. that.element.style.webkitTransitionDuration = "1.2s";
  3713. }, 0);
  3714. // set the destroy timer
  3715. if (this.options.lifespan) {
  3716. this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1e3);
  3717. }
  3718. // fire the custom onShow event
  3719. if (this.options.onShow) {
  3720. this.options.onShow.call(this);
  3721. }
  3722. },
  3723. remove: function() {
  3724. clearTimeout(this.removeTimer);
  3725. // clear up the event listeners
  3726. if (this.img) {
  3727. this.img.removeEventListener("load", this, false);
  3728. this.img.removeEventListener("error", this, false);
  3729. }
  3730. window.removeEventListener("resize", this, false);
  3731. window.removeEventListener("scroll", this, false);
  3732. window.removeEventListener("orientationchange", this, false);
  3733. document.removeEventListener("touchmove", this, true);
  3734. this.element.removeEventListener("click", this, true);
  3735. // remove the message element on transition end
  3736. this.element.addEventListener("transitionend", this, false);
  3737. this.element.addEventListener("webkitTransitionEnd", this, false);
  3738. this.element.addEventListener("MSTransitionEnd", this, false);
  3739. // start the fade out animation
  3740. this.element.style.webkitTransitionDuration = "0.3s";
  3741. this.element.style.opacity = "0";
  3742. },
  3743. _removeElements: function() {
  3744. this.element.removeEventListener("transitionend", this, false);
  3745. this.element.removeEventListener("webkitTransitionEnd", this, false);
  3746. this.element.removeEventListener("MSTransitionEnd", this, false);
  3747. // remove the message from the DOM
  3748. this.container.removeChild(this.viewport);
  3749. this.shown = false;
  3750. // fire the custom onRemove event
  3751. if (this.options.onRemove) {
  3752. this.options.onRemove.call(this);
  3753. }
  3754. },
  3755. updateViewport: function() {
  3756. if (!this.shown) {
  3757. return;
  3758. }
  3759. this.viewport.style.width = window.innerWidth + "px";
  3760. this.viewport.style.height = window.innerHeight + "px";
  3761. this.viewport.style.left = window.scrollX + "px";
  3762. this.viewport.style.top = window.scrollY + "px";
  3763. var clientWidth = document.documentElement.clientWidth;
  3764. this.orientation = clientWidth > document.documentElement.clientHeight ? "landscape" : "portrait";
  3765. var screenWidth = ath.OS == "ios" ? this.orientation == "portrait" ? screen.width : screen.height : screen.width;
  3766. this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth;
  3767. this.element.style.fontSize = this.options.fontSize / this.scale + "px";
  3768. },
  3769. resize: function() {
  3770. clearTimeout(this.resizeTimer);
  3771. this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100);
  3772. },
  3773. updateSession: function() {
  3774. if (ath.hasLocalStorage === false) {
  3775. return;
  3776. }
  3777. localStorage.setItem(this.options.appID, JSON.stringify(this.session));
  3778. },
  3779. clearSession: function() {
  3780. this.session = _defaultSession;
  3781. this.updateSession();
  3782. },
  3783. optOut: function() {
  3784. this.session.optedout = true;
  3785. this.updateSession();
  3786. },
  3787. optIn: function() {
  3788. this.session.optedout = false;
  3789. this.updateSession();
  3790. },
  3791. clearDisplayCount: function() {
  3792. this.session.displayCount = 0;
  3793. this.updateSession();
  3794. },
  3795. _preventDefault: function(e) {
  3796. e.preventDefault();
  3797. e.stopPropagation();
  3798. }
  3799. };
  3800. // utility
  3801. function _extend(target, obj) {
  3802. for (var i in obj) {
  3803. target[i] = obj[i];
  3804. }
  3805. return target;
  3806. }
  3807. function _removeToken() {
  3808. if (document.location.hash == "#ath") {
  3809. history.replaceState("", window.document.title, document.location.href.split("#")[0]);
  3810. }
  3811. if (_reSmartURL.test(document.location.href)) {
  3812. history.replaceState("", window.document.title, document.location.href.replace(_reSmartURL, "$1"));
  3813. }
  3814. if (_reQueryString.test(document.location.search)) {
  3815. history.replaceState("", window.document.title, document.location.href.replace(_reQueryString, "$2"));
  3816. }
  3817. }
  3818. $.AMUI.addToHomescreen = ath;
  3819. module.exports = ath;
  3820. });
  3821. define("ui.alert", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  3822. "use strict";
  3823. require("core");
  3824. var $ = window.Zepto, UI = $.AMUI;
  3825. /**
  3826. * @via https://github.com/Minwe/bootstrap/blob/master/js/alert.js
  3827. * @copyright Copyright 2013 Twitter, Inc.
  3828. * @license Apache 2.0
  3829. */
  3830. // Alert Class
  3831. // NOTE: removeElement option is unavailable now
  3832. var Alert = function(element, options) {
  3833. this.options = $.extend({}, Alert.DEFAULTS, options);
  3834. this.$element = $(element);
  3835. this.$element.addClass("am-fade am-in").on("click.alert.amui", ".am-close", $.proxy(this.close, this));
  3836. };
  3837. Alert.DEFAULTS = {
  3838. removeElement: true
  3839. };
  3840. Alert.prototype.close = function() {
  3841. var $this = $(this), $target = $this.hasClass("am-alert") ? $this : $this.parent(".am-alert");
  3842. $target.trigger("close:alert:amui");
  3843. $target.removeClass("am-in");
  3844. function processAlert() {
  3845. $target.off().trigger("closed:alert:amui").remove();
  3846. }
  3847. UI.support.transition && $target.hasClass("am-fade") ? $target.one(UI.support.transition.end, processAlert).emulateTransitionEnd(200) : processAlert();
  3848. };
  3849. UI.alert = Alert;
  3850. // Alert Plugin
  3851. $.fn.alert = function(option) {
  3852. return this.each(function() {
  3853. var $this = $(this), data = $this.data("amui.alert"), options = typeof option == "object" && option;
  3854. if (!data) {
  3855. $this.data("amui.alert", data = new Alert(this, options || {}));
  3856. }
  3857. if (typeof option == "string") {
  3858. data[option].call($this);
  3859. }
  3860. });
  3861. };
  3862. // Init code
  3863. $(document).on("click.alert.amui", "[data-am-alert]", function(e) {
  3864. var $target = $(e.target);
  3865. $(this).addClass("am-fade am-in");
  3866. $target.is(".am-close") && $(this).alert("close");
  3867. });
  3868. module.exports = Alert;
  3869. });
  3870. define("ui.button", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  3871. "use strict";
  3872. require("core");
  3873. var $ = window.Zepto, UI = $.AMUI;
  3874. /**
  3875. * @via https://github.com/twbs/bootstrap/blob/master/js/button.js
  3876. * @copyright (c) 2011-2014 Twitter, Inc
  3877. * @license The MIT License
  3878. */
  3879. var Button = function(element, options) {
  3880. this.$element = $(element);
  3881. this.options = $.extend({}, Button.DEFAULTS, options);
  3882. this.isLoading = false;
  3883. this.hasSpinner = false;
  3884. };
  3885. Button.DEFAULTS = {
  3886. loadingText: "loading...",
  3887. className: {
  3888. loading: "am-btn-loading",
  3889. disabled: "am-disabled"
  3890. },
  3891. spinner: undefined
  3892. };
  3893. Button.prototype.setState = function(state) {
  3894. var disabled = "disabled", $element = this.$element, options = this.options, val = $element.is("input") ? "val" : "html", loadingClassName = options.className.disabled + " " + options.className.loading;
  3895. state = state + "Text";
  3896. if (!options.resetText) {
  3897. options.resetText = $element[val]();
  3898. }
  3899. // add spinner for element with html()
  3900. if (UI.support.animation && options.spinner && val === "html" && !this.hasSpinner) {
  3901. options.loadingText = '<span class="am-icon-' + options.spinner + ' am-icon-spin"></span>' + options.loadingText;
  3902. this.hasSpinner = true;
  3903. }
  3904. $element[val](options[state]);
  3905. // push to event loop to allow forms to submit
  3906. setTimeout($.proxy(function() {
  3907. if (state == "loadingText") {
  3908. $element.addClass(loadingClassName).attr(disabled, disabled);
  3909. this.isLoading = true;
  3910. } else if (this.isLoading) {
  3911. $element.removeClass(loadingClassName).removeAttr(disabled);
  3912. this.isLoading = false;
  3913. }
  3914. }, this), 0);
  3915. };
  3916. Button.prototype.toggle = function() {
  3917. var changed = true, $element = this.$element, $parent = this.$element.parent(".am-btn-group");
  3918. if ($parent.length) {
  3919. var $input = this.$element.find("input");
  3920. if ($input.prop("type") == "radio") {
  3921. if ($input.prop("checked") && $element.hasClass("am-active")) {
  3922. changed = false;
  3923. } else {
  3924. $parent.find(".am-active").removeClass("am-active");
  3925. }
  3926. }
  3927. if (changed) {
  3928. $input.prop("checked", !$element.hasClass("am-active")).trigger("change");
  3929. }
  3930. }
  3931. if (changed) {
  3932. $element.toggleClass("am-active");
  3933. if (!$element.hasClass("am-active")) {
  3934. $element.blur();
  3935. }
  3936. }
  3937. };
  3938. // Button plugin
  3939. function Plugin(option) {
  3940. return this.each(function() {
  3941. var $this = $(this);
  3942. var data = $this.data("amui.button");
  3943. var options = typeof option == "object" && option || {};
  3944. if (!data) {
  3945. $this.data("amui.button", data = new Button(this, options));
  3946. }
  3947. if (option == "toggle") {
  3948. data.toggle();
  3949. } else if (typeof option == "string") {
  3950. data.setState(option);
  3951. }
  3952. });
  3953. }
  3954. $.fn.button = Plugin;
  3955. // Init code
  3956. $(document).on("click.button.amui", "[data-am-button]", function(e) {
  3957. var $btn = $(e.target);
  3958. if (!$btn.hasClass("am-btn")) {
  3959. $btn = $btn.closest(".am-btn");
  3960. }
  3961. Plugin.call($btn, "toggle");
  3962. e.preventDefault();
  3963. });
  3964. $(function() {
  3965. $("[data-am-loading]").each(function() {
  3966. $(this).button(UI.utils.parseOptions($(this).data("amLoading")));
  3967. });
  3968. });
  3969. module.exports = Button;
  3970. });
  3971. define("ui.collapse", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  3972. "use strict";
  3973. require("core");
  3974. var $ = window.Zepto, UI = $.AMUI;
  3975. /**
  3976. * @via https://github.com/twbs/bootstrap/blob/master/js/collapse.js
  3977. * @copyright (c) 2011-2014 Twitter, Inc
  3978. * @license The MIT License
  3979. */
  3980. var Collapse = function(element, options) {
  3981. this.$element = $(element);
  3982. this.options = $.extend({}, Collapse.DEFAULTS, options);
  3983. this.transitioning = null;
  3984. if (this.options.parent) {
  3985. this.$parent = $(this.options.parent);
  3986. }
  3987. if (this.options.toggle) {
  3988. this.toggle();
  3989. }
  3990. };
  3991. Collapse.DEFAULTS = {
  3992. toggle: true
  3993. };
  3994. Collapse.prototype.open = function() {
  3995. if (this.transitioning || this.$element.hasClass("am-in")) return;
  3996. var startEvent = $.Event("open:collapse:amui");
  3997. this.$element.trigger(startEvent);
  3998. if (startEvent.isDefaultPrevented()) return;
  3999. var actives = this.$parent && this.$parent.find("> .am-panel > .am-in");
  4000. if (actives && actives.length) {
  4001. var hasData = actives.data("amui.collapse");
  4002. if (hasData && hasData.transitioning) return;
  4003. Plugin.call(actives, "close");
  4004. hasData || actives.data("amui.collapse", null);
  4005. }
  4006. this.$element.removeClass("am-collapse").addClass("am-collapsing").height(0);
  4007. this.transitioning = 1;
  4008. var complete = function() {
  4009. this.$element.removeClass("am-collapsing").addClass("am-collapse am-in").height("");
  4010. this.transitioning = 0;
  4011. this.$element.trigger("opened:collapse:amui");
  4012. };
  4013. if (!UI.support.transition) {
  4014. return complete.call(this);
  4015. }
  4016. this.$element.one(UI.support.transition.end, $.proxy(complete, this)).emulateTransitionEnd(300).height(this.$element[0].scrollHeight);
  4017. };
  4018. Collapse.prototype.close = function() {
  4019. if (this.transitioning || !this.$element.hasClass("am-in")) return;
  4020. var startEvent = $.Event("close:collapse:amui");
  4021. this.$element.trigger(startEvent);
  4022. if (startEvent.isDefaultPrevented()) return;
  4023. this.$element.height(this.$element.height());
  4024. this.$element[0].offsetHeight;
  4025. this.$element.addClass("am-collapsing").removeClass("am-collapse").removeClass("am-in");
  4026. this.transitioning = 1;
  4027. var complete = function() {
  4028. this.transitioning = 0;
  4029. this.$element.trigger("closed:collapse:amui").removeClass("am-collapsing").addClass("am-collapse");
  4030. };
  4031. if (!UI.support.transition) {
  4032. return complete.call(this);
  4033. }
  4034. this.$element.height(0).one(UI.support.transition.end, $.proxy(complete, this)).emulateTransitionEnd(350);
  4035. };
  4036. Collapse.prototype.toggle = function() {
  4037. this[this.$element.hasClass("am-in") ? "close" : "open"]();
  4038. };
  4039. UI.collapse = Collapse;
  4040. // Collapse Plugin
  4041. function Plugin(option) {
  4042. return this.each(function() {
  4043. var $this = $(this), data = $this.data("amui.collapse"), options = $.extend({}, Collapse.DEFAULTS, UI.utils.options($this.attr("data-am-collapse")), typeof option == "object" && option);
  4044. if (!data && options.toggle && option == "open") {
  4045. option = !option;
  4046. }
  4047. if (!data) {
  4048. $this.data("amui.collapse", data = new Collapse(this, options));
  4049. }
  4050. if (typeof option == "string") {
  4051. data[option]();
  4052. }
  4053. });
  4054. }
  4055. $.fn.collapse = Plugin;
  4056. // Init code
  4057. $(document).on("click.collapse.amui", "[data-am-collapse]", function(e) {
  4058. var href, $this = $(this), options = UI.utils.options($this.attr("data-am-collapse")), target = options.target || e.preventDefault() || (href = $this.attr("href")) && href.replace(/.*(?=#[^\s]+$)/, "");
  4059. var $target = $(target);
  4060. var data = $target.data("amui.collapse");
  4061. var option = data ? "toggle" : options;
  4062. var parent = options.parent;
  4063. var $parent = parent && $(parent);
  4064. if (!data || !data.transitioning) {
  4065. if ($parent) {
  4066. //'[data-am-collapse*="{parent: \'' + parent + '"]
  4067. $parent.find("[data-am-collapse]").not($this).addClass("am-collapsed");
  4068. }
  4069. $this[$target.hasClass("am-in") ? "addClass" : "removeClass"]("am-collapsed");
  4070. }
  4071. Plugin.call($target, option);
  4072. });
  4073. module.exports = Collapse;
  4074. });
  4075. define("ui.dimmer", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  4076. require("core");
  4077. var $ = window.Zepto, UI = $.AMUI;
  4078. var $doc = $(document), $html = $("html"), transition = UI.support.transition;
  4079. /**
  4080. * 通用遮罩层
  4081. * @constructor
  4082. */
  4083. var Dimmer = function() {
  4084. this.id = UI.utils.generateGUID("am-dimmer");
  4085. this.$element = $(Dimmer.DEFAULTS.tpl, {
  4086. id: this.id
  4087. });
  4088. this.inited = false;
  4089. this.scrollbarWidth = 0;
  4090. this.used = $([]);
  4091. };
  4092. Dimmer.DEFAULTS = {
  4093. tpl: '<div class="am-dimmer" data-am-dimmer></div>'
  4094. };
  4095. Dimmer.prototype.init = function() {
  4096. if (!this.inited) {
  4097. $(document.body).append(this.$element);
  4098. this.inited = true;
  4099. $doc.trigger("init:dimmer:amui");
  4100. }
  4101. return this;
  4102. };
  4103. Dimmer.prototype.open = function(relatedElement) {
  4104. if (!this.inited) this.init();
  4105. var $element = this.$element;
  4106. // 用于多重调用
  4107. if (relatedElement) {
  4108. this.used = this.used.add($(relatedElement));
  4109. }
  4110. this.checkScrollbar().setScrollbar();
  4111. $element.show().trigger("open:dimmer:amui");
  4112. setTimeout(function() {
  4113. $element.addClass("am-active");
  4114. }, 0);
  4115. return this;
  4116. };
  4117. Dimmer.prototype.close = function(relatedElement, force) {
  4118. this.used = this.used.not($(relatedElement));
  4119. if (!force && this.used.length) return this;
  4120. var $element = this.$element;
  4121. $element.removeClass("am-active").trigger("close:dimmer:amui");
  4122. function complete() {
  4123. this.resetScrollbar();
  4124. $element.hide();
  4125. }
  4126. transition ? $element.one(transition.end, $.proxy(complete, this)) : complete.call(this);
  4127. return this;
  4128. };
  4129. Dimmer.prototype.checkScrollbar = function() {
  4130. this.scrollbarWidth = UI.utils.measureScrollbar();
  4131. return this;
  4132. };
  4133. Dimmer.prototype.setScrollbar = function() {
  4134. var $body = $(document.body), bodyPaddingRight = parseInt($body.css("padding-right") || 0, 10);
  4135. if (this.scrollbarWidth) $body.css("padding-right", bodyPaddingRight + this.scrollbarWidth);
  4136. $body.addClass("am-dimmer-active");
  4137. return this;
  4138. };
  4139. Dimmer.prototype.resetScrollbar = function() {
  4140. $(document.body).css("padding-right", "").removeClass("am-dimmer-active");
  4141. return this;
  4142. };
  4143. var dimmer = new Dimmer();
  4144. UI.dimmer = dimmer;
  4145. module.exports = dimmer;
  4146. });
  4147. define("ui.dropdown", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  4148. require("core");
  4149. var $ = window.Zepto, UI = $.AMUI, animation = UI.support.animation;
  4150. /**
  4151. * @via https://github.com/Minwe/bootstrap/blob/master/js/dropdown.js
  4152. * @copyright (c) 2011-2014 Twitter, Inc
  4153. * @license The MIT License
  4154. */
  4155. var toggle = "[data-am-dropdown] > .am-dropdown-toggle";
  4156. var Dropdown = function(element, options) {
  4157. this.options = $.extend({}, Dropdown.DEFAULTS, options);
  4158. options = this.options;
  4159. this.$element = $(element);
  4160. this.$toggle = this.$element.find(options.selector.toggle);
  4161. this.$dropdown = this.$element.find(options.selector.dropdown);
  4162. this.$boundary = options.boundary === window ? $(window) : this.$element.closest(options.boundary);
  4163. this.$justify = options.justify && $(options.justify).length && $(options.justify) || undefined;
  4164. !this.$boundary.length && (this.$boundary = $(window));
  4165. this.active = this.$element.hasClass("am-active") ? true : false;
  4166. this.animating = null;
  4167. this.events();
  4168. };
  4169. Dropdown.DEFAULTS = {
  4170. animation: "am-animation-slide-top-fixed",
  4171. boundary: window,
  4172. justify: undefined,
  4173. selector: {
  4174. dropdown: ".am-dropdown-content",
  4175. toggle: ".am-dropdown-toggle"
  4176. },
  4177. trigger: "click"
  4178. };
  4179. Dropdown.prototype.toggle = function() {
  4180. this.clear();
  4181. if (this.animating) return;
  4182. this[this.active ? "close" : "open"]();
  4183. };
  4184. Dropdown.prototype.open = function(e) {
  4185. var $toggle = this.$toggle, $element = this.$element, $dropdown = this.$dropdown;
  4186. if ($toggle.is(".am-disabled, :disabled")) return;
  4187. if (this.active) return;
  4188. $element.trigger("open:dropdown:amui").addClass("am-active");
  4189. $toggle.trigger("focus");
  4190. this.checkDimensions();
  4191. var complete = $.proxy(function() {
  4192. $element.trigger("opened:dropdown:amui");
  4193. this.active = true;
  4194. this.animating = 0;
  4195. }, this);
  4196. if (animation) {
  4197. this.animating = 1;
  4198. $dropdown.addClass(this.options.animation).on(animation.end + ".open.dropdown.amui", $.proxy(function() {
  4199. complete();
  4200. $dropdown.removeClass(this.options.animation);
  4201. }, this));
  4202. } else {
  4203. complete();
  4204. }
  4205. };
  4206. Dropdown.prototype.close = function() {
  4207. if (!this.active) return;
  4208. var animationName = this.options.animation + " am-animation-reverse", $element = this.$element, $dropdown = this.$dropdown;
  4209. $element.trigger("close:dropdown:amui");
  4210. var complete = $.proxy(function complete() {
  4211. $element.removeClass("am-active").trigger("closed:dropdown:amui");
  4212. this.active = false;
  4213. this.animating = 0;
  4214. this.$toggle.blur();
  4215. }, this);
  4216. if (animation) {
  4217. $dropdown.addClass(animationName);
  4218. this.animating = 1;
  4219. // animation
  4220. $dropdown.one(animation.end + ".close.dropdown.amui", function() {
  4221. complete();
  4222. $dropdown.removeClass(animationName);
  4223. });
  4224. } else {
  4225. complete();
  4226. }
  4227. };
  4228. Dropdown.prototype.checkDimensions = function() {
  4229. if (!this.$dropdown.length) return;
  4230. var $dropdown = this.$dropdown, offset = $dropdown.offset(), width = $dropdown.outerWidth(), boundaryWidth = this.$boundary.width(), boundaryOffset = $.isWindow(this.boundary) && this.$boundary.offset() ? this.$boundary.offset().left : 0;
  4231. if (this.$justify) {
  4232. $dropdown.css({
  4233. "min-width": this.$justify.width()
  4234. });
  4235. }
  4236. if (width + (offset.left - boundaryOffset) > boundaryWidth) {
  4237. this.$element.addClass("am-dropdown-flip");
  4238. }
  4239. };
  4240. Dropdown.prototype.clear = function() {
  4241. $("[data-am-dropdown]").not(this.$element).each(function() {
  4242. var data = $(this).data("amui.dropdown");
  4243. data && data["close"]();
  4244. });
  4245. };
  4246. Dropdown.prototype.events = function() {
  4247. var eventNS = "dropdown.amui", triggers = this.options.trigger.split(" "), $toggle = this.$toggle;
  4248. $toggle.on("click." + eventNS, $.proxy(this.toggle, this));
  4249. /*for (var i = triggers.length; i--;) {
  4250. var trigger = triggers[i];
  4251. if (trigger === 'click') {
  4252. $toggle.on('click.' + eventNS, $.proxy(this.toggle, this))
  4253. }
  4254. if (trigger === 'focus' || trigger === 'hover') {
  4255. var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
  4256. var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
  4257. this.$element.on(eventIn + '.' + eventNS, $.proxy(this.open, this))
  4258. .on(eventOut + '.' + eventNS, $.proxy(this.close, this));
  4259. }
  4260. }*/
  4261. $(document).on("keydown.dropdown.amui", $.proxy(function(e) {
  4262. e.keyCode === 27 && this.active && this.close();
  4263. }, this)).on("click.outer.dropdown.amui", $.proxy(function(e) {
  4264. var $target = $(e.target);
  4265. if (this.active && (this.$element[0] === e.target || !this.$element.find(e.target).length)) {
  4266. this.close();
  4267. }
  4268. }, this));
  4269. };
  4270. UI.dropdown = Dropdown;
  4271. // Dropdown Plugin
  4272. function Plugin(option) {
  4273. return this.each(function() {
  4274. var $this = $(this), data = $this.data("amui.dropdown"), options = $.extend({}, UI.utils.parseOptions($this.attr("data-am-dropdown")), typeof option == "object" && option);
  4275. if (!data) {
  4276. $this.data("amui.dropdown", data = new Dropdown(this, options));
  4277. }
  4278. if (typeof option == "string") {
  4279. data[option]();
  4280. }
  4281. });
  4282. }
  4283. $.fn.dropdown = Plugin;
  4284. // Init code
  4285. $(function() {
  4286. $("[data-am-dropdown]").dropdown();
  4287. });
  4288. $(document).on("click.dropdown.amui", ".am-dropdown form", function(e) {
  4289. e.stopPropagation();
  4290. });
  4291. });
  4292. define("ui.iscroll-lite", [], function(require, exports, module) {
  4293. /*! iScroll v5.1.2 ~ (c) 2008-2014 Matteo Spinelli ~ http://cubiq.org/license */
  4294. (function(window, document, Math) {
  4295. var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
  4296. window.setTimeout(callback, 1e3 / 60);
  4297. };
  4298. var utils = function() {
  4299. var me = {};
  4300. var _elementStyle = document.createElement("div").style;
  4301. var _vendor = function() {
  4302. var vendors = [ "t", "webkitT", "MozT", "msT", "OT" ], transform, i = 0, l = vendors.length;
  4303. for (;i < l; i++) {
  4304. transform = vendors[i] + "ransform";
  4305. if (transform in _elementStyle) return vendors[i].substr(0, vendors[i].length - 1);
  4306. }
  4307. return false;
  4308. }();
  4309. function _prefixStyle(style) {
  4310. if (_vendor === false) return false;
  4311. if (_vendor === "") return style;
  4312. return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
  4313. }
  4314. me.getTime = Date.now || function getTime() {
  4315. return new Date().getTime();
  4316. };
  4317. me.extend = function(target, obj) {
  4318. for (var i in obj) {
  4319. target[i] = obj[i];
  4320. }
  4321. };
  4322. me.addEvent = function(el, type, fn, capture) {
  4323. el.addEventListener(type, fn, !!capture);
  4324. };
  4325. me.removeEvent = function(el, type, fn, capture) {
  4326. el.removeEventListener(type, fn, !!capture);
  4327. };
  4328. me.prefixPointerEvent = function(pointerEvent) {
  4329. return window.MSPointerEvent ? "MSPointer" + pointerEvent.charAt(9).toUpperCase() + pointerEvent.substr(10) : pointerEvent;
  4330. };
  4331. me.momentum = function(current, start, time, lowerMargin, wrapperSize, deceleration) {
  4332. var distance = current - start, speed = Math.abs(distance) / time, destination, duration;
  4333. deceleration = deceleration === undefined ? 6e-4 : deceleration;
  4334. destination = current + speed * speed / (2 * deceleration) * (distance < 0 ? -1 : 1);
  4335. duration = speed / deceleration;
  4336. if (destination < lowerMargin) {
  4337. destination = wrapperSize ? lowerMargin - wrapperSize / 2.5 * (speed / 8) : lowerMargin;
  4338. distance = Math.abs(destination - current);
  4339. duration = distance / speed;
  4340. } else if (destination > 0) {
  4341. destination = wrapperSize ? wrapperSize / 2.5 * (speed / 8) : 0;
  4342. distance = Math.abs(current) + destination;
  4343. duration = distance / speed;
  4344. }
  4345. return {
  4346. destination: Math.round(destination),
  4347. duration: duration
  4348. };
  4349. };
  4350. var _transform = _prefixStyle("transform");
  4351. me.extend(me, {
  4352. hasTransform: _transform !== false,
  4353. hasPerspective: _prefixStyle("perspective") in _elementStyle,
  4354. hasTouch: "ontouchstart" in window,
  4355. hasPointer: window.PointerEvent || window.MSPointerEvent,
  4356. // IE10 is prefixed
  4357. hasTransition: _prefixStyle("transition") in _elementStyle
  4358. });
  4359. // This should find all Android browsers lower than build 535.19 (both stock browser and webview)
  4360. me.isBadAndroid = /Android /.test(window.navigator.appVersion) && !/Chrome\/\d/.test(window.navigator.appVersion);
  4361. me.extend(me.style = {}, {
  4362. transform: _transform,
  4363. transitionTimingFunction: _prefixStyle("transitionTimingFunction"),
  4364. transitionDuration: _prefixStyle("transitionDuration"),
  4365. transitionDelay: _prefixStyle("transitionDelay"),
  4366. transformOrigin: _prefixStyle("transformOrigin")
  4367. });
  4368. me.hasClass = function(e, c) {
  4369. var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
  4370. return re.test(e.className);
  4371. };
  4372. me.addClass = function(e, c) {
  4373. if (me.hasClass(e, c)) {
  4374. return;
  4375. }
  4376. var newclass = e.className.split(" ");
  4377. newclass.push(c);
  4378. e.className = newclass.join(" ");
  4379. };
  4380. me.removeClass = function(e, c) {
  4381. if (!me.hasClass(e, c)) {
  4382. return;
  4383. }
  4384. var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g");
  4385. e.className = e.className.replace(re, " ");
  4386. };
  4387. me.offset = function(el) {
  4388. var left = -el.offsetLeft, top = -el.offsetTop;
  4389. // jshint -W084
  4390. while (el = el.offsetParent) {
  4391. left -= el.offsetLeft;
  4392. top -= el.offsetTop;
  4393. }
  4394. // jshint +W084
  4395. return {
  4396. left: left,
  4397. top: top
  4398. };
  4399. };
  4400. me.preventDefaultException = function(el, exceptions) {
  4401. for (var i in exceptions) {
  4402. if (exceptions[i].test(el[i])) {
  4403. return true;
  4404. }
  4405. }
  4406. return false;
  4407. };
  4408. me.extend(me.eventType = {}, {
  4409. touchstart: 1,
  4410. touchmove: 1,
  4411. touchend: 1,
  4412. mousedown: 2,
  4413. mousemove: 2,
  4414. mouseup: 2,
  4415. pointerdown: 3,
  4416. pointermove: 3,
  4417. pointerup: 3,
  4418. MSPointerDown: 3,
  4419. MSPointerMove: 3,
  4420. MSPointerUp: 3
  4421. });
  4422. me.extend(me.ease = {}, {
  4423. quadratic: {
  4424. style: "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
  4425. fn: function(k) {
  4426. return k * (2 - k);
  4427. }
  4428. },
  4429. circular: {
  4430. style: "cubic-bezier(0.1, 0.57, 0.1, 1)",
  4431. // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
  4432. fn: function(k) {
  4433. return Math.sqrt(1 - --k * k);
  4434. }
  4435. },
  4436. back: {
  4437. style: "cubic-bezier(0.175, 0.885, 0.32, 1.275)",
  4438. fn: function(k) {
  4439. var b = 4;
  4440. return (k = k - 1) * k * ((b + 1) * k + b) + 1;
  4441. }
  4442. },
  4443. bounce: {
  4444. style: "",
  4445. fn: function(k) {
  4446. if ((k /= 1) < 1 / 2.75) {
  4447. return 7.5625 * k * k;
  4448. } else if (k < 2 / 2.75) {
  4449. return 7.5625 * (k -= 1.5 / 2.75) * k + .75;
  4450. } else if (k < 2.5 / 2.75) {
  4451. return 7.5625 * (k -= 2.25 / 2.75) * k + .9375;
  4452. } else {
  4453. return 7.5625 * (k -= 2.625 / 2.75) * k + .984375;
  4454. }
  4455. }
  4456. },
  4457. elastic: {
  4458. style: "",
  4459. fn: function(k) {
  4460. var f = .22, e = .4;
  4461. if (k === 0) {
  4462. return 0;
  4463. }
  4464. if (k == 1) {
  4465. return 1;
  4466. }
  4467. return e * Math.pow(2, -10 * k) * Math.sin((k - f / 4) * 2 * Math.PI / f) + 1;
  4468. }
  4469. }
  4470. });
  4471. me.tap = function(e, eventName) {
  4472. var ev = document.createEvent("Event");
  4473. ev.initEvent(eventName, true, true);
  4474. ev.pageX = e.pageX;
  4475. ev.pageY = e.pageY;
  4476. e.target.dispatchEvent(ev);
  4477. };
  4478. me.click = function(e) {
  4479. var target = e.target, ev;
  4480. if (!/(SELECT|INPUT|TEXTAREA)/i.test(target.tagName)) {
  4481. ev = document.createEvent("MouseEvents");
  4482. ev.initMouseEvent("click", true, true, e.view, 1, target.screenX, target.screenY, target.clientX, target.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null);
  4483. ev._constructed = true;
  4484. target.dispatchEvent(ev);
  4485. }
  4486. };
  4487. return me;
  4488. }();
  4489. function IScroll(el, options) {
  4490. this.wrapper = typeof el == "string" ? document.querySelector(el) : el;
  4491. this.scroller = this.wrapper.children[0];
  4492. this.scrollerStyle = this.scroller.style;
  4493. // cache style for better performance
  4494. this.options = {
  4495. // INSERT POINT: OPTIONS
  4496. startX: 0,
  4497. startY: 0,
  4498. scrollY: true,
  4499. directionLockThreshold: 5,
  4500. momentum: true,
  4501. bounce: true,
  4502. bounceTime: 600,
  4503. bounceEasing: "",
  4504. preventDefault: true,
  4505. preventDefaultException: {
  4506. tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
  4507. },
  4508. HWCompositing: true,
  4509. useTransition: true,
  4510. useTransform: true
  4511. };
  4512. for (var i in options) {
  4513. this.options[i] = options[i];
  4514. }
  4515. // Normalize options
  4516. this.translateZ = this.options.HWCompositing && utils.hasPerspective ? " translateZ(0)" : "";
  4517. this.options.useTransition = utils.hasTransition && this.options.useTransition;
  4518. this.options.useTransform = utils.hasTransform && this.options.useTransform;
  4519. this.options.eventPassthrough = this.options.eventPassthrough === true ? "vertical" : this.options.eventPassthrough;
  4520. this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
  4521. // If you want eventPassthrough I have to lock one of the axes
  4522. this.options.scrollY = this.options.eventPassthrough == "vertical" ? false : this.options.scrollY;
  4523. this.options.scrollX = this.options.eventPassthrough == "horizontal" ? false : this.options.scrollX;
  4524. // With eventPassthrough we also need lockDirection mechanism
  4525. this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
  4526. this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
  4527. this.options.bounceEasing = typeof this.options.bounceEasing == "string" ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
  4528. this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
  4529. if (this.options.tap === true) {
  4530. this.options.tap = "tap";
  4531. }
  4532. // INSERT POINT: NORMALIZATION
  4533. // Some defaults
  4534. this.x = 0;
  4535. this.y = 0;
  4536. this.directionX = 0;
  4537. this.directionY = 0;
  4538. this._events = {};
  4539. // INSERT POINT: DEFAULTS
  4540. this._init();
  4541. this.refresh();
  4542. this.scrollTo(this.options.startX, this.options.startY);
  4543. this.enable();
  4544. }
  4545. IScroll.prototype = {
  4546. version: "5.1.2",
  4547. _init: function() {
  4548. this._initEvents();
  4549. },
  4550. destroy: function() {
  4551. this._initEvents(true);
  4552. this._execEvent("destroy");
  4553. },
  4554. _transitionEnd: function(e) {
  4555. if (e.target != this.scroller || !this.isInTransition) {
  4556. return;
  4557. }
  4558. this._transitionTime();
  4559. if (!this.resetPosition(this.options.bounceTime)) {
  4560. this.isInTransition = false;
  4561. this._execEvent("scrollEnd");
  4562. }
  4563. },
  4564. _start: function(e) {
  4565. // React to left mouse button only
  4566. if (utils.eventType[e.type] != 1) {
  4567. if (e.button !== 0) {
  4568. return;
  4569. }
  4570. }
  4571. if (!this.enabled || this.initiated && utils.eventType[e.type] !== this.initiated) {
  4572. return;
  4573. }
  4574. if (this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
  4575. e.preventDefault();
  4576. }
  4577. var point = e.touches ? e.touches[0] : e, pos;
  4578. this.initiated = utils.eventType[e.type];
  4579. this.moved = false;
  4580. this.distX = 0;
  4581. this.distY = 0;
  4582. this.directionX = 0;
  4583. this.directionY = 0;
  4584. this.directionLocked = 0;
  4585. this._transitionTime();
  4586. this.startTime = utils.getTime();
  4587. if (this.options.useTransition && this.isInTransition) {
  4588. this.isInTransition = false;
  4589. pos = this.getComputedPosition();
  4590. this._translate(Math.round(pos.x), Math.round(pos.y));
  4591. this._execEvent("scrollEnd");
  4592. } else if (!this.options.useTransition && this.isAnimating) {
  4593. this.isAnimating = false;
  4594. this._execEvent("scrollEnd");
  4595. }
  4596. this.startX = this.x;
  4597. this.startY = this.y;
  4598. this.absStartX = this.x;
  4599. this.absStartY = this.y;
  4600. this.pointX = point.pageX;
  4601. this.pointY = point.pageY;
  4602. this._execEvent("beforeScrollStart");
  4603. },
  4604. _move: function(e) {
  4605. if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
  4606. return;
  4607. }
  4608. if (this.options.preventDefault) {
  4609. // increases performance on Android? TODO: check!
  4610. e.preventDefault();
  4611. }
  4612. var point = e.touches ? e.touches[0] : e, deltaX = point.pageX - this.pointX, deltaY = point.pageY - this.pointY, timestamp = utils.getTime(), newX, newY, absDistX, absDistY;
  4613. this.pointX = point.pageX;
  4614. this.pointY = point.pageY;
  4615. this.distX += deltaX;
  4616. this.distY += deltaY;
  4617. absDistX = Math.abs(this.distX);
  4618. absDistY = Math.abs(this.distY);
  4619. // We need to move at least 10 pixels for the scrolling to initiate
  4620. if (timestamp - this.endTime > 300 && absDistX < 10 && absDistY < 10) {
  4621. return;
  4622. }
  4623. // If you are scrolling in one direction lock the other
  4624. if (!this.directionLocked && !this.options.freeScroll) {
  4625. if (absDistX > absDistY + this.options.directionLockThreshold) {
  4626. this.directionLocked = "h";
  4627. } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
  4628. this.directionLocked = "v";
  4629. } else {
  4630. this.directionLocked = "n";
  4631. }
  4632. }
  4633. if (this.directionLocked == "h") {
  4634. if (this.options.eventPassthrough == "vertical") {
  4635. e.preventDefault();
  4636. } else if (this.options.eventPassthrough == "horizontal") {
  4637. this.initiated = false;
  4638. return;
  4639. }
  4640. deltaY = 0;
  4641. } else if (this.directionLocked == "v") {
  4642. if (this.options.eventPassthrough == "horizontal") {
  4643. e.preventDefault();
  4644. } else if (this.options.eventPassthrough == "vertical") {
  4645. this.initiated = false;
  4646. return;
  4647. }
  4648. deltaX = 0;
  4649. }
  4650. deltaX = this.hasHorizontalScroll ? deltaX : 0;
  4651. deltaY = this.hasVerticalScroll ? deltaY : 0;
  4652. newX = this.x + deltaX;
  4653. newY = this.y + deltaY;
  4654. // Slow down if outside of the boundaries
  4655. if (newX > 0 || newX < this.maxScrollX) {
  4656. newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
  4657. }
  4658. if (newY > 0 || newY < this.maxScrollY) {
  4659. newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
  4660. }
  4661. this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
  4662. this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  4663. if (!this.moved) {
  4664. this._execEvent("scrollStart");
  4665. }
  4666. this.moved = true;
  4667. this._translate(newX, newY);
  4668. /* REPLACE START: _move */
  4669. if (timestamp - this.startTime > 300) {
  4670. this.startTime = timestamp;
  4671. this.startX = this.x;
  4672. this.startY = this.y;
  4673. }
  4674. },
  4675. _end: function(e) {
  4676. if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
  4677. return;
  4678. }
  4679. if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
  4680. e.preventDefault();
  4681. }
  4682. var point = e.changedTouches ? e.changedTouches[0] : e, momentumX, momentumY, duration = utils.getTime() - this.startTime, newX = Math.round(this.x), newY = Math.round(this.y), distanceX = Math.abs(newX - this.startX), distanceY = Math.abs(newY - this.startY), time = 0, easing = "";
  4683. this.isInTransition = 0;
  4684. this.initiated = 0;
  4685. this.endTime = utils.getTime();
  4686. // reset if we are outside of the boundaries
  4687. if (this.resetPosition(this.options.bounceTime)) {
  4688. return;
  4689. }
  4690. this.scrollTo(newX, newY);
  4691. // ensures that the last position is rounded
  4692. // we scrolled less than 10 pixels
  4693. if (!this.moved) {
  4694. if (this.options.tap) {
  4695. utils.tap(e, this.options.tap);
  4696. }
  4697. if (this.options.click) {
  4698. utils.click(e);
  4699. }
  4700. this._execEvent("scrollCancel");
  4701. return;
  4702. }
  4703. if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) {
  4704. this._execEvent("flick");
  4705. return;
  4706. }
  4707. // start momentum animation if needed
  4708. if (this.options.momentum && duration < 300) {
  4709. momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : {
  4710. destination: newX,
  4711. duration: 0
  4712. };
  4713. momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : {
  4714. destination: newY,
  4715. duration: 0
  4716. };
  4717. newX = momentumX.destination;
  4718. newY = momentumY.destination;
  4719. time = Math.max(momentumX.duration, momentumY.duration);
  4720. this.isInTransition = 1;
  4721. }
  4722. // INSERT POINT: _end
  4723. if (newX != this.x || newY != this.y) {
  4724. // change easing function when scroller goes out of the boundaries
  4725. if (newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY) {
  4726. easing = utils.ease.quadratic;
  4727. }
  4728. this.scrollTo(newX, newY, time, easing);
  4729. return;
  4730. }
  4731. this._execEvent("scrollEnd");
  4732. },
  4733. _resize: function() {
  4734. var that = this;
  4735. clearTimeout(this.resizeTimeout);
  4736. this.resizeTimeout = setTimeout(function() {
  4737. that.refresh();
  4738. }, this.options.resizePolling);
  4739. },
  4740. resetPosition: function(time) {
  4741. var x = this.x, y = this.y;
  4742. time = time || 0;
  4743. if (!this.hasHorizontalScroll || this.x > 0) {
  4744. x = 0;
  4745. } else if (this.x < this.maxScrollX) {
  4746. x = this.maxScrollX;
  4747. }
  4748. if (!this.hasVerticalScroll || this.y > 0) {
  4749. y = 0;
  4750. } else if (this.y < this.maxScrollY) {
  4751. y = this.maxScrollY;
  4752. }
  4753. if (x == this.x && y == this.y) {
  4754. return false;
  4755. }
  4756. this.scrollTo(x, y, time, this.options.bounceEasing);
  4757. return true;
  4758. },
  4759. disable: function() {
  4760. this.enabled = false;
  4761. },
  4762. enable: function() {
  4763. this.enabled = true;
  4764. },
  4765. refresh: function() {
  4766. var rf = this.wrapper.offsetHeight;
  4767. // Force reflow
  4768. this.wrapperWidth = this.wrapper.clientWidth;
  4769. this.wrapperHeight = this.wrapper.clientHeight;
  4770. /* REPLACE START: refresh */
  4771. this.scrollerWidth = this.scroller.offsetWidth;
  4772. this.scrollerHeight = this.scroller.offsetHeight;
  4773. this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
  4774. this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
  4775. /* REPLACE END: refresh */
  4776. this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
  4777. this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
  4778. if (!this.hasHorizontalScroll) {
  4779. this.maxScrollX = 0;
  4780. this.scrollerWidth = this.wrapperWidth;
  4781. }
  4782. if (!this.hasVerticalScroll) {
  4783. this.maxScrollY = 0;
  4784. this.scrollerHeight = this.wrapperHeight;
  4785. }
  4786. this.endTime = 0;
  4787. this.directionX = 0;
  4788. this.directionY = 0;
  4789. this.wrapperOffset = utils.offset(this.wrapper);
  4790. this._execEvent("refresh");
  4791. this.resetPosition();
  4792. },
  4793. on: function(type, fn) {
  4794. if (!this._events[type]) {
  4795. this._events[type] = [];
  4796. }
  4797. this._events[type].push(fn);
  4798. },
  4799. off: function(type, fn) {
  4800. if (!this._events[type]) {
  4801. return;
  4802. }
  4803. var index = this._events[type].indexOf(fn);
  4804. if (index > -1) {
  4805. this._events[type].splice(index, 1);
  4806. }
  4807. },
  4808. _execEvent: function(type) {
  4809. if (!this._events[type]) {
  4810. return;
  4811. }
  4812. var i = 0, l = this._events[type].length;
  4813. if (!l) {
  4814. return;
  4815. }
  4816. for (;i < l; i++) {
  4817. this._events[type][i].apply(this, [].slice.call(arguments, 1));
  4818. }
  4819. },
  4820. scrollBy: function(x, y, time, easing) {
  4821. x = this.x + x;
  4822. y = this.y + y;
  4823. time = time || 0;
  4824. this.scrollTo(x, y, time, easing);
  4825. },
  4826. scrollTo: function(x, y, time, easing) {
  4827. easing = easing || utils.ease.circular;
  4828. this.isInTransition = this.options.useTransition && time > 0;
  4829. if (!time || this.options.useTransition && easing.style) {
  4830. this._transitionTimingFunction(easing.style);
  4831. this._transitionTime(time);
  4832. this._translate(x, y);
  4833. } else {
  4834. this._animate(x, y, time, easing.fn);
  4835. }
  4836. },
  4837. scrollToElement: function(el, time, offsetX, offsetY, easing) {
  4838. el = el.nodeType ? el : this.scroller.querySelector(el);
  4839. if (!el) {
  4840. return;
  4841. }
  4842. var pos = utils.offset(el);
  4843. pos.left -= this.wrapperOffset.left;
  4844. pos.top -= this.wrapperOffset.top;
  4845. // if offsetX/Y are true we center the element to the screen
  4846. if (offsetX === true) {
  4847. offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
  4848. }
  4849. if (offsetY === true) {
  4850. offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
  4851. }
  4852. pos.left -= offsetX || 0;
  4853. pos.top -= offsetY || 0;
  4854. pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
  4855. pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;
  4856. time = time === undefined || time === null || time === "auto" ? Math.max(Math.abs(this.x - pos.left), Math.abs(this.y - pos.top)) : time;
  4857. this.scrollTo(pos.left, pos.top, time, easing);
  4858. },
  4859. _transitionTime: function(time) {
  4860. time = time || 0;
  4861. this.scrollerStyle[utils.style.transitionDuration] = time + "ms";
  4862. if (!time && utils.isBadAndroid) {
  4863. this.scrollerStyle[utils.style.transitionDuration] = "0.001s";
  4864. }
  4865. },
  4866. _transitionTimingFunction: function(easing) {
  4867. this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
  4868. },
  4869. _translate: function(x, y) {
  4870. if (this.options.useTransform) {
  4871. /* REPLACE START: _translate */
  4872. this.scrollerStyle[utils.style.transform] = "translate(" + x + "px," + y + "px)" + this.translateZ;
  4873. } else {
  4874. x = Math.round(x);
  4875. y = Math.round(y);
  4876. this.scrollerStyle.left = x + "px";
  4877. this.scrollerStyle.top = y + "px";
  4878. }
  4879. this.x = x;
  4880. this.y = y;
  4881. },
  4882. _initEvents: function(remove) {
  4883. var eventType = remove ? utils.removeEvent : utils.addEvent, target = this.options.bindToWrapper ? this.wrapper : window;
  4884. eventType(window, "orientationchange", this);
  4885. eventType(window, "resize", this);
  4886. if (this.options.click) {
  4887. eventType(this.wrapper, "click", this, true);
  4888. }
  4889. if (!this.options.disableMouse) {
  4890. eventType(this.wrapper, "mousedown", this);
  4891. eventType(target, "mousemove", this);
  4892. eventType(target, "mousecancel", this);
  4893. eventType(target, "mouseup", this);
  4894. }
  4895. if (utils.hasPointer && !this.options.disablePointer) {
  4896. eventType(this.wrapper, utils.prefixPointerEvent("pointerdown"), this);
  4897. eventType(target, utils.prefixPointerEvent("pointermove"), this);
  4898. eventType(target, utils.prefixPointerEvent("pointercancel"), this);
  4899. eventType(target, utils.prefixPointerEvent("pointerup"), this);
  4900. }
  4901. if (utils.hasTouch && !this.options.disableTouch) {
  4902. eventType(this.wrapper, "touchstart", this);
  4903. eventType(target, "touchmove", this);
  4904. eventType(target, "touchcancel", this);
  4905. eventType(target, "touchend", this);
  4906. }
  4907. eventType(this.scroller, "transitionend", this);
  4908. eventType(this.scroller, "webkitTransitionEnd", this);
  4909. eventType(this.scroller, "oTransitionEnd", this);
  4910. eventType(this.scroller, "MSTransitionEnd", this);
  4911. },
  4912. getComputedPosition: function() {
  4913. var matrix = window.getComputedStyle(this.scroller, null), x, y;
  4914. if (this.options.useTransform) {
  4915. matrix = matrix[utils.style.transform].split(")")[0].split(", ");
  4916. x = +(matrix[12] || matrix[4]);
  4917. y = +(matrix[13] || matrix[5]);
  4918. } else {
  4919. x = +matrix.left.replace(/[^-\d.]/g, "");
  4920. y = +matrix.top.replace(/[^-\d.]/g, "");
  4921. }
  4922. return {
  4923. x: x,
  4924. y: y
  4925. };
  4926. },
  4927. _animate: function(destX, destY, duration, easingFn) {
  4928. var that = this, startX = this.x, startY = this.y, startTime = utils.getTime(), destTime = startTime + duration;
  4929. function step() {
  4930. var now = utils.getTime(), newX, newY, easing;
  4931. if (now >= destTime) {
  4932. that.isAnimating = false;
  4933. that._translate(destX, destY);
  4934. if (!that.resetPosition(that.options.bounceTime)) {
  4935. that._execEvent("scrollEnd");
  4936. }
  4937. return;
  4938. }
  4939. now = (now - startTime) / duration;
  4940. easing = easingFn(now);
  4941. newX = (destX - startX) * easing + startX;
  4942. newY = (destY - startY) * easing + startY;
  4943. that._translate(newX, newY);
  4944. if (that.isAnimating) {
  4945. rAF(step);
  4946. }
  4947. }
  4948. this.isAnimating = true;
  4949. step();
  4950. },
  4951. handleEvent: function(e) {
  4952. switch (e.type) {
  4953. case "touchstart":
  4954. case "pointerdown":
  4955. case "MSPointerDown":
  4956. case "mousedown":
  4957. this._start(e);
  4958. break;
  4959. case "touchmove":
  4960. case "pointermove":
  4961. case "MSPointerMove":
  4962. case "mousemove":
  4963. this._move(e);
  4964. break;
  4965. case "touchend":
  4966. case "pointerup":
  4967. case "MSPointerUp":
  4968. case "mouseup":
  4969. case "touchcancel":
  4970. case "pointercancel":
  4971. case "MSPointerCancel":
  4972. case "mousecancel":
  4973. this._end(e);
  4974. break;
  4975. case "orientationchange":
  4976. case "resize":
  4977. this._resize();
  4978. break;
  4979. case "transitionend":
  4980. case "webkitTransitionEnd":
  4981. case "oTransitionEnd":
  4982. case "MSTransitionEnd":
  4983. this._transitionEnd(e);
  4984. break;
  4985. case "wheel":
  4986. case "DOMMouseScroll":
  4987. case "mousewheel":
  4988. this._wheel(e);
  4989. break;
  4990. case "keydown":
  4991. this._key(e);
  4992. break;
  4993. case "click":
  4994. if (!e._constructed) {
  4995. e.preventDefault();
  4996. e.stopPropagation();
  4997. }
  4998. break;
  4999. }
  5000. }
  5001. };
  5002. IScroll.utils = utils;
  5003. if (typeof module != "undefined" && module.exports) {
  5004. module.exports = IScroll;
  5005. } else {
  5006. window.IScroll = IScroll;
  5007. }
  5008. })(window, document, Math);
  5009. });
  5010. define("ui.modal", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.dimmer" ], function(require, exports, module) {
  5011. require("core");
  5012. var dimmer = require("ui.dimmer");
  5013. var $ = window.Zepto;
  5014. var UI = $.AMUI;
  5015. var $win = $(window), $doc = $(document), $body = $("body"), supportTransition = UI.support.transition;
  5016. /**
  5017. * @reference https://github.com/nolimits4web/Framework7/blob/master/src/js/modals.js
  5018. * @license https://github.com/nolimits4web/Framework7/blob/master/LICENSE
  5019. */
  5020. var Modal = function(element, options) {
  5021. this.options = $.extend({}, Modal.DEFAULTS, options || {});
  5022. this.$element = $(element);
  5023. if (!this.$element.attr("id")) {
  5024. this.$element.attr("id", UI.utils.generateGUID("am-modal"));
  5025. }
  5026. this.isPopup = this.$element.hasClass("am-popup");
  5027. this.active = this.transitioning = null;
  5028. this.events();
  5029. };
  5030. Modal.DEFAULTS = {
  5031. className: {
  5032. active: "am-modal-active",
  5033. out: "am-modal-out"
  5034. },
  5035. selector: {
  5036. modal: ".am-modal",
  5037. active: ".am-modal-active"
  5038. },
  5039. cancelable: true,
  5040. onConfirm: function() {},
  5041. onCancel: function() {},
  5042. duration: 300
  5043. };
  5044. Modal.prototype.toggle = function(relatedElement) {
  5045. return this.active ? this.close() : this.open(relatedElement);
  5046. };
  5047. Modal.prototype.open = function(relatedElement) {
  5048. var $element = this.$element, options = this.options, isPopup = this.isPopup;
  5049. if (this.transitioning || this.active) return;
  5050. if (!this.$element.length) return;
  5051. isPopup && this.$element.show();
  5052. this.active = true;
  5053. $element.trigger($.Event("open:modal:amui", {
  5054. relatedElement: relatedElement
  5055. }));
  5056. dimmer.open($element);
  5057. $element.show().redraw();
  5058. !isPopup && $element.css({
  5059. marginTop: -parseInt($element.height() / 2, 10) + "px"
  5060. });
  5061. $element.removeClass(options.className.out).addClass(options.className.active);
  5062. this.transitioning = 1;
  5063. var complete = function() {
  5064. $element.trigger($.Event("opened:modal:amui", {
  5065. relatedElement: relatedElement
  5066. }));
  5067. this.transitioning = 0;
  5068. };
  5069. if (!supportTransition) return complete.call(this);
  5070. $element.one(supportTransition.end, $.proxy(complete, this)).emulateTransitionEnd(options.duration);
  5071. };
  5072. Modal.prototype.close = function(relatedElement) {
  5073. if (this.transitioning || !this.active) return;
  5074. var $element = this.$element, options = this.options, isPopup = this.isPopup, that = this;
  5075. this.$element.trigger($.Event("close:modal:amui", {
  5076. relatedElement: relatedElement
  5077. }));
  5078. this.transitioning = 1;
  5079. var complete = function() {
  5080. $element.trigger("closed:amui:modal");
  5081. isPopup && $element.removeClass(options.className.out);
  5082. $element.hide();
  5083. this.transitioning = 0;
  5084. };
  5085. $element.removeClass(options.className.active).addClass(options.className.out);
  5086. if (!supportTransition) return complete.call(this);
  5087. $element.one(supportTransition.end, $.proxy(complete, this)).emulateTransitionEnd(options.duration);
  5088. dimmer.close($element);
  5089. this.active = false;
  5090. };
  5091. Modal.prototype.events = function() {
  5092. var that = this, $element = this.$element, $ipt = $element.find(".am-modal-prompt-input");
  5093. if (this.options.cancelable) {
  5094. $element.on("keyup.modal.amui", $.proxy(function(e) {
  5095. if (this.active && e.which === 27) {
  5096. this.options.onCancel();
  5097. this.close();
  5098. }
  5099. }, that));
  5100. dimmer.$element.on("click", function(e) {
  5101. that.close();
  5102. });
  5103. }
  5104. // Close button
  5105. $element.find("[data-am-modal-close]").on("click.modal.amui", function(e) {
  5106. e.preventDefault();
  5107. that.close();
  5108. });
  5109. $element.find(".am-modal-btn").on("click.modal.amui", function(e) {
  5110. that.close();
  5111. });
  5112. $element.find("[data-am-modal-confirm]").on("click.modal.amui", function() {
  5113. that.options.onConfirm($ipt.val());
  5114. });
  5115. $element.find("[data-am-modal-cancel]").on("click.modal.amui", function() {
  5116. that.options.onCancel($ipt.val());
  5117. });
  5118. };
  5119. function Plugin(option, relatedElement) {
  5120. return this.each(function() {
  5121. var $this = $(this), data = $this.data("am.modal"), options = $.extend({}, Modal.DEFAULTS, typeof option == "object" && option);
  5122. if (!data) {
  5123. $this.data("am.modal", data = new Modal(this, options));
  5124. }
  5125. if (typeof option == "string") {
  5126. data[option](relatedElement);
  5127. } else {
  5128. data.toggle(option && option.relatedElement || undefined);
  5129. }
  5130. });
  5131. }
  5132. $.fn.modal = Plugin;
  5133. $doc.on("click", "[data-am-modal]", function() {
  5134. var $this = $(this), options = UI.utils.parseOptions($this.attr("data-am-modal")), $target = $(options.target || this.href && this.href.replace(/.*(?=#[^\s]+$)/, "")), option = $target.data("am.modal") ? "toggle" : options;
  5135. Plugin.call($target, option, this);
  5136. });
  5137. module.exports = Modal;
  5138. });
  5139. define("ui.offcanvas", [ "zepto.outerdemension", "zepto.extend.data", "core", "zepto.extend.fx", "zepto.extend.selector" ], function(require, exports, module) {
  5140. require("zepto.outerdemension");
  5141. require("zepto.extend.data");
  5142. require("core");
  5143. var $ = window.Zepto, UI = $.AMUI, $win = $(window), $doc = $(document), scrollPos;
  5144. /**
  5145. * @via https://github.com/uikit/uikit/blob/master/src/js/offcanvas.js
  5146. * @license https://github.com/uikit/uikit/blob/master/LICENSE.md
  5147. */
  5148. var OffCanvas = function(element, options) {
  5149. this.$element = $(element);
  5150. this.options = options;
  5151. this.events();
  5152. };
  5153. OffCanvas.DEFAULTS = {
  5154. effect: "overlay"
  5155. };
  5156. OffCanvas.prototype.open = function(relatedElement) {
  5157. var _self = this, $element = this.$element, openEvent = $.Event("open:offcanvas:amui");
  5158. if (!$element.length || $element.hasClass("am-active")) return;
  5159. var effect = this.options.effect, $html = $("html"), $bar = $element.find(".am-offcanvas-bar").first(), dir = $bar.hasClass("am-offcanvas-bar-flip") ? -1 : 1;
  5160. $bar.addClass("am-offcanvas-bar-" + effect);
  5161. scrollPos = {
  5162. x: window.scrollX,
  5163. y: window.scrollY
  5164. };
  5165. $element.addClass("am-active");
  5166. $html.css({
  5167. width: "100%",
  5168. height: $win.height()
  5169. }).addClass("am-offcanvas-page");
  5170. if (!(effect === "overlay")) {
  5171. $html.css({
  5172. "margin-left": $bar.outerWidth() * dir
  5173. }).width();
  5174. }
  5175. $html.css("margin-top", scrollPos.y * -1);
  5176. UI.utils.debounce(function() {
  5177. $bar.addClass("am-offcanvas-bar-active").width();
  5178. }, 0)();
  5179. $doc.trigger(openEvent);
  5180. $element.off(".offcanvas.amui").on("click.offcanvas.amui swipeRight.offcanvas.amui swipeLeft.offcanvas.amui", function(e) {
  5181. var $target = $(e.target);
  5182. if (!e.type.match(/swipe/)) {
  5183. if ($target.hasClass("am-offcanvas-bar")) return;
  5184. if ($target.parents(".am-offcanvas-bar").first().length) return;
  5185. }
  5186. // https://developer.mozilla.org/zh-CN/docs/DOM/event.stopImmediatePropagation
  5187. e.stopImmediatePropagation();
  5188. _self.close();
  5189. });
  5190. $doc.on("keydown.offcanvas.amui", function(e) {
  5191. if (e.keyCode === 27) {
  5192. // ESC
  5193. _self.close();
  5194. }
  5195. });
  5196. };
  5197. OffCanvas.prototype.close = function(relatedElement) {
  5198. var $html = $("html"), $element = this.$element, $bar = $element.find(".am-offcanvas-bar").first();
  5199. if (!$element.length || !$element.hasClass("am-active")) return;
  5200. $element.trigger("close:offcanvas:amui");
  5201. if (UI.support.transition) {
  5202. $html.one(UI.support.transition.end, function() {
  5203. $html.removeClass("am-offcanvas-page").css({
  5204. width: "",
  5205. height: "",
  5206. "margin-top": ""
  5207. });
  5208. $element.removeClass("am-active");
  5209. window.scrollTo(scrollPos.x, scrollPos.y);
  5210. }).css("margin-left", "");
  5211. UI.utils.debounce(function() {
  5212. $bar.removeClass("am-offcanvas-bar-active");
  5213. }, 0)();
  5214. } else {
  5215. $html.removeClass("am-offcanvas-page").attr("style", "");
  5216. $element.removeClass("am-active");
  5217. $bar.removeClass("am-offcanvas-bar-active");
  5218. window.scrollTo(scrollPos.x, scrollPos.y);
  5219. }
  5220. $element.off(".offcanvas.amui");
  5221. };
  5222. OffCanvas.prototype.events = function() {
  5223. $doc.on("click.offcanvas.amui", '[data-am-dismiss="offcanvas"]', $.proxy(function(e) {
  5224. e.preventDefault();
  5225. this.close();
  5226. }, this));
  5227. return this;
  5228. };
  5229. UI.offcanvas = OffCanvas;
  5230. function Plugin(option, relatedElement) {
  5231. return this.each(function() {
  5232. var $this = $(this), data = $this.data("am.offcanvas"), options = $.extend({}, OffCanvas.DEFAULTS, typeof option == "object" && option);
  5233. if (!data) {
  5234. $this.data("am.offcanvas", data = new OffCanvas(this, options));
  5235. data.open(relatedElement);
  5236. }
  5237. if (typeof option == "string") {
  5238. data[option] && data[option](relatedElement);
  5239. }
  5240. });
  5241. }
  5242. $.fn.offCanvas = Plugin;
  5243. // Init code
  5244. $doc.on("click.offcanvas.amui", "[data-am-offcanvas]", function(e) {
  5245. e.preventDefault();
  5246. var $this = $(this), options = UI.utils.parseOptions($this.attr("data-am-offcanvas")), $target = $(options.target || this.href && this.href.replace(/.*(?=#[^\s]+$)/, ""));
  5247. option = $target.data("am.offcanvas") ? "open" : options;
  5248. Plugin.call($target, option, this);
  5249. });
  5250. module.exports = OffCanvas;
  5251. });
  5252. define("ui.popover", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  5253. require("core");
  5254. var $ = window.Zepto, UI = $.AMUI, $w = $(window), $doc = $(document);
  5255. /**
  5256. * @reference https://github.com/nolimits4web/Framework7/blob/master/src/js/modals.js
  5257. * @license https://github.com/nolimits4web/Framework7/blob/master/LICENSE
  5258. */
  5259. var Popover = function(element, options) {
  5260. this.options = $.extend({}, Popover.DEFAULTS, options || {});
  5261. this.$element = $(element);
  5262. this.active = null;
  5263. this.$popover = this.options.target && $(this.options.target) || null;
  5264. this.init();
  5265. this.events();
  5266. };
  5267. Popover.DEFAULTS = {
  5268. trigger: "click",
  5269. content: "",
  5270. open: false,
  5271. target: undefined,
  5272. tpl: '<div class="am-popover"><div class="am-popover-inner"></div><div class="am-popover-caret"></div></div>'
  5273. };
  5274. Popover.prototype.init = function() {
  5275. var me = this, $element = this.$element, $popover;
  5276. if (!this.options.target) {
  5277. this.$popover = this.getPopover();
  5278. this.setContent();
  5279. }
  5280. $popover = this.$popover;
  5281. $popover.appendTo($("body"));
  5282. this.sizePopover();
  5283. function sizePopover() {
  5284. me.sizePopover();
  5285. }
  5286. $(window).on("resize:popover:amui", UI.utils.debounce(sizePopover, 50));
  5287. $element.on("open:popover:amui", function() {
  5288. $(window).on("resize:popover:amui", UI.utils.debounce(sizePopover, 50));
  5289. });
  5290. $element.on("close:popover:amui", function() {
  5291. $(window).off("resize:popover:amui", sizePopover);
  5292. });
  5293. this.options.open && this.open();
  5294. };
  5295. Popover.prototype.sizePopover = function sizePopover() {
  5296. var $element = this.$element, $popover = this.$popover;
  5297. if (!$popover || !$popover.length) return;
  5298. var popSize = $popover.getSize(), popWidth = $popover.width() || popSize.width, popHeight = $popover.height() || popSize.height, $popCaret = $popover.find(".am-popover-caret"), popCaretSize = $popCaret.width() / 2 || 10, popTotalHeight = popHeight + popCaretSize;
  5299. var triggerWidth = $element.outerWidth(), triggerHeight = $element.outerHeight(), triggerOffset = $element.offset(), triggerRect = $element[0].getBoundingClientRect();
  5300. var winHeight = $w.height(), winWidth = $w.width();
  5301. var popTop = 0, popLeft = 0, diff = 0, spacing = 3, popPosition = "top";
  5302. $popover.css({
  5303. left: "",
  5304. top: ""
  5305. }).removeClass("am-popover-left am-popover-right am-popover-top am-popover-bottom");
  5306. $popCaret.css({
  5307. left: "",
  5308. top: ""
  5309. });
  5310. if (popTotalHeight - spacing < triggerRect.top + spacing) {
  5311. // on Top
  5312. popTop = triggerOffset.top - popTotalHeight - spacing;
  5313. } else if (popTotalHeight < winHeight - triggerRect.top - triggerRect.height) {
  5314. // On bottom
  5315. popPosition = "bottom";
  5316. popTop = triggerOffset.top + triggerHeight + popCaretSize + spacing;
  5317. } else {
  5318. // On middle
  5319. popPosition = "middle";
  5320. popTop = triggerHeight / 2 + triggerOffset.top - popHeight / 2;
  5321. }
  5322. // Horizontal Position
  5323. if (popPosition === "top" || popPosition === "bottom") {
  5324. popLeft = triggerWidth / 2 + triggerOffset.left - popWidth / 2;
  5325. diff = popLeft;
  5326. if (popLeft < 5) popLeft = 5;
  5327. if (popLeft + popWidth > winWidth) {
  5328. popLeft = winWidth - popWidth - 20;
  5329. }
  5330. if (popPosition === "top") $popover.addClass("am-popover-bottom");
  5331. if (popPosition === "bottom") $popover.addClass("am-popover-top");
  5332. diff = diff - popLeft;
  5333. $popCaret.css({
  5334. left: popWidth / 2 - popCaretSize + diff + "px"
  5335. });
  5336. } else if (popPosition === "middle") {
  5337. popLeft = triggerOffset.left - popWidth - popCaretSize;
  5338. $popover.addClass("am-popover-left");
  5339. if (popLeft < 5) {
  5340. popLeft = triggerOffset.left + triggerWidth + popCaretSize;
  5341. $popover.removeClass("am-popover-left").addClass("am-popover-right");
  5342. }
  5343. if (popLeft + popWidth > winWidth) {
  5344. popLeft = winWidth - popWidth - 5;
  5345. $popover.removeClass("am-popover-left").addClass("am-popover-right");
  5346. }
  5347. $popCaret.css({
  5348. top: popHeight / 2 - popCaretSize / 2 + "px"
  5349. });
  5350. }
  5351. // Apply position style
  5352. $popover.css({
  5353. top: popTop + "px",
  5354. left: popLeft + "px"
  5355. });
  5356. };
  5357. Popover.prototype.toggle = function() {
  5358. return this[this.active ? "close" : "open"]();
  5359. };
  5360. Popover.prototype.open = function() {
  5361. var $popover = this.$popover;
  5362. this.$element.trigger("open:popover:amui");
  5363. this.sizePopover();
  5364. $popover.show().addClass("am-active");
  5365. this.active = true;
  5366. };
  5367. Popover.prototype.close = function() {
  5368. var $popover = this.$popover;
  5369. this.$element.trigger("close:popover:amui");
  5370. $popover.removeClass("am-active").trigger("closed:popover:amui").hide();
  5371. this.active = false;
  5372. };
  5373. Popover.prototype.getPopover = function() {
  5374. var uid = UI.utils.generateGUID("am-popover");
  5375. return $(this.options.tpl, {
  5376. id: uid
  5377. });
  5378. };
  5379. Popover.prototype.setContent = function() {
  5380. this.$popover && this.$popover.find(".am-popover-inner").empty().html(this.options.content);
  5381. };
  5382. Popover.prototype.events = function() {
  5383. var eventNS = "popover.amui", triggers = this.options.trigger.split(" ");
  5384. for (var i = triggers.length; i--; ) {
  5385. var trigger = triggers[i];
  5386. if (trigger === "click") {
  5387. this.$element.on("click." + eventNS, $.proxy(this.toggle, this));
  5388. } else {
  5389. // hover or focus
  5390. var eventIn = trigger == "hover" ? "mouseenter" : "focusin";
  5391. var eventOut = trigger == "hover" ? "mouseleave" : "focusout";
  5392. this.$element.on(eventIn + "." + eventNS, $.proxy(this.open, this));
  5393. this.$element.on(eventOut + "." + eventNS, $.proxy(this.close, this));
  5394. }
  5395. }
  5396. };
  5397. UI.popover = Popover;
  5398. function Plugin(option) {
  5399. return this.each(function() {
  5400. var $this = $(this), data = $this.data("am.popover"), options = $.extend({}, UI.utils.parseOptions($this.attr("data-am-popover")), typeof option == "object" && option);
  5401. if (!data) {
  5402. $this.data("am.popover", data = new Popover(this, options));
  5403. }
  5404. if (typeof option == "string") {
  5405. data[option]();
  5406. }
  5407. });
  5408. }
  5409. $.fn.popover = Plugin;
  5410. // Init code
  5411. $(function() {
  5412. $("[data-am-popover]").popover();
  5413. });
  5414. module.exports = Popover;
  5415. });
  5416. define("ui.progress", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  5417. require("core");
  5418. var $ = window.Zepto, UI = $.AMUI;
  5419. var Progress = function() {
  5420. /**
  5421. * NProgress (c) 2013, Rico Sta. Cruz
  5422. * @via http://ricostacruz.com/nprogress
  5423. */
  5424. var NProgress = {}, $html = $("html");
  5425. NProgress.version = "0.1.6";
  5426. var Settings = NProgress.settings = {
  5427. minimum: .08,
  5428. easing: "ease",
  5429. positionUsing: "",
  5430. speed: 200,
  5431. trickle: true,
  5432. trickleRate: .02,
  5433. trickleSpeed: 800,
  5434. showSpinner: true,
  5435. parent: "body",
  5436. barSelector: '[role="nprogress-bar"]',
  5437. spinnerSelector: '[role="nprogress-spinner"]',
  5438. template: '<div class="nprogress-bar" role="nprogress-bar">' + '<div class="nprogress-peg"></div></div>' + '<div class="nprogress-spinner" role="nprogress-spinner">' + '<div class="nprogress-spinner-icon"></div></div>'
  5439. };
  5440. /**
  5441. * Updates configuration.
  5442. *
  5443. * NProgress.configure({
  5444. * minimum: 0.1
  5445. * });
  5446. */
  5447. NProgress.configure = function(options) {
  5448. var key, value;
  5449. for (key in options) {
  5450. value = options[key];
  5451. if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
  5452. }
  5453. return this;
  5454. };
  5455. /**
  5456. * Last number.
  5457. */
  5458. NProgress.status = null;
  5459. /**
  5460. * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
  5461. *
  5462. * NProgress.set(0.4);
  5463. * NProgress.set(1.0);
  5464. */
  5465. NProgress.set = function(n) {
  5466. var started = NProgress.isStarted();
  5467. n = clamp(n, Settings.minimum, 1);
  5468. NProgress.status = n === 1 ? null : n;
  5469. var progress = NProgress.render(!started), bar = progress.querySelector(Settings.barSelector), speed = Settings.speed, ease = Settings.easing;
  5470. progress.offsetWidth;
  5471. /* Repaint */
  5472. queue(function(next) {
  5473. // Set positionUsing if it hasn't already been set
  5474. if (Settings.positionUsing === "") Settings.positionUsing = NProgress.getPositioningCSS();
  5475. // Add transition
  5476. css(bar, barPositionCSS(n, speed, ease));
  5477. if (n === 1) {
  5478. // Fade out
  5479. css(progress, {
  5480. transition: "none",
  5481. opacity: 1
  5482. });
  5483. progress.offsetWidth;
  5484. /* Repaint */
  5485. setTimeout(function() {
  5486. css(progress, {
  5487. transition: "all " + speed + "ms linear",
  5488. opacity: 0
  5489. });
  5490. setTimeout(function() {
  5491. NProgress.remove();
  5492. next();
  5493. }, speed);
  5494. }, speed);
  5495. } else {
  5496. setTimeout(next, speed);
  5497. }
  5498. });
  5499. return this;
  5500. };
  5501. NProgress.isStarted = function() {
  5502. return typeof NProgress.status === "number";
  5503. };
  5504. /**
  5505. * Shows the progress bar.
  5506. * This is the same as setting the status to 0%, except that it doesn't go backwards.
  5507. *
  5508. * NProgress.start();
  5509. *
  5510. */
  5511. NProgress.start = function() {
  5512. if (!NProgress.status) NProgress.set(0);
  5513. var work = function() {
  5514. setTimeout(function() {
  5515. if (!NProgress.status) return;
  5516. NProgress.trickle();
  5517. work();
  5518. }, Settings.trickleSpeed);
  5519. };
  5520. if (Settings.trickle) work();
  5521. return this;
  5522. };
  5523. /**
  5524. * Hides the progress bar.
  5525. * This is the *sort of* the same as setting the status to 100%, with the
  5526. * difference being `done()` makes some placebo effect of some realistic motion.
  5527. *
  5528. * NProgress.done();
  5529. *
  5530. * If `true` is passed, it will show the progress bar even if its hidden.
  5531. *
  5532. * NProgress.done(true);
  5533. */
  5534. NProgress.done = function(force) {
  5535. if (!force && !NProgress.status) return this;
  5536. return NProgress.inc(.3 + .5 * Math.random()).set(1);
  5537. };
  5538. /**
  5539. * Increments by a random amount.
  5540. */
  5541. NProgress.inc = function(amount) {
  5542. var n = NProgress.status;
  5543. if (!n) {
  5544. return NProgress.start();
  5545. } else {
  5546. if (typeof amount !== "number") {
  5547. amount = (1 - n) * clamp(Math.random() * n, .1, .95);
  5548. }
  5549. n = clamp(n + amount, 0, .994);
  5550. return NProgress.set(n);
  5551. }
  5552. };
  5553. NProgress.trickle = function() {
  5554. return NProgress.inc(Math.random() * Settings.trickleRate);
  5555. };
  5556. /**
  5557. * (Internal) renders the progress bar markup based on the `template`
  5558. * setting.
  5559. */
  5560. NProgress.render = function(fromStart) {
  5561. if (NProgress.isRendered()) return document.getElementById("nprogress");
  5562. $html.addClass("nprogress-busy");
  5563. var progress = document.createElement("div");
  5564. progress.id = "nprogress";
  5565. progress.innerHTML = Settings.template;
  5566. var bar = progress.querySelector(Settings.barSelector), perc = fromStart ? "-100" : toBarPerc(NProgress.status || 0), parent = document.querySelector(Settings.parent), spinner;
  5567. css(bar, {
  5568. transition: "all 0 linear",
  5569. transform: "translate3d(" + perc + "%,0,0)"
  5570. });
  5571. if (!Settings.showSpinner) {
  5572. spinner = progress.querySelector(Settings.spinnerSelector);
  5573. spinner && $(spinner).remove();
  5574. }
  5575. if (parent != document.body) {
  5576. $(parent).addClass("nprogress-custom-parent");
  5577. }
  5578. parent.appendChild(progress);
  5579. return progress;
  5580. };
  5581. /**
  5582. * Removes the element. Opposite of render().
  5583. */
  5584. NProgress.remove = function() {
  5585. $html.removeClass("nprogress-busy");
  5586. $(Settings.parent).removeClass("nprogress-custom-parent");
  5587. var progress = document.getElementById("nprogress");
  5588. progress && $(progress).remove();
  5589. };
  5590. /**
  5591. * Checks if the progress bar is rendered.
  5592. */
  5593. NProgress.isRendered = function() {
  5594. return !!document.getElementById("nprogress");
  5595. };
  5596. /**
  5597. * Determine which positioning CSS rule to use.
  5598. */
  5599. NProgress.getPositioningCSS = function() {
  5600. // Sniff on document.body.style
  5601. var bodyStyle = document.body.style;
  5602. // Sniff prefixes
  5603. var vendorPrefix = "WebkitTransform" in bodyStyle ? "Webkit" : "MozTransform" in bodyStyle ? "Moz" : "msTransform" in bodyStyle ? "ms" : "OTransform" in bodyStyle ? "O" : "";
  5604. if (vendorPrefix + "Perspective" in bodyStyle) {
  5605. // Modern browsers with 3D support, e.g. Webkit, IE10
  5606. return "translate3d";
  5607. } else if (vendorPrefix + "Transform" in bodyStyle) {
  5608. // Browsers without 3D support, e.g. IE9
  5609. return "translate";
  5610. } else {
  5611. // Browsers without translate() support, e.g. IE7-8
  5612. return "margin";
  5613. }
  5614. };
  5615. /**
  5616. * Helpers
  5617. */
  5618. function clamp(n, min, max) {
  5619. if (n < min) return min;
  5620. if (n > max) return max;
  5621. return n;
  5622. }
  5623. /**
  5624. * (Internal) converts a percentage (`0..1`) to a bar translateX
  5625. * percentage (`-100%..0%`).
  5626. */
  5627. function toBarPerc(n) {
  5628. return (-1 + n) * 100;
  5629. }
  5630. /**
  5631. * (Internal) returns the correct CSS for changing the bar's
  5632. * position given an n percentage, and speed and ease from Settings
  5633. */
  5634. function barPositionCSS(n, speed, ease) {
  5635. var barCSS;
  5636. if (Settings.positionUsing === "translate3d") {
  5637. barCSS = {
  5638. transform: "translate3d(" + toBarPerc(n) + "%,0,0)"
  5639. };
  5640. } else if (Settings.positionUsing === "translate") {
  5641. barCSS = {
  5642. transform: "translate(" + toBarPerc(n) + "%,0)"
  5643. };
  5644. } else {
  5645. barCSS = {
  5646. "margin-left": toBarPerc(n) + "%"
  5647. };
  5648. }
  5649. barCSS.transition = "all " + speed + "ms " + ease;
  5650. return barCSS;
  5651. }
  5652. /**
  5653. * (Internal) Queues a function to be executed.
  5654. */
  5655. var queue = function() {
  5656. var pending = [];
  5657. function next() {
  5658. var fn = pending.shift();
  5659. if (fn) {
  5660. fn(next);
  5661. }
  5662. }
  5663. return function(fn) {
  5664. pending.push(fn);
  5665. if (pending.length == 1) next();
  5666. };
  5667. }();
  5668. /**
  5669. * (Internal) Applies css properties to an element, similar to the jQuery
  5670. * css method.
  5671. *
  5672. * While this helper does assist with vendor prefixed property names, it
  5673. * does not perform any manipulation of values prior to setting styles.
  5674. */
  5675. var css = function() {
  5676. var cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], cssProps = {};
  5677. function camelCase(string) {
  5678. return string.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function(match, letter) {
  5679. return letter.toUpperCase();
  5680. });
  5681. }
  5682. function getVendorProp(name) {
  5683. var style = document.body.style;
  5684. if (name in style) return name;
  5685. var i = cssPrefixes.length, capName = name.charAt(0).toUpperCase() + name.slice(1), vendorName;
  5686. while (i--) {
  5687. vendorName = cssPrefixes[i] + capName;
  5688. if (vendorName in style) return vendorName;
  5689. }
  5690. return name;
  5691. }
  5692. function getStyleProp(name) {
  5693. name = camelCase(name);
  5694. return cssProps[name] || (cssProps[name] = getVendorProp(name));
  5695. }
  5696. function applyCss(element, prop, value) {
  5697. prop = getStyleProp(prop);
  5698. element.style[prop] = value;
  5699. }
  5700. return function(element, properties) {
  5701. var args = arguments, prop, value;
  5702. if (args.length == 2) {
  5703. for (prop in properties) {
  5704. value = properties[prop];
  5705. if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
  5706. }
  5707. } else {
  5708. applyCss(element, args[1], args[2]);
  5709. }
  5710. };
  5711. }();
  5712. return NProgress;
  5713. }();
  5714. UI.progress = Progress;
  5715. module.exports = Progress;
  5716. });
  5717. define("ui.pureview", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "zepto.pinchzoom", "util.hammer" ], function(require, exports, module) {
  5718. require("core");
  5719. var PinchZoom = require("zepto.pinchzoom"), Hammer = require("util.hammer"), $ = window.Zepto, UI = $.AMUI, animation = UI.support.animation, transition = UI.support.transition, $html = $("html");
  5720. /**
  5721. * PureView
  5722. * @desc Image browser for Mobile
  5723. * @param element
  5724. * @param options
  5725. * @constructor
  5726. */
  5727. var PureView = function(element, options) {
  5728. this.$element = $(element);
  5729. this.$body = $(document.body);
  5730. this.options = $.extend({}, PureView.DEFAULTS, options);
  5731. this.$pureview = $(this.options.tpl, {
  5732. id: UI.utils.generateGUID("am-pureview")
  5733. });
  5734. this.$slides = null;
  5735. this.transitioning = null;
  5736. this.scrollbarWidth = 0;
  5737. this.init();
  5738. };
  5739. PureView.DEFAULTS = {
  5740. tpl: '<div class="am-pureview am-pureview-bar-active">' + '<ul class="am-pureview-slider"></ul>' + '<ul class="am-pureview-direction"><li class="am-pureview-prev"><a href=""></a></li><li class="am-pureview-next"><a href=""></a></li></ul>' + '<ol class="am-pureview-nav"></ol>' + '<div class="am-pureview-bar am-active"><span class="am-pureview-title"></span><span class="am-pureview-current"></span> / <span class="am-pureview-total"></span></div>' + '<div class="am-pureview-actions am-active"><a href="javascript: void(0)" class="am-icon-chevron-left" data-am-close="pureview"></a></div>' + "</div>",
  5741. className: {
  5742. prevSlide: "am-pureview-slide-prev",
  5743. nextSlide: "am-pureview-slide-next",
  5744. onlyOne: "am-pureview-only",
  5745. active: "am-active",
  5746. barActive: "am-pureview-bar-active",
  5747. activeBody: "am-pureview-active"
  5748. },
  5749. selector: {
  5750. slider: ".am-pureview-slider",
  5751. close: '[data-am-close="pureview"]',
  5752. total: ".am-pureview-total",
  5753. current: ".am-pureview-current",
  5754. title: ".am-pureview-title",
  5755. actions: ".am-pureview-actions",
  5756. bar: ".am-pureview-bar",
  5757. pinchZoom: ".am-pinch-zoom",
  5758. nav: ".am-pureview-nav"
  5759. },
  5760. shareBtn: false,
  5761. // 从何处获取图片,img 可以使用 data-rel 指定大图
  5762. target: "img",
  5763. // 微信 Webview 中调用微信的图片浏览器
  5764. // 实现图片保存、分享好友、收藏图片等功能
  5765. weChatImagePreview: true
  5766. };
  5767. PureView.prototype.init = function() {
  5768. var me = this, options = this.options, $element = this.$element, $pureview = this.$pureview, $slider = $pureview.find(options.selector.slider), $nav = $pureview.find(options.selector.nav), $slides = $([]), $navItems = $([]), $images = $element.find(options.target), total = $images.length, imgUrls = [];
  5769. if (!total) return;
  5770. if (total === 1) {
  5771. $pureview.addClass(options.className.onlyOne);
  5772. }
  5773. $images.each(function(i, item) {
  5774. var src, title;
  5775. if (options.target == "a") {
  5776. src = item.href;
  5777. // to absolute path
  5778. title = item.title || "";
  5779. } else {
  5780. src = $(item).data("rel") || item.src;
  5781. // <img src='' data-rel='' />
  5782. title = $(item).attr("alt") || "";
  5783. }
  5784. // hide bar: wechat_webview_type=1
  5785. // http://tmt.io/wechat/ not working?
  5786. imgUrls.push(src);
  5787. $slides = $slides.add($('<li><div class="am-pinch-zoom"><img src="' + src + '" alt="' + title + '"/></div></li>'));
  5788. $navItems = $navItems.add($("<li>" + (i + 1) + "</li>"));
  5789. });
  5790. $slider.append($slides);
  5791. $nav.append($navItems);
  5792. $("body").append($pureview);
  5793. $pureview.find(options.selector.total).text(total);
  5794. this.$title = $pureview.find(options.selector.title);
  5795. this.$current = $pureview.find(options.selector.current);
  5796. this.$bar = $pureview.find(options.selector.bar);
  5797. this.$actions = $pureview.find(options.selector.actions);
  5798. this.$navItems = $nav.find("li");
  5799. this.$slides = $slider.find("li");
  5800. if (options.shareBtn) {
  5801. this.$actions.append('<a href="javascript: void(0)" class="am-icon-share-square-o" data-am-toggle="share"></a>');
  5802. }
  5803. $slider.find(options.selector.pinchZoom).each(function() {
  5804. $(this).data("amui.pinchzoom", new PinchZoom($(this), {}));
  5805. $(this).on("pz_doubletap", function(e) {});
  5806. });
  5807. $images.on("click.pureview.amui", function(e) {
  5808. e.preventDefault();
  5809. var clicked = $images.index(this);
  5810. // Invoke WeChat ImagePreview in WeChat
  5811. // TODO: detect WeChat before init
  5812. if (options.weChatImagePreview && window.WeixinJSBridge) {
  5813. WeixinJSBridge.invoke("imagePreview", {
  5814. current: imgUrls[clicked],
  5815. urls: imgUrls
  5816. });
  5817. } else {
  5818. me.open(clicked);
  5819. }
  5820. });
  5821. $pureview.find(".am-pureview-direction a").on("click.direction.pureview.amui", function(e) {
  5822. e.preventDefault();
  5823. var $clicked = $(e.target).parent("li");
  5824. if ($clicked.is(".am-pureview-prev")) {
  5825. me.prevSlide();
  5826. } else {
  5827. me.nextSlide();
  5828. }
  5829. });
  5830. // Nav Contorl
  5831. this.$navItems.on("click.nav.pureview.amui", function() {
  5832. var index = me.$navItems.index($(this));
  5833. me.activate(me.$slides.eq(index));
  5834. });
  5835. // Close Icon
  5836. $pureview.find(options.selector.close).on("click.close.pureview.amui", function(e) {
  5837. e.preventDefault();
  5838. me.close();
  5839. });
  5840. $slider.hammer().on("press.pureview.amui", function(e) {
  5841. e.preventDefault();
  5842. me.toggleToolBar();
  5843. }).on("swipeleft.pureview.amui", function(e) {
  5844. e.preventDefault();
  5845. me.nextSlide();
  5846. }).on("swiperight.pureview.amui", function(e) {
  5847. e.preventDefault();
  5848. me.prevSlide();
  5849. });
  5850. $slider.data("hammer").get("swipe").set({
  5851. direction: Hammer.DIRECTION_HORIZONTAL,
  5852. velocity: .35
  5853. });
  5854. $(document).on("keydown.pureview.amui", $.proxy(function(e) {
  5855. var keyCode = e.keyCode;
  5856. if (keyCode == 37) {
  5857. this.prevSlide();
  5858. } else if (keyCode == 39) {
  5859. this.nextSlide();
  5860. } else if (keyCode == 27) {
  5861. this.close();
  5862. }
  5863. }, this));
  5864. };
  5865. PureView.prototype.activate = function($slide) {
  5866. var options = this.options, $slides = this.$slides, activeIndex = $slides.index($slide), alt = $slide.find("img").attr("alt") || "", active = options.className.active;
  5867. UI.utils.imageLoader($slide.find("img"), function(image) {
  5868. $(image).addClass("am-img-loaded");
  5869. });
  5870. if ($slides.find("." + active).is($slide)) return;
  5871. if (this.transitioning) return;
  5872. this.transitioning = 1;
  5873. this.$title.text(alt);
  5874. this.$current.text(activeIndex + 1);
  5875. $slides.removeClass();
  5876. $slide.addClass(active);
  5877. $slides.eq(activeIndex - 1).addClass(options.className.prevSlide);
  5878. $slides.eq(activeIndex + 1).addClass(options.className.nextSlide);
  5879. this.$navItems.removeClass().eq(activeIndex).addClass(options.className.active);
  5880. if (transition) {
  5881. $slide.one(transition.end, $.proxy(function() {
  5882. this.transitioning = 0;
  5883. }, this));
  5884. } else {
  5885. this.transitioning = 0;
  5886. }
  5887. };
  5888. PureView.prototype.nextSlide = function() {
  5889. if (this.$slides.length === 1) return;
  5890. var $slides = this.$slides, $active = $slides.filter(".am-active"), activeIndex = $slides.index($active), rightSpring = "am-animation-right-spring";
  5891. if (activeIndex + 1 >= $slides.length) {
  5892. // last one
  5893. animation && $active.addClass(rightSpring).on(animation.end, function() {
  5894. $active.removeClass(rightSpring);
  5895. });
  5896. } else {
  5897. this.activate($slides.eq(activeIndex + 1));
  5898. }
  5899. };
  5900. PureView.prototype.prevSlide = function() {
  5901. if (this.$slides.length === 1) return;
  5902. var $slides = this.$slides, $active = $slides.filter(".am-active"), activeIndex = this.$slides.index($active), leftSpring = "am-animation-left-spring";
  5903. if (activeIndex === 0) {
  5904. // first one
  5905. animation && $active.addClass(leftSpring).on(animation.end, function() {
  5906. $active.removeClass(leftSpring);
  5907. });
  5908. } else {
  5909. this.activate($slides.eq(activeIndex - 1));
  5910. }
  5911. };
  5912. PureView.prototype.toggleToolBar = function() {
  5913. this.$pureview.toggleClass(this.options.className.barActive);
  5914. };
  5915. PureView.prototype.open = function(index) {
  5916. var active = index || 0;
  5917. this.checkScrollbar();
  5918. this.setScrollbar();
  5919. this.activate(this.$slides.eq(active));
  5920. this.$pureview.addClass(this.options.className.active);
  5921. this.$body.addClass(this.options.className.activeBody);
  5922. };
  5923. PureView.prototype.close = function() {
  5924. var options = this.options;
  5925. this.$pureview.removeClass(options.className.active);
  5926. this.$slides.removeClass();
  5927. function resetBody() {
  5928. this.$body.removeClass(options.className.activeBody);
  5929. this.resetScrollbar();
  5930. }
  5931. if (transition) {
  5932. this.$pureview.one(transition.end, $.proxy(resetBody, this));
  5933. } else {
  5934. resetBody.call(this);
  5935. }
  5936. };
  5937. PureView.prototype.checkScrollbar = function() {
  5938. this.scrollbarWidth = UI.utils.measureScrollbar();
  5939. };
  5940. PureView.prototype.setScrollbar = function() {
  5941. var bodyPaddingRight = parseInt(this.$body.css("padding-right") || 0, 10);
  5942. if (this.scrollbarWidth) this.$body.css("padding-right", bodyPaddingRight + this.scrollbarWidth);
  5943. };
  5944. PureView.prototype.resetScrollbar = function() {
  5945. this.$body.css("padding-right", "");
  5946. };
  5947. UI.pureview = PureView;
  5948. function Plugin(option) {
  5949. return this.each(function() {
  5950. var $this = $(this), data = $this.data("am.pureview"), options = $.extend({}, UI.utils.parseOptions($this.data("amPureview")), typeof option == "object" && option);
  5951. if (!data) {
  5952. $this.data("am.pureview", data = new PureView(this, options));
  5953. }
  5954. if (typeof option == "string") {
  5955. data[option]();
  5956. }
  5957. });
  5958. }
  5959. $.fn.pureview = Plugin;
  5960. // Init code
  5961. $(function() {
  5962. $("[data-am-pureview]").pureview();
  5963. });
  5964. module.exports = PureView;
  5965. });
  5966. define("ui.scrollspy", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  5967. "use strict";
  5968. require("core");
  5969. var $ = window.Zepto, UI = $.AMUI;
  5970. /**
  5971. * @via https://github.com/uikit/uikit/blob/master/src/js/scrollspy.js
  5972. * @license https://github.com/uikit/uikit/blob/master/LICENSE.md
  5973. */
  5974. var ScrollSpy = function(element, options) {
  5975. if (!UI.support.animation) return;
  5976. this.options = $.extend({}, ScrollSpy.DEFAULTS, options);
  5977. this.$element = $(element);
  5978. var checkViewRAF = function() {
  5979. UI.utils.rAF.call(window, $.proxy(this.checkView, this));
  5980. }.bind(this);
  5981. this.$window = $(window).on("scroll.scrollspy.amui", checkViewRAF).on("resize.scrollspy.amui orientationchange.scrollspy.amui", UI.utils.debounce(checkViewRAF, 50));
  5982. this.timer = this.inViewState = this.initInView = null;
  5983. checkViewRAF();
  5984. };
  5985. ScrollSpy.DEFAULTS = {
  5986. animation: "fade",
  5987. className: {
  5988. inView: "am-scrollspy-inview",
  5989. init: "am-scrollspy-init"
  5990. },
  5991. repeat: true,
  5992. delay: 0,
  5993. topOffset: 0,
  5994. leftOffset: 0
  5995. };
  5996. ScrollSpy.prototype.checkView = function() {
  5997. var $element = this.$element, options = this.options, inView = UI.utils.isInView($element, options), animation = options.animation ? " am-animation-" + options.animation : "";
  5998. if (inView && !this.inViewState) {
  5999. if (this.timer) clearTimeout(this.timer);
  6000. if (!this.initInView) {
  6001. $element.addClass(options.className.init);
  6002. this.offset = $element.offset();
  6003. this.initInView = true;
  6004. $element.trigger("init:scrollspy:amui");
  6005. }
  6006. this.timer = setTimeout(function() {
  6007. if (inView) {
  6008. $element.addClass(options.className.inView + animation).width();
  6009. }
  6010. }, options.delay);
  6011. this.inViewState = true;
  6012. $element.trigger("inview:scrollspy:amui");
  6013. }
  6014. if (!inView && this.inViewState && options.repeat) {
  6015. $element.removeClass(options.className.inView + animation);
  6016. this.inViewState = false;
  6017. $element.trigger("outview:scrollspy:amui");
  6018. }
  6019. };
  6020. ScrollSpy.prototype.check = function() {
  6021. UI.utils.rAF.call(window, $.proxy(this.checkView, this));
  6022. };
  6023. UI.scrollspy = ScrollSpy;
  6024. // Sticky Plugin
  6025. function Plugin(option) {
  6026. return this.each(function() {
  6027. var $this = $(this), data = $this.data("am.scrollspy"), options = typeof option == "object" && option;
  6028. if (!data) $this.data("am.scrollspy", data = new ScrollSpy(this, options));
  6029. if (typeof option == "string") data[option]();
  6030. });
  6031. }
  6032. $.fn.scrollspy = Plugin;
  6033. // Init code
  6034. $(function() {
  6035. $("[data-am-scrollspy]").each(function() {
  6036. var $this = $(this), options = UI.utils.options($this.attr("data-am-scrollspy"));
  6037. $this.scrollspy(options);
  6038. });
  6039. });
  6040. module.exports = ScrollSpy;
  6041. });
  6042. define("ui.scrollspynav", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.smooth-scroll" ], function(require, exports, module) {
  6043. "use strict";
  6044. require("core");
  6045. require("ui.smooth-scroll");
  6046. var $ = window.Zepto, UI = $.AMUI;
  6047. /**
  6048. * @via https://github.com/uikit/uikit/
  6049. * @license https://github.com/uikit/uikit/blob/master/LICENSE.md
  6050. */
  6051. // ScrollSpyNav Class
  6052. var ScrollSpyNav = function(element, options) {
  6053. this.options = $.extend({}, ScrollSpyNav.DEFAULTS, options);
  6054. this.$element = $(element);
  6055. this.anchors = [];
  6056. this.$links = this.$element.find('a[href^="#"]').each(function(i, link) {
  6057. this.anchors.push($(link).attr("href"));
  6058. }.bind(this));
  6059. this.$targets = $(this.anchors.join(", "));
  6060. var processRAF = function() {
  6061. UI.utils.rAF.call(window, $.proxy(this.process, this));
  6062. }.bind(this);
  6063. this.$window = $(window).on("scroll.scrollspynav.amui", processRAF).on("resize.scrollspynav.amui orientationchange.scrollspynav.amui", UI.utils.debounce(processRAF, 50));
  6064. processRAF();
  6065. this.scrollProcess();
  6066. };
  6067. ScrollSpyNav.DEFAULTS = {
  6068. className: {
  6069. active: "am-active"
  6070. },
  6071. closest: false,
  6072. smooth: true
  6073. };
  6074. ScrollSpyNav.prototype.process = function() {
  6075. var scrollTop = this.$window.scrollTop(), options = this.options, inViews = [], $links = this.$links;
  6076. var $targets = this.$targets;
  6077. $targets.each(function(i, target) {
  6078. if (UI.utils.isInView(target, options)) {
  6079. inViews.push(target);
  6080. }
  6081. });
  6082. //console.log(inViews.length);
  6083. if (inViews.length) {
  6084. var $target;
  6085. $.each(inViews, function(i, item) {
  6086. if ($(item).offset().top >= scrollTop) {
  6087. // console.log($(item));
  6088. $target = $(item);
  6089. return false;
  6090. }
  6091. });
  6092. if (!$target) return;
  6093. if (options.closest) {
  6094. $links.closest(options.closest).removeClass(options.className.active);
  6095. $links.filter('a[href="#' + $target.attr("id") + '"]').closest(options.closest).addClass(options.className.active);
  6096. } else {
  6097. $links.removeClass(options.className.active).filter('a[href="#' + $target.attr("id") + '"]').addClass(options.className.active);
  6098. }
  6099. }
  6100. };
  6101. ScrollSpyNav.prototype.scrollProcess = function() {
  6102. var $links = this.$links;
  6103. // smoothScroll
  6104. if (this.options.smooth) {
  6105. $links.on("click", function(e) {
  6106. e.preventDefault();
  6107. var $this = $(this), $target = $($this.attr("href"));
  6108. if (!$target) return;
  6109. $(window).smoothScroll({
  6110. position: $target.offset().top
  6111. });
  6112. });
  6113. }
  6114. };
  6115. UI.scrollspynav = ScrollSpyNav;
  6116. // ScrollSpyNav Plugin
  6117. function Plugin(option) {
  6118. return this.each(function() {
  6119. var $this = $(this), data = $this.data("am.scrollspynav"), options = typeof option == "object" && option;
  6120. if (!data) $this.data("am.scrollspynav", data = new ScrollSpyNav(this, options));
  6121. if (typeof option == "string") data[option]();
  6122. });
  6123. }
  6124. $.fn.scrollspynav = Plugin;
  6125. // Init code
  6126. $(function() {
  6127. $("[data-am-scrollspy-nav]").each(function() {
  6128. var $this = $(this), options = UI.utils.options($this.attr("data-am-scrollspy-nav"));
  6129. Plugin.call($this, options);
  6130. });
  6131. });
  6132. module.exports = ScrollSpyNav;
  6133. });
  6134. define("ui.share", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.modal", "ui.dimmer", "util.qrcode" ], function(require, exports, module) {
  6135. require("core");
  6136. require("ui.modal");
  6137. var QRCode = require("util.qrcode");
  6138. var $ = window.Zepto, UI = $.AMUI, doc = document, $doc = $(doc), transition = UI.support.transition;
  6139. /**
  6140. * https://github.com/cho45/micro-template.js
  6141. * (c) cho45 http://cho45.github.com/mit-license
  6142. */
  6143. function template(id, data) {
  6144. var me = arguments.callee;
  6145. if (!me.cache[id]) me.cache[id] = function() {
  6146. var name = id, string = /^[\w\-]+$/.test(id) ? me.get(id) : (name = "template(string)",
  6147. id);
  6148. // no warnings
  6149. var line = 1, body = ("try { " + (me.variable ? "var " + me.variable + " = this.stash;" : "with (this.stash) { ") + "this.ret += '" + string.replace(/<%/g, "").replace(/%>/g, "").replace(/'(?![^\x11\x13]+?\x13)/g, "\\x27").replace(/^\s*|\s*$/g, "").replace(/\n/g, function() {
  6150. return "';\nthis.line = " + ++line + "; this.ret += '\\n";
  6151. }).replace(/\x11=raw(.+?)\x13/g, "' + ($1) + '").replace(/\x11=(.+?)\x13/g, "' + this.escapeHTML($1) + '").replace(/\x11(.+?)\x13/g, "'; $1; this.ret += '") + "'; " + (me.variable ? "" : "}") + "return this.ret;" + "} catch (e) { throw 'TemplateError: ' + e + ' (on " + name + "' + ' line ' + this.line + ')'; } " + "//@ sourceURL=" + name + "\n").replace(/this\.ret \+= '';/g, "");
  6152. var func = new Function(body);
  6153. var map = {
  6154. "&": "&amp;",
  6155. "<": "&lt;",
  6156. ">": "&gt;",
  6157. '"': "&#x22;",
  6158. "'": "&#x27;"
  6159. };
  6160. var escapeHTML = function(string) {
  6161. return ("" + string).replace(/[&<>\'\"]/g, function(_) {
  6162. return map[_];
  6163. });
  6164. };
  6165. return function(stash) {
  6166. return func.call(me.context = {
  6167. escapeHTML: escapeHTML,
  6168. line: 1,
  6169. ret: "",
  6170. stash: stash
  6171. });
  6172. };
  6173. }();
  6174. return data ? me.cache[id](data) : me.cache[id];
  6175. }
  6176. template.cache = {};
  6177. template.get = function(id) {
  6178. return Share.DEFAULTS.tpl;
  6179. };
  6180. var Share = function(options) {
  6181. this.options = $.extend({}, Share.DEFAULTS, options || {});
  6182. this.$element = null;
  6183. this.$wechatQr = null;
  6184. this.pics = null;
  6185. this.inited = false;
  6186. this.active = false;
  6187. };
  6188. Share.DEFAULTS = {
  6189. sns: [ "weibo", "qq", "qzone", "tqq", "wechat", "renren" ],
  6190. title: "分享到",
  6191. cancel: "取消",
  6192. closeOnShare: true,
  6193. id: UI.utils.generateGUID("am-share"),
  6194. desc: "Hi,孤夜观天象,发现一个不错的西西,分享一下下 ;-)",
  6195. via: "Amaze UI",
  6196. tpl: '<div class="am-share am-modal-actions" id="<%= id %>">' + '<h3 class="am-share-title"><%= title %></h3>' + '<ul class="am-share-sns sm-block-grid-3"><% for(var i = 0; i < sns.length; i++) {%>' + '<li><a href="<%= sns[i].shareUrl %>" data-am-share-to="<%= sns[i].id %>" ><i class="am-icon-<%= sns[i].icon %>"></i><span><%= sns[i].title %></span></a></li>' + "<% } %></ul>" + '<div class="am-share-footer"><button class="am-btn am-btn-default am-btn-block" data-am-share-close><%= cancel %></button></div>' + "</div>"
  6197. };
  6198. Share.SNS = {
  6199. weibo: {
  6200. title: "新浪微博",
  6201. url: "http://service.weibo.com/share/share.php",
  6202. width: 620,
  6203. height: 450,
  6204. icon: "weibo"
  6205. },
  6206. // url 链接地址
  6207. // title:”, 分享的文字内容(可选,默认为所在页面的title)
  6208. // appkey:”, 您申请的应用appkey,显示分享来源(可选)
  6209. // pic:”, 分享图片的路径(可选)
  6210. // ralateUid:”, 关联用户的UID,分享微博会@该用户(可选)
  6211. // NOTE: 会自动抓取图片,不用指定 pic
  6212. qq: {
  6213. title: "QQ 好友",
  6214. url: "http://connect.qq.com/widget/shareqq/index.html",
  6215. icon: "qq"
  6216. },
  6217. // url:,
  6218. // title:'', 分享标题(可选)
  6219. // pics:'', 分享图片的路径(可选)
  6220. // summary:'', 分享摘要(可选)
  6221. // site:'', 分享来源 如:腾讯网(可选)
  6222. // desc: '' 发送给用户的消息
  6223. // NOTE: 经过测试,最终发给用户的只有 url 和 desc
  6224. qzone: {
  6225. title: "QQ 空间",
  6226. url: "http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey",
  6227. icon: "star"
  6228. },
  6229. // http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=xxx&title=xxx&desc=&summary=&site=
  6230. // url:,
  6231. // title:'', 分享标题(可选)
  6232. // desc:'', 默认分享理由(可选)
  6233. // summary:'', 分享摘要(可选)
  6234. // site:'', 分享来源 如:腾讯网(可选)
  6235. // pics:'', 分享图片的路径(可选),不会自动抓取,多个图片用|分隔
  6236. tqq: {
  6237. title: "腾讯微博",
  6238. url: "http://v.t.qq.com/share/share.php",
  6239. icon: "tencent-weibo"
  6240. },
  6241. // url=xx&title=&appkey=801cf76d3cfc44ada52ec13114e84a96
  6242. // url
  6243. // title
  6244. // pic 多个图片用 | 分隔
  6245. // appkey
  6246. // NOTE: 不会自动抓取图片
  6247. wechat: {
  6248. title: "微信",
  6249. url: "[qrcode]",
  6250. icon: "wechat"
  6251. },
  6252. // 生成一个二维码 供用户扫描
  6253. // 相关接口 https://github.com/zxlie/WeixinApi
  6254. renren: {
  6255. title: "人人网",
  6256. url: "http://widget.renren.com/dialog/share",
  6257. icon: "renren"
  6258. },
  6259. // http://widget.renren.com/dialog/share?resourceUrl=www&srcUrl=www&title=ww&description=xxx
  6260. // 550 * 400
  6261. // resourceUrl : '', // 分享的资源Url
  6262. // srcUrl : '', // 分享的资源来源Url,默认为header中的Referer,如果分享失败可以调整此值为resourceUrl试试
  6263. // pic : '', // 分享的主题图片,会自动抓取
  6264. // title : '', // 分享的标题
  6265. // description : '' // 分享的详细描述
  6266. // NOTE: 经过测试,直接使用 url 参数即可
  6267. douban: {
  6268. title: "豆瓣",
  6269. url: "http://www.douban.com/recommend/",
  6270. icon: "share-alt"
  6271. },
  6272. // http://www.douban.com/service/sharebutton
  6273. // 450 * 330
  6274. // http://www.douban.com/share/service?bm=1&image=&href=xxx&updated=&name=
  6275. // href 链接
  6276. // name 标题
  6277. /* void (function() {
  6278. var d = document, e = encodeURIComponent, s1 = window.getSelection, s2 = d.getSelection, s3 = d.selection, s = s1 ? s1()
  6279. : s2 ? s2() : s3 ? s3.createRange().text : '', r = 'http://www.douban.com/recommend/?url=&title=&sel=&v=1&r=1'
  6280. })();
  6281. */
  6282. // tsohu: '',
  6283. // http://t.sohu.com/third/post.jsp?url=&title=&content=utf-8&pic=
  6284. //print: '',
  6285. mail: {
  6286. title: "邮件分享",
  6287. url: "mailto:",
  6288. icon: "envelope-o"
  6289. },
  6290. sms: {
  6291. title: "短信分享",
  6292. url: "sms:",
  6293. icon: "comment"
  6294. }
  6295. };
  6296. Share.prototype.render = function() {
  6297. var options = this.options, snsData = [], title = encodeURIComponent(doc.title), link = encodeURIComponent(doc.location), msgBody = "?body=" + title + link;
  6298. options.sns.forEach(function(item, i) {
  6299. if (Share.SNS[item]) {
  6300. var tmp = Share.SNS[item], shareUrl;
  6301. tmp.id = item;
  6302. if (item === "mail") {
  6303. shareUrl = msgBody + "&subject=" + options.desc;
  6304. } else if (item === "sms") {
  6305. shareUrl = msgBody;
  6306. } else {
  6307. shareUrl = "?url=" + link + "&title=" + title;
  6308. }
  6309. tmp.shareUrl = tmp.url + shareUrl;
  6310. snsData.push(tmp);
  6311. }
  6312. });
  6313. return template("share", $.extend({}, options, {
  6314. sns: snsData
  6315. }));
  6316. };
  6317. Share.prototype.init = function() {
  6318. if (this.inited) return;
  6319. var me = this, shareItem = "[data-am-share-to]";
  6320. $doc.ready($.proxy(function() {
  6321. $("body").append(this.render());
  6322. // append share DOM to body
  6323. this.$element = $("#" + this.options.id);
  6324. this.$element.find("[data-am-share-close]").on("click.share.amui", function() {
  6325. me.close();
  6326. });
  6327. }, this));
  6328. $doc.on("click.share.amui", shareItem, $.proxy(function(e) {
  6329. var $clicked = $(e.target), $target = $clicked.is(shareItem) && $clicked || $clicked.parent(shareItem), sns = $target.attr("data-am-share-to");
  6330. if (!(sns === "mail" || sns === "sms")) {
  6331. e.preventDefault();
  6332. this.shareTo(sns, this.setData(sns));
  6333. }
  6334. this.close();
  6335. }, this));
  6336. this.inited = true;
  6337. };
  6338. Share.prototype.open = function() {
  6339. !this.inited && this.init();
  6340. this.$element && this.$element.modal("open");
  6341. this.$element.trigger("open:share:amui");
  6342. this.active = true;
  6343. };
  6344. Share.prototype.close = function() {
  6345. this.$element && this.$element.modal("close");
  6346. this.$element.trigger("close:share:amui");
  6347. this.active = false;
  6348. };
  6349. Share.prototype.toggle = function() {
  6350. this.active ? this.close() : this.open();
  6351. };
  6352. Share.prototype.setData = function(sns) {
  6353. if (!sns) return;
  6354. var shareData = {
  6355. url: doc.location,
  6356. title: doc.title
  6357. }, desc = this.options.desc, imgSrc = this.pics || [], qqReg = /^(qzone|qq|tqq)$/;
  6358. if (qqReg.test(sns) && !imgSrc.length) {
  6359. var allImages = doc.images;
  6360. for (var i = 0; i < allImages.length && i < 10; i++) {
  6361. !!allImages[i].src && imgSrc.push(encodeURIComponent(allImages[i].src));
  6362. }
  6363. this.pics = imgSrc;
  6364. }
  6365. switch (sns) {
  6366. case "qzone":
  6367. shareData.desc = desc;
  6368. shareData.site = this.options.via;
  6369. shareData.pics = imgSrc.join("|");
  6370. // TODO: 抓取图片多张
  6371. break;
  6372. case "qq":
  6373. shareData.desc = desc;
  6374. shareData.site = this.options.via;
  6375. shareData.pics = imgSrc[0];
  6376. // 抓取一张图片
  6377. break;
  6378. case "tqq":
  6379. // 抓取图片多张
  6380. shareData.pic = imgSrc.join("|");
  6381. break;
  6382. }
  6383. return shareData;
  6384. };
  6385. Share.prototype.shareTo = function(sns, data) {
  6386. var snsInfo = Share.SNS[sns];
  6387. if (!snsInfo) return;
  6388. if (sns === "wechat" || sns === "weixin") return this.wechatQr();
  6389. var query = [];
  6390. for (var key in data) {
  6391. if (data[key]) {
  6392. // 避免 encode 图片分隔符 |
  6393. query.push(key.toString() + "=" + (key === "pic" || key === "pics" ? data[key] : encodeURIComponent(data[key])));
  6394. }
  6395. }
  6396. window.open(snsInfo.url + "?" + query.join("&"));
  6397. };
  6398. Share.prototype.wechatQr = function() {
  6399. if (!this.$wechatQr) {
  6400. var qrId = UI.utils.generateGUID("am-share-wechat"), $qr = $('<div class="am-modal am-modal-no-btn am-share-wechat-qr"><div class="am-modal-dialog"><div class="am-modal-hd">分享到微信 <a href="" class="am-close am-close-spin" data-am-modal-close>&times;</a> </div><div class="am-modal-bd"><div class="am-share-wx-qr"></div><div class="am-share-wechat-tip">打开微信,点击底部的<em>发现</em>,<br/> 使用<em>扫一扫</em>将网页分享至朋友圈</div></div></div></div>', {
  6401. id: qrId
  6402. });
  6403. var qrNode = new QRCode({
  6404. render: "canvas",
  6405. correctLevel: 0,
  6406. text: doc.location.href,
  6407. width: 180,
  6408. height: 180,
  6409. background: "#fff",
  6410. foreground: "#000"
  6411. });
  6412. $qr.find(".am-share-wx-qr").html(qrNode);
  6413. $qr.appendTo($("body"));
  6414. this.$wechatQr = $("#" + qrId);
  6415. }
  6416. this.$wechatQr.modal("open");
  6417. };
  6418. var share = new Share();
  6419. UI.share = share;
  6420. $doc.on("click.share.amui", '[data-am-toggle="share"]', function(e) {
  6421. e.preventDefault();
  6422. share.toggle();
  6423. });
  6424. module.exports = share;
  6425. });
  6426. define("ui.smooth-scroll", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  6427. var UI = require("core"), rAF = UI.utils.rAF;
  6428. var $ = window.Zepto;
  6429. /**
  6430. * Smooth Scroll
  6431. * @param position
  6432. * @via http://mir.aculo.us/2014/01/19/scrolling-dom-elements-to-the-top-a-zepto-plugin/
  6433. */
  6434. // Usage: $(window).smoothScroll([options])
  6435. // only allow one scroll to top operation to be in progress at a time,
  6436. // which is probably what you want
  6437. var smoothScrollInProgress = false;
  6438. $.fn.smoothScroll = function(options) {
  6439. options = options || {};
  6440. var $this = this, targetY = parseInt(options.position) || 0, initialY = $this.scrollTop(), lastY = initialY, delta = targetY - initialY, // duration in ms, make it a bit shorter for short distances
  6441. // this is not scientific and you might want to adjust this for
  6442. // your preferences
  6443. speed = options.speed || Math.min(750, Math.min(1500, Math.abs(initialY - targetY))), // temp variables (t will be a position between 0 and 1, y is the calculated scrollTop)
  6444. start, t, y, cancelScroll = function() {
  6445. abort();
  6446. };
  6447. // abort if already in progress or nothing to scroll
  6448. if (smoothScrollInProgress) return;
  6449. if (delta == 0) return;
  6450. // quint ease-in-out smoothing, from
  6451. // https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js#L127-L136
  6452. function smooth(pos) {
  6453. if ((pos /= .5) < 1) return .5 * Math.pow(pos, 5);
  6454. return .5 * (Math.pow(pos - 2, 5) + 2);
  6455. }
  6456. function abort() {
  6457. $this.off("touchstart", cancelScroll);
  6458. smoothScrollInProgress = false;
  6459. }
  6460. // when there's a touch detected while scrolling is in progress, abort
  6461. // the scrolling (emulates native scrolling behavior)
  6462. $this.on("touchstart", cancelScroll);
  6463. smoothScrollInProgress = true;
  6464. // start rendering away! note the function given to frame
  6465. // is named "render" so we can reference it again further down
  6466. rAF(function render(now) {
  6467. if (!smoothScrollInProgress) return;
  6468. if (!start) start = now;
  6469. // calculate t, position of animation in [0..1]
  6470. t = Math.min(1, Math.max((now - start) / speed, 0));
  6471. // calculate the new scrollTop position (don't forget to smooth)
  6472. y = Math.round(initialY + delta * smooth(t));
  6473. // bracket scrollTop so we're never over-scrolling
  6474. if (delta > 0 && y > targetY) y = targetY;
  6475. if (delta < 0 && y < targetY) y = targetY;
  6476. // only actually set scrollTop if there was a change fromt he last frame
  6477. if (lastY != y) $this.scrollTop(y);
  6478. lastY = y;
  6479. // if we're not done yet, queue up an other frame to render,
  6480. // or clean up
  6481. if (y !== targetY) {
  6482. rAF(render);
  6483. } else {
  6484. abort();
  6485. }
  6486. });
  6487. };
  6488. // Init code
  6489. $(document).on("click.smoothScroll.amui", "[data-am-smooth-scroll]", function(e) {
  6490. e.preventDefault();
  6491. var options = UI.utils.parseOptions($(this).attr("data-am-smooth-scroll"));
  6492. $(window).smoothScroll(options);
  6493. });
  6494. });
  6495. define("ui.sticky", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  6496. require("core");
  6497. var $ = window.Zepto, UI = $.AMUI;
  6498. /**
  6499. * @via https://github.com/uikit/uikit/blob/master/src/js/addons/sticky.js
  6500. * @license https://github.com/uikit/uikit/blob/master/LICENSE.md
  6501. */
  6502. // Sticky Class
  6503. var Sticky = function(element, options) {
  6504. var me = this;
  6505. this.options = $.extend({}, Sticky.DEFAULTS, options);
  6506. this.$element = $(element);
  6507. this.sticked = null;
  6508. this.inited = null;
  6509. this.$holder = undefined;
  6510. this.$window = $(window).on("scroll.sticky.amui", UI.utils.debounce($.proxy(this.checkPosition, this), 10)).on("resize.sticky.amui orientationchange.sticky.amui", UI.utils.debounce(function() {
  6511. me.reset(true, function() {
  6512. me.checkPosition();
  6513. });
  6514. }, 50)).on("load.sticky.amui", $.proxy(this.checkPosition, this));
  6515. this.offset = this.$element.offset();
  6516. this.init();
  6517. };
  6518. Sticky.DEFAULTS = {
  6519. top: 0,
  6520. bottom: 0,
  6521. animation: "",
  6522. className: {
  6523. sticky: "am-sticky",
  6524. resetting: "am-sticky-resetting",
  6525. stickyBtm: "am-sticky-bottom",
  6526. animationRev: "am-animation-reverse"
  6527. }
  6528. };
  6529. Sticky.prototype.init = function() {
  6530. var result = this.check();
  6531. if (!result) return false;
  6532. var $element = this.$element, $holder = $('<div class="am-sticky-placeholder"></div>').css({
  6533. height: $element.css("position") != "absolute" ? $element.outerHeight() : "",
  6534. "float": $element.css("float") != "none" ? $element.css("float") : "",
  6535. margin: $element.css("margin")
  6536. });
  6537. this.$holder = $element.css("margin", 0).wrap($holder).parent();
  6538. this.inited = 1;
  6539. return true;
  6540. };
  6541. Sticky.prototype.reset = function(force, cb) {
  6542. var options = this.options, $element = this.$element, animation = options.animation ? " am-animation-" + options.animation : "", complete = function() {
  6543. $element.css({
  6544. position: "",
  6545. top: "",
  6546. width: "",
  6547. left: "",
  6548. margin: 0
  6549. });
  6550. $element.removeClass([ animation, options.className.animationRev, options.className.sticky, options.className.resetting ].join(" "));
  6551. this.animating = false;
  6552. this.sticked = false;
  6553. this.offset = $element.offset();
  6554. cb && cb();
  6555. }.bind(this);
  6556. $element.addClass(options.className.resetting);
  6557. if (!force && options.animation && UI.support.animation) {
  6558. this.animating = true;
  6559. $element.removeClass(animation).one(UI.support.animation.end, function() {
  6560. complete();
  6561. }).width();
  6562. // force redraw
  6563. $element.addClass(animation + " " + options.className.animationRev);
  6564. } else {
  6565. complete();
  6566. }
  6567. };
  6568. Sticky.prototype.check = function() {
  6569. if (!this.$element.is(":visible")) return false;
  6570. var media = this.options.media;
  6571. if (media) {
  6572. switch (typeof media) {
  6573. case "number":
  6574. if (window.innerWidth < media) {
  6575. return false;
  6576. }
  6577. break;
  6578. case "string":
  6579. if (window.matchMedia && !window.matchMedia(media).matches) {
  6580. return false;
  6581. }
  6582. break;
  6583. }
  6584. }
  6585. return true;
  6586. };
  6587. Sticky.prototype.checkPosition = function() {
  6588. if (!this.inited) {
  6589. var initialized = this.init();
  6590. if (!initialized) return;
  6591. }
  6592. var options = this.options, scrollHeight = $("body").height(), scrollTop = this.$window.scrollTop(), offsetTop = options.top, offsetBottom = options.bottom, $element = this.$element, animation = options.animation ? " am-animation-" + options.animation : "", className = [ options.className.sticky, animation ].join(" ");
  6593. if (typeof offsetBottom == "function") offsetBottom = offsetBottom(this.$element);
  6594. var checkResult = scrollTop > this.$holder.offset().top;
  6595. if (!this.sticked && checkResult) {
  6596. $element.addClass(className);
  6597. } else if (this.sticked && !checkResult) {
  6598. this.reset();
  6599. }
  6600. this.$holder.height($element.height());
  6601. if (checkResult) {
  6602. $element.css({
  6603. top: offsetTop,
  6604. left: this.$holder.offset().left,
  6605. width: this.offset.width
  6606. });
  6607. }
  6608. this.sticked = checkResult;
  6609. };
  6610. UI.sticky = Sticky;
  6611. // Sticky Plugin
  6612. function Plugin(option) {
  6613. return this.each(function() {
  6614. var $this = $(this), data = $this.data("am.sticky"), options = typeof option == "object" && option;
  6615. if (!data) $this.data("am.sticky", data = new Sticky(this, options));
  6616. if (typeof option == "string") data[option]();
  6617. });
  6618. }
  6619. $.fn.sticky = Plugin;
  6620. // Init code
  6621. $(window).on("load", function() {
  6622. $("[data-am-sticky]").each(function() {
  6623. var $this = $(this), options = UI.utils.options($this.attr("data-am-sticky"));
  6624. Plugin.call($this, options);
  6625. });
  6626. });
  6627. module.exports = Sticky;
  6628. });
  6629. define("ui.tabs", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "util.hammer" ], function(require, exports, module) {
  6630. require("core");
  6631. var Hammer = require("util.hammer");
  6632. var $ = window.Zepto, UI = $.AMUI, supportTransition = UI.support.transition, animation = UI.support.animation;
  6633. /**
  6634. * @via https://github.com/twbs/bootstrap/blob/master/js/tab.js
  6635. * @copyright 2011-2014 Twitter, Inc.
  6636. * @license MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  6637. */
  6638. var Tabs = function(element, options) {
  6639. this.$element = $(element);
  6640. this.options = $.extend({}, Tabs.DEFAULTS, options || {});
  6641. this.$tabNav = this.$element.find(this.options.selector.nav);
  6642. this.$navs = this.$tabNav.find("a");
  6643. this.$content = this.$element.find(this.options.selector.content);
  6644. this.$tabPanels = this.$content.find(this.options.selector.panel);
  6645. this.transitioning = null;
  6646. this.init();
  6647. };
  6648. Tabs.DEFAULTS = {
  6649. selector: {
  6650. nav: ".am-tabs-nav",
  6651. content: ".am-tabs-bd",
  6652. panel: ".am-tab-panel"
  6653. },
  6654. className: {
  6655. active: "am-active"
  6656. }
  6657. };
  6658. Tabs.prototype.init = function() {
  6659. var me = this, options = this.options;
  6660. // Activate the first Tab when no active Tab or multiple active Tabs
  6661. if (this.$tabNav.find("> .am-active").length !== 1) {
  6662. var $tabNav = this.$tabNav;
  6663. this.activate($tabNav.children("li").first(), $tabNav);
  6664. this.activate(this.$tabPanels.first(), this.$content);
  6665. }
  6666. this.$navs.on("click.tabs.amui", function(e) {
  6667. e.preventDefault();
  6668. me.open($(this));
  6669. });
  6670. var hammer = new Hammer(this.$content[0]);
  6671. hammer.get("pan").set({
  6672. direction: Hammer.DIRECTION_HORIZONTAL,
  6673. threshold: 30
  6674. });
  6675. hammer.on("panleft", UI.utils.debounce(function(e) {
  6676. e.preventDefault();
  6677. var $target = $(e.target);
  6678. if (!$target.is(options.selector.panel)) {
  6679. $target = $target.closest(options.selector.panel);
  6680. }
  6681. $target.focus();
  6682. var $nav = me.getNextNav($target);
  6683. $nav && me.open($nav);
  6684. }, 100));
  6685. hammer.on("panright", UI.utils.debounce(function(e) {
  6686. e.preventDefault();
  6687. var $target = $(e.target);
  6688. if (!$target.is(options.selector.panel)) {
  6689. $target = $target.closest(options.selector.panel);
  6690. }
  6691. var $nav = me.getPrevNav($target);
  6692. $nav && me.open($nav);
  6693. }, 100));
  6694. };
  6695. Tabs.prototype.open = function($nav) {
  6696. if (!$nav || this.transitioning || $nav.parent("li").hasClass("am-active")) return;
  6697. var $tabNav = this.$tabNav, $navs = this.$navs, $tabContent = this.$content, href = $nav.attr("href"), regexHash = /^#.+$/, $target = regexHash.test(href) && this.$content.find(href) || this.$tabPanels.eq($navs.index($nav));
  6698. var previous = $tabNav.find(".am-active a")[0], e = $.Event("open:tabs:amui", {
  6699. relatedTarget: previous
  6700. });
  6701. $nav.trigger(e);
  6702. if (e.isDefaultPrevented()) return;
  6703. // activate Tab nav
  6704. this.activate($nav.closest("li"), $tabNav);
  6705. // activate Tab content
  6706. this.activate($target, $tabContent, function() {
  6707. $nav.trigger({
  6708. type: "opened:tabs:amui",
  6709. relatedTarget: previous
  6710. });
  6711. });
  6712. };
  6713. Tabs.prototype.activate = function($element, $container, callback) {
  6714. this.transitioning = true;
  6715. var $active = $container.find("> .am-active"), transition = callback && supportTransition && !!$active.length;
  6716. $active.removeClass("am-active am-in");
  6717. $element.addClass("am-active");
  6718. if (transition) {
  6719. $element.redraw();
  6720. // reflow for transition
  6721. $element.addClass("am-in");
  6722. } else {
  6723. $element.removeClass("am-fade");
  6724. }
  6725. function complete() {
  6726. callback && callback();
  6727. this.transitioning = false;
  6728. }
  6729. transition ? $active.one(supportTransition.end, $.proxy(complete, this)) : $.proxy(complete, this)();
  6730. };
  6731. Tabs.prototype.getNextNav = function($panel) {
  6732. var navIndex = this.$tabPanels.index($panel), rightSpring = "am-animation-right-spring";
  6733. if (navIndex + 1 >= this.$navs.length) {
  6734. // last one
  6735. animation && $panel.addClass(rightSpring).on(animation.end, function() {
  6736. $panel.removeClass(rightSpring);
  6737. });
  6738. return null;
  6739. } else {
  6740. return this.$navs.eq(navIndex + 1);
  6741. }
  6742. };
  6743. Tabs.prototype.getPrevNav = function($panel) {
  6744. var navIndex = this.$tabPanels.index($panel), leftSpring = "am-animation-left-spring";
  6745. if (navIndex === 0) {
  6746. // first one
  6747. animation && $panel.addClass(leftSpring).on(animation.end, function() {
  6748. $panel.removeClass(leftSpring);
  6749. });
  6750. return null;
  6751. } else {
  6752. return this.$navs.eq(navIndex - 1);
  6753. }
  6754. };
  6755. // Plugin
  6756. function Plugin(option) {
  6757. return this.each(function() {
  6758. var $this = $(this), $tabs = $this.is(".am-tabs") && $this || $this.closest(".am-tabs"), data = $tabs.data("amui.tabs");
  6759. if (!data) $tabs.data("amui.tabs", data = new Tabs($tabs[0]));
  6760. if (typeof option == "string" && $this.is(".am-tabs-nav a")) data[option]($this);
  6761. });
  6762. }
  6763. $.fn.tabs = Plugin;
  6764. // Init code
  6765. $(document).on("ready", function(e) {
  6766. $("[data-am-tabs]").tabs();
  6767. });
  6768. module.exports = Tabs;
  6769. });
  6770. define("util.cookie", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  6771. "use strict";
  6772. require("core");
  6773. var $ = window.Zepto, UI = $.AMUI;
  6774. var cookie = {
  6775. get: function(name) {
  6776. var cookieName = encodeURIComponent(name) + "=", cookieStart = document.cookie.indexOf(cookieName), cookieValue = null, cookieEnd;
  6777. if (cookieStart > -1) {
  6778. cookieEnd = document.cookie.indexOf(";", cookieStart);
  6779. if (cookieEnd == -1) {
  6780. cookieEnd = document.cookie.length;
  6781. }
  6782. cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
  6783. }
  6784. return cookieValue;
  6785. },
  6786. set: function(name, value, expires, path, domain, secure) {
  6787. var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
  6788. if (expires instanceof Date) {
  6789. cookieText += "; expires=" + expires.toGMTString();
  6790. }
  6791. if (path) {
  6792. cookieText += "; path=" + path;
  6793. }
  6794. if (domain) {
  6795. cookieText += "; domain=" + domain;
  6796. }
  6797. if (secure) {
  6798. cookieText += "; secure";
  6799. }
  6800. document.cookie = cookieText;
  6801. },
  6802. unset: function(name, path, domain, secure) {
  6803. this.set(name, "", new Date(0), path, domain, secure);
  6804. }
  6805. };
  6806. UI.utils.cookie = cookie;
  6807. module.exports = cookie;
  6808. });
  6809. define("util.fullscreen", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  6810. require("core");
  6811. var $ = window.Zepto, UI = $.AMUI;
  6812. /**
  6813. * @via https://github.com/sindresorhus/screenfull.js
  6814. * @license MIT © Sindre Sorhus
  6815. * @version 1.2.1
  6816. */
  6817. "use strict";
  6818. var keyboardAllowed = typeof Element !== "undefined" && "ALLOW_KEYBOARD_INPUT" in Element;
  6819. var fn = function() {
  6820. var val;
  6821. var valLength;
  6822. var fnMap = [ [ "requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror" ], // new WebKit
  6823. [ "webkitRequestFullscreen", "webkitExitFullscreen", "webkitFullscreenElement", "webkitFullscreenEnabled", "webkitfullscreenchange", "webkitfullscreenerror" ], // old WebKit (Safari 5.1)
  6824. [ "webkitRequestFullScreen", "webkitCancelFullScreen", "webkitCurrentFullScreenElement", "webkitCancelFullScreen", "webkitfullscreenchange", "webkitfullscreenerror" ], [ "mozRequestFullScreen", "mozCancelFullScreen", "mozFullScreenElement", "mozFullScreenEnabled", "mozfullscreenchange", "mozfullscreenerror" ], [ "msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError" ] ];
  6825. var i = 0;
  6826. var l = fnMap.length;
  6827. var ret = {};
  6828. for (;i < l; i++) {
  6829. val = fnMap[i];
  6830. if (val && val[1] in document) {
  6831. for (i = 0, valLength = val.length; i < valLength; i++) {
  6832. ret[fnMap[0][i]] = val[i];
  6833. }
  6834. return ret;
  6835. }
  6836. }
  6837. return false;
  6838. }();
  6839. var fullscreen = {
  6840. request: function(elem) {
  6841. var request = fn.requestFullscreen;
  6842. elem = elem || document.documentElement;
  6843. // Work around Safari 5.1 bug: reports support for
  6844. // keyboard in fullscreen even though it doesn't.
  6845. // Browser sniffing, since the alternative with
  6846. // setTimeout is even worse.
  6847. if (/5\.1[\.\d]* Safari/.test(navigator.userAgent)) {
  6848. elem[request]();
  6849. } else {
  6850. elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT);
  6851. }
  6852. },
  6853. exit: function() {
  6854. document[fn.exitFullscreen]();
  6855. },
  6856. toggle: function(elem) {
  6857. if (this.isFullscreen) {
  6858. this.exit();
  6859. } else {
  6860. this.request(elem);
  6861. }
  6862. },
  6863. onchange: function() {},
  6864. onerror: function() {},
  6865. raw: fn
  6866. };
  6867. if (!fn) {
  6868. module.exports = false;
  6869. return;
  6870. }
  6871. Object.defineProperties(fullscreen, {
  6872. isFullscreen: {
  6873. get: function() {
  6874. return !!document[fn.fullscreenElement];
  6875. }
  6876. },
  6877. element: {
  6878. enumerable: true,
  6879. get: function() {
  6880. return document[fn.fullscreenElement];
  6881. }
  6882. },
  6883. enabled: {
  6884. enumerable: true,
  6885. get: function() {
  6886. // Coerce to boolean in case of old WebKit
  6887. return !!document[fn.fullscreenEnabled];
  6888. }
  6889. }
  6890. });
  6891. document.addEventListener(fn.fullscreenchange, function(e) {
  6892. fullscreen.onchange.call(fullscreen, e);
  6893. });
  6894. document.addEventListener(fn.fullscreenerror, function(e) {
  6895. fullscreen.onerror.call(fullscreen, e);
  6896. });
  6897. //!window.fullscreen && (window.fullscreen = fullscreen);
  6898. UI.fullscreen = fullscreen;
  6899. module.exports = fullscreen;
  6900. });
  6901. define("util.qrcode", [], function(require, exports, module) {
  6902. var $ = Zepto;
  6903. /**
  6904. * @ver 1.1.0
  6905. * @via https://github.com/aralejs/qrcode/blob/master/src/qrcode.js
  6906. * @license http://aralejs.org/
  6907. */
  6908. var qrcodeAlgObjCache = [];
  6909. /**
  6910. * 二维码构造函数,主要用于绘制
  6911. * @param {参数列表} opt 传递参数
  6912. * @return {}
  6913. */
  6914. var qrcode = function(opt) {
  6915. if (typeof opt === "string") {
  6916. // 只编码ASCII字符串
  6917. opt = {
  6918. text: opt
  6919. };
  6920. }
  6921. //设置默认参数
  6922. this.options = $.extend({}, {
  6923. text: "",
  6924. render: "",
  6925. width: 256,
  6926. height: 256,
  6927. correctLevel: 3,
  6928. background: "#ffffff",
  6929. foreground: "#000000"
  6930. }, opt);
  6931. //使用QRCodeAlg创建二维码结构
  6932. var qrCodeAlg = null;
  6933. for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
  6934. if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
  6935. qrCodeAlg = qrcodeAlgObjCache[i].obj;
  6936. break;
  6937. }
  6938. }
  6939. if (i == l) {
  6940. qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
  6941. qrcodeAlgObjCache.push({
  6942. text: this.options.text,
  6943. correctLevel: this.options.correctLevel,
  6944. obj: qrCodeAlg
  6945. });
  6946. }
  6947. if (this.options.render) {
  6948. switch (this.options.render) {
  6949. case "canvas":
  6950. return this.createCanvas(qrCodeAlg);
  6951. case "table":
  6952. return this.createTable(qrCodeAlg);
  6953. case "svg":
  6954. return this.createSVG(qrCodeAlg);
  6955. default:
  6956. return this.createDefault(qrCodeAlg);
  6957. }
  6958. }
  6959. return this.createDefault(qrCodeAlg);
  6960. };
  6961. /**
  6962. * 使用Canvas来画二维码
  6963. * @return {}
  6964. */
  6965. qrcode.prototype.createDefault = function(qrCodeAlg) {
  6966. var canvas = document.createElement("canvas");
  6967. if (canvas.getContext) return this.createCanvas(qrCodeAlg);
  6968. SVG_NS = "http://www.w3.org/2000/svg";
  6969. if (!!document.createElementNS && !!document.createElementNS(SVG_NS, "svg").createSVGRect) return this.createSVG(qrCodeAlg);
  6970. return this.createTable(qrCodeAlg);
  6971. };
  6972. qrcode.prototype.createCanvas = function(qrCodeAlg) {
  6973. //创建canvas节点
  6974. var canvas = document.createElement("canvas");
  6975. canvas.width = this.options.width;
  6976. canvas.height = this.options.height;
  6977. var ctx = canvas.getContext("2d");
  6978. //计算每个点的长宽
  6979. var tileW = (this.options.width / qrCodeAlg.getModuleCount()).toPrecision(4);
  6980. var tileH = this.options.height / qrCodeAlg.getModuleCount().toPrecision(4);
  6981. //绘制
  6982. for (var row = 0; row < qrCodeAlg.getModuleCount(); row++) {
  6983. for (var col = 0; col < qrCodeAlg.getModuleCount(); col++) {
  6984. ctx.fillStyle = qrCodeAlg.modules[row][col] ? this.options.foreground : this.options.background;
  6985. var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW);
  6986. var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW);
  6987. ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
  6988. }
  6989. }
  6990. //返回绘制的节点
  6991. return canvas;
  6992. };
  6993. /**
  6994. * 使用table来绘制二维码
  6995. * @return {}
  6996. */
  6997. qrcode.prototype.createTable = function(qrCodeAlg) {
  6998. //创建table节点
  6999. var s = [];
  7000. s.push('<table style="border:0px; margin:0px; padding:0px; border-collapse:collapse; background-color: ' + this.options.background + ';">');
  7001. // 计算每个节点的长宽;取整,防止点之间出现分离
  7002. var tileW = -1, tileH = -1, caculateW = -1, caculateH = -1;
  7003. tileW = caculateW = Math.floor(this.options.width / qrCodeAlg.getModuleCount());
  7004. tileH = caculateH = Math.floor(this.options.height / qrCodeAlg.getModuleCount());
  7005. if (caculateW <= 0) {
  7006. if (qrCodeAlg.getModuleCount() < 80) {
  7007. tileW = 2;
  7008. } else {
  7009. tileW = 1;
  7010. }
  7011. }
  7012. if (caculateH <= 0) {
  7013. if (qrCodeAlg.getModuleCount() < 80) {
  7014. tileH = 2;
  7015. } else {
  7016. tileH = 1;
  7017. }
  7018. }
  7019. // 绘制二维码
  7020. foreTd = '<td style="border:0px; margin:0px; padding:0px; width:' + tileW + "px; background-color: " + this.options.foreground + '"></td>',
  7021. backTd = '<td style="border:0px; margin:0px; padding:0px; width:' + tileW + "px; background-color: " + this.options.background + '"></td>',
  7022. l = qrCodeAlg.getModuleCount();
  7023. for (var row = 0; row < l; row++) {
  7024. s.push('<tr style="border:0px; margin:0px; padding:0px; height: ' + tileH + 'px">');
  7025. for (var col = 0; col < l; col++) {
  7026. s.push(qrCodeAlg.modules[row][col] ? foreTd : backTd);
  7027. }
  7028. s.push("</tr>");
  7029. }
  7030. s.push("</table>");
  7031. var span = document.createElement("span");
  7032. span.innerHTML = s.join("");
  7033. return span.firstChild;
  7034. };
  7035. /**
  7036. * 使用SVG开绘制二维码
  7037. * @return {}
  7038. */
  7039. qrcode.prototype.createSVG = function(qrCodeAlg) {
  7040. var x, dx, y, dy, moduleCount = qrCodeAlg.getModuleCount(), scale = this.options.height / this.options.width, svg = '<svg xmlns="http://www.w3.org/2000/svg" ' + 'width="' + this.options.width + 'px" height="' + this.options.height + 'px" ' + 'viewbox="0 0 ' + moduleCount * 10 + " " + moduleCount * 10 * scale + '">', rectHead = "<path ", foreRect = ' style="stroke-width:0.5;stroke:' + this.options.foreground + ";fill:" + this.options.foreground + ';"></path>', backRect = ' style="stroke-width:0.5;stroke:' + this.options.background + ";fill:" + this.options.background + ';"></path>';
  7041. // draw in the svg
  7042. for (var row = 0; row < moduleCount; row++) {
  7043. for (var col = 0; col < moduleCount; col++) {
  7044. x = col * 10;
  7045. y = row * 10 * scale;
  7046. dx = (col + 1) * 10;
  7047. dy = (row + 1) * 10 * scale;
  7048. svg += rectHead + 'd="M ' + x + "," + y + " L " + dx + "," + y + " L " + dx + "," + dy + " L " + x + "," + dy + ' Z"';
  7049. svg += qrCodeAlg.modules[row][col] ? foreRect : backRect;
  7050. }
  7051. }
  7052. svg += "</svg>";
  7053. // return just built svg
  7054. return $(svg)[0];
  7055. };
  7056. module.exports = qrcode;
  7057. /**
  7058. * 获取单个字符的utf8编码
  7059. * unicode BMP平面约65535个字符
  7060. * @param {num} code
  7061. * return {array}
  7062. */
  7063. function unicodeFormat8(code) {
  7064. // 1 byte
  7065. if (code < 128) {
  7066. return [ code ];
  7067. } else if (code < 2048) {
  7068. c0 = 192 + (code >> 6);
  7069. c1 = 128 + (code & 63);
  7070. return [ c0, c1 ];
  7071. } else {
  7072. c0 = 224 + (code >> 12);
  7073. c1 = 128 + (code >> 6 & 63);
  7074. c2 = 128 + (code & 63);
  7075. return [ c0, c1, c2 ];
  7076. }
  7077. }
  7078. /**
  7079. * 获取字符串的utf8编码字节串
  7080. * @param {string} string
  7081. * @return {array}
  7082. */
  7083. function getUTF8Bytes(string) {
  7084. var utf8codes = [];
  7085. for (var i = 0; i < string.length; i++) {
  7086. var code = string.charCodeAt(i);
  7087. var utf8 = unicodeFormat8(code);
  7088. for (var j = 0; j < utf8.length; j++) {
  7089. utf8codes.push(utf8[j]);
  7090. }
  7091. }
  7092. return utf8codes;
  7093. }
  7094. /**
  7095. * 二维码算法实现
  7096. * @param {string} data 要编码的信息字符串
  7097. * @param {num} errorCorrectLevel 纠错等级
  7098. */
  7099. function QRCodeAlg(data, errorCorrectLevel) {
  7100. this.typeNumber = -1;
  7101. //版本
  7102. this.errorCorrectLevel = errorCorrectLevel;
  7103. this.modules = null;
  7104. //二维矩阵,存放最终结果
  7105. this.moduleCount = 0;
  7106. //矩阵大小
  7107. this.dataCache = null;
  7108. //数据缓存
  7109. this.rsBlocks = null;
  7110. //版本数据信息
  7111. this.totalDataCount = -1;
  7112. //可使用的数据量
  7113. this.data = data;
  7114. this.utf8bytes = getUTF8Bytes(data);
  7115. this.make();
  7116. }
  7117. QRCodeAlg.prototype = {
  7118. constructor: QRCodeAlg,
  7119. /**
  7120. * 获取二维码矩阵大小
  7121. * @return {num} 矩阵大小
  7122. */
  7123. getModuleCount: function() {
  7124. return this.moduleCount;
  7125. },
  7126. /**
  7127. * 编码
  7128. */
  7129. make: function() {
  7130. this.getRightType();
  7131. this.dataCache = this.createData();
  7132. this.createQrcode();
  7133. },
  7134. /**
  7135. * 设置二位矩阵功能图形
  7136. * @param {bool} test 表示是否在寻找最好掩膜阶段
  7137. * @param {num} maskPattern 掩膜的版本
  7138. */
  7139. makeImpl: function(maskPattern) {
  7140. this.moduleCount = this.typeNumber * 4 + 17;
  7141. this.modules = new Array(this.moduleCount);
  7142. for (var row = 0; row < this.moduleCount; row++) {
  7143. this.modules[row] = new Array(this.moduleCount);
  7144. }
  7145. this.setupPositionProbePattern(0, 0);
  7146. this.setupPositionProbePattern(this.moduleCount - 7, 0);
  7147. this.setupPositionProbePattern(0, this.moduleCount - 7);
  7148. this.setupPositionAdjustPattern();
  7149. this.setupTimingPattern();
  7150. this.setupTypeInfo(true, maskPattern);
  7151. if (this.typeNumber >= 7) {
  7152. this.setupTypeNumber(true);
  7153. }
  7154. this.mapData(this.dataCache, maskPattern);
  7155. },
  7156. /**
  7157. * 设置二维码的位置探测图形
  7158. * @param {num} row 探测图形的中心横坐标
  7159. * @param {num} col 探测图形的中心纵坐标
  7160. */
  7161. setupPositionProbePattern: function(row, col) {
  7162. for (var r = -1; r <= 7; r++) {
  7163. if (row + r <= -1 || this.moduleCount <= row + r) continue;
  7164. for (var c = -1; c <= 7; c++) {
  7165. if (col + c <= -1 || this.moduleCount <= col + c) continue;
  7166. if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
  7167. this.modules[row + r][col + c] = true;
  7168. } else {
  7169. this.modules[row + r][col + c] = false;
  7170. }
  7171. }
  7172. }
  7173. },
  7174. /**
  7175. * 创建二维码
  7176. * @return {[type]} [description]
  7177. */
  7178. createQrcode: function() {
  7179. var minLostPoint = 0;
  7180. var pattern = 0;
  7181. var bestModules = null;
  7182. for (var i = 0; i < 8; i++) {
  7183. this.makeImpl(i);
  7184. var lostPoint = QRUtil.getLostPoint(this);
  7185. if (i == 0 || minLostPoint > lostPoint) {
  7186. minLostPoint = lostPoint;
  7187. pattern = i;
  7188. bestModules = this.modules;
  7189. }
  7190. }
  7191. this.modules = bestModules;
  7192. this.setupTypeInfo(false, pattern);
  7193. if (this.typeNumber >= 7) {
  7194. this.setupTypeNumber(false);
  7195. }
  7196. },
  7197. /**
  7198. * 设置定位图形
  7199. * @return {[type]} [description]
  7200. */
  7201. setupTimingPattern: function() {
  7202. for (var r = 8; r < this.moduleCount - 8; r++) {
  7203. if (this.modules[r][6] != null) {
  7204. continue;
  7205. }
  7206. this.modules[r][6] = r % 2 == 0;
  7207. if (this.modules[6][r] != null) {
  7208. continue;
  7209. }
  7210. this.modules[6][r] = r % 2 == 0;
  7211. }
  7212. },
  7213. /**
  7214. * 设置矫正图形
  7215. * @return {[type]} [description]
  7216. */
  7217. setupPositionAdjustPattern: function() {
  7218. var pos = QRUtil.getPatternPosition(this.typeNumber);
  7219. for (var i = 0; i < pos.length; i++) {
  7220. for (var j = 0; j < pos.length; j++) {
  7221. var row = pos[i];
  7222. var col = pos[j];
  7223. if (this.modules[row][col] != null) {
  7224. continue;
  7225. }
  7226. for (var r = -2; r <= 2; r++) {
  7227. for (var c = -2; c <= 2; c++) {
  7228. if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {
  7229. this.modules[row + r][col + c] = true;
  7230. } else {
  7231. this.modules[row + r][col + c] = false;
  7232. }
  7233. }
  7234. }
  7235. }
  7236. }
  7237. },
  7238. /**
  7239. * 设置版本信息(7以上版本才有)
  7240. * @param {bool} test 是否处于判断最佳掩膜阶段
  7241. * @return {[type]} [description]
  7242. */
  7243. setupTypeNumber: function(test) {
  7244. var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
  7245. for (var i = 0; i < 18; i++) {
  7246. var mod = !test && (bits >> i & 1) == 1;
  7247. this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
  7248. this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
  7249. }
  7250. },
  7251. /**
  7252. * 设置格式信息(纠错等级和掩膜版本)
  7253. * @param {bool} test
  7254. * @param {num} maskPattern 掩膜版本
  7255. * @return {}
  7256. */
  7257. setupTypeInfo: function(test, maskPattern) {
  7258. var data = QRErrorCorrectLevel[this.errorCorrectLevel] << 3 | maskPattern;
  7259. var bits = QRUtil.getBCHTypeInfo(data);
  7260. // vertical
  7261. for (var i = 0; i < 15; i++) {
  7262. var mod = !test && (bits >> i & 1) == 1;
  7263. if (i < 6) {
  7264. this.modules[i][8] = mod;
  7265. } else if (i < 8) {
  7266. this.modules[i + 1][8] = mod;
  7267. } else {
  7268. this.modules[this.moduleCount - 15 + i][8] = mod;
  7269. }
  7270. // horizontal
  7271. var mod = !test && (bits >> i & 1) == 1;
  7272. if (i < 8) {
  7273. this.modules[8][this.moduleCount - i - 1] = mod;
  7274. } else if (i < 9) {
  7275. this.modules[8][15 - i - 1 + 1] = mod;
  7276. } else {
  7277. this.modules[8][15 - i - 1] = mod;
  7278. }
  7279. }
  7280. // fixed module
  7281. this.modules[this.moduleCount - 8][8] = !test;
  7282. },
  7283. /**
  7284. * 数据编码
  7285. * @return {[type]} [description]
  7286. */
  7287. createData: function() {
  7288. var buffer = new QRBitBuffer();
  7289. var lengthBits = this.typeNumber > 9 ? 16 : 8;
  7290. buffer.put(4, 4);
  7291. //添加模式
  7292. buffer.put(this.utf8bytes.length, lengthBits);
  7293. for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
  7294. buffer.put(this.utf8bytes[i], 8);
  7295. }
  7296. if (buffer.length + 4 <= this.totalDataCount * 8) {
  7297. buffer.put(0, 4);
  7298. }
  7299. // padding
  7300. while (buffer.length % 8 != 0) {
  7301. buffer.putBit(false);
  7302. }
  7303. // padding
  7304. while (true) {
  7305. if (buffer.length >= this.totalDataCount * 8) {
  7306. break;
  7307. }
  7308. buffer.put(QRCodeAlg.PAD0, 8);
  7309. if (buffer.length >= this.totalDataCount * 8) {
  7310. break;
  7311. }
  7312. buffer.put(QRCodeAlg.PAD1, 8);
  7313. }
  7314. return this.createBytes(buffer);
  7315. },
  7316. /**
  7317. * 纠错码编码
  7318. * @param {buffer} buffer 数据编码
  7319. * @return {[type]}
  7320. */
  7321. createBytes: function(buffer) {
  7322. var offset = 0;
  7323. var maxDcCount = 0;
  7324. var maxEcCount = 0;
  7325. var length = this.rsBlock.length / 3;
  7326. var rsBlocks = new Array();
  7327. for (var i = 0; i < length; i++) {
  7328. var count = this.rsBlock[i * 3 + 0];
  7329. var totalCount = this.rsBlock[i * 3 + 1];
  7330. var dataCount = this.rsBlock[i * 3 + 2];
  7331. for (var j = 0; j < count; j++) {
  7332. rsBlocks.push([ dataCount, totalCount ]);
  7333. }
  7334. }
  7335. var dcdata = new Array(rsBlocks.length);
  7336. var ecdata = new Array(rsBlocks.length);
  7337. for (var r = 0; r < rsBlocks.length; r++) {
  7338. var dcCount = rsBlocks[r][0];
  7339. var ecCount = rsBlocks[r][1] - dcCount;
  7340. maxDcCount = Math.max(maxDcCount, dcCount);
  7341. maxEcCount = Math.max(maxEcCount, ecCount);
  7342. dcdata[r] = new Array(dcCount);
  7343. for (var i = 0; i < dcdata[r].length; i++) {
  7344. dcdata[r][i] = 255 & buffer.buffer[i + offset];
  7345. }
  7346. offset += dcCount;
  7347. var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
  7348. var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
  7349. var modPoly = rawPoly.mod(rsPoly);
  7350. ecdata[r] = new Array(rsPoly.getLength() - 1);
  7351. for (var i = 0; i < ecdata[r].length; i++) {
  7352. var modIndex = i + modPoly.getLength() - ecdata[r].length;
  7353. ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
  7354. }
  7355. }
  7356. var data = new Array(this.totalDataCount);
  7357. var index = 0;
  7358. for (var i = 0; i < maxDcCount; i++) {
  7359. for (var r = 0; r < rsBlocks.length; r++) {
  7360. if (i < dcdata[r].length) {
  7361. data[index++] = dcdata[r][i];
  7362. }
  7363. }
  7364. }
  7365. for (var i = 0; i < maxEcCount; i++) {
  7366. for (var r = 0; r < rsBlocks.length; r++) {
  7367. if (i < ecdata[r].length) {
  7368. data[index++] = ecdata[r][i];
  7369. }
  7370. }
  7371. }
  7372. return data;
  7373. },
  7374. /**
  7375. * 布置模块,构建最终信息
  7376. * @param {} data
  7377. * @param {} maskPattern
  7378. * @return {}
  7379. */
  7380. mapData: function(data, maskPattern) {
  7381. var inc = -1;
  7382. var row = this.moduleCount - 1;
  7383. var bitIndex = 7;
  7384. var byteIndex = 0;
  7385. for (var col = this.moduleCount - 1; col > 0; col -= 2) {
  7386. if (col == 6) col--;
  7387. while (true) {
  7388. for (var c = 0; c < 2; c++) {
  7389. if (this.modules[row][col - c] == null) {
  7390. var dark = false;
  7391. if (byteIndex < data.length) {
  7392. dark = (data[byteIndex] >>> bitIndex & 1) == 1;
  7393. }
  7394. var mask = QRUtil.getMask(maskPattern, row, col - c);
  7395. if (mask) {
  7396. dark = !dark;
  7397. }
  7398. this.modules[row][col - c] = dark;
  7399. bitIndex--;
  7400. if (bitIndex == -1) {
  7401. byteIndex++;
  7402. bitIndex = 7;
  7403. }
  7404. }
  7405. }
  7406. row += inc;
  7407. if (row < 0 || this.moduleCount <= row) {
  7408. row -= inc;
  7409. inc = -inc;
  7410. break;
  7411. }
  7412. }
  7413. }
  7414. }
  7415. };
  7416. /**
  7417. * 填充字段
  7418. */
  7419. QRCodeAlg.PAD0 = 236;
  7420. QRCodeAlg.PAD1 = 17;
  7421. //---------------------------------------------------------------------
  7422. // 纠错等级对应的编码
  7423. //---------------------------------------------------------------------
  7424. var QRErrorCorrectLevel = [ 1, 0, 3, 2 ];
  7425. //---------------------------------------------------------------------
  7426. // 掩膜版本
  7427. //---------------------------------------------------------------------
  7428. var QRMaskPattern = {
  7429. PATTERN000: 0,
  7430. PATTERN001: 1,
  7431. PATTERN010: 2,
  7432. PATTERN011: 3,
  7433. PATTERN100: 4,
  7434. PATTERN101: 5,
  7435. PATTERN110: 6,
  7436. PATTERN111: 7
  7437. };
  7438. //---------------------------------------------------------------------
  7439. // 工具类
  7440. //---------------------------------------------------------------------
  7441. var QRUtil = {
  7442. /*
  7443. 每个版本矫正图形的位置
  7444. */
  7445. PATTERN_POSITION_TABLE: [ [], [ 6, 18 ], [ 6, 22 ], [ 6, 26 ], [ 6, 30 ], [ 6, 34 ], [ 6, 22, 38 ], [ 6, 24, 42 ], [ 6, 26, 46 ], [ 6, 28, 50 ], [ 6, 30, 54 ], [ 6, 32, 58 ], [ 6, 34, 62 ], [ 6, 26, 46, 66 ], [ 6, 26, 48, 70 ], [ 6, 26, 50, 74 ], [ 6, 30, 54, 78 ], [ 6, 30, 56, 82 ], [ 6, 30, 58, 86 ], [ 6, 34, 62, 90 ], [ 6, 28, 50, 72, 94 ], [ 6, 26, 50, 74, 98 ], [ 6, 30, 54, 78, 102 ], [ 6, 28, 54, 80, 106 ], [ 6, 32, 58, 84, 110 ], [ 6, 30, 58, 86, 114 ], [ 6, 34, 62, 90, 118 ], [ 6, 26, 50, 74, 98, 122 ], [ 6, 30, 54, 78, 102, 126 ], [ 6, 26, 52, 78, 104, 130 ], [ 6, 30, 56, 82, 108, 134 ], [ 6, 34, 60, 86, 112, 138 ], [ 6, 30, 58, 86, 114, 142 ], [ 6, 34, 62, 90, 118, 146 ], [ 6, 30, 54, 78, 102, 126, 150 ], [ 6, 24, 50, 76, 102, 128, 154 ], [ 6, 28, 54, 80, 106, 132, 158 ], [ 6, 32, 58, 84, 110, 136, 162 ], [ 6, 26, 54, 82, 110, 138, 166 ], [ 6, 30, 58, 86, 114, 142, 170 ] ],
  7446. G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
  7447. G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
  7448. G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
  7449. /*
  7450. BCH编码格式信息
  7451. */
  7452. getBCHTypeInfo: function(data) {
  7453. var d = data << 10;
  7454. while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
  7455. d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
  7456. }
  7457. return (data << 10 | d) ^ QRUtil.G15_MASK;
  7458. },
  7459. /*
  7460. BCH编码版本信息
  7461. */
  7462. getBCHTypeNumber: function(data) {
  7463. var d = data << 12;
  7464. while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
  7465. d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
  7466. }
  7467. return data << 12 | d;
  7468. },
  7469. /*
  7470. 获取BCH位信息
  7471. */
  7472. getBCHDigit: function(data) {
  7473. var digit = 0;
  7474. while (data != 0) {
  7475. digit++;
  7476. data >>>= 1;
  7477. }
  7478. return digit;
  7479. },
  7480. /*
  7481. 获取版本对应的矫正图形位置
  7482. */
  7483. getPatternPosition: function(typeNumber) {
  7484. return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
  7485. },
  7486. /*
  7487. 掩膜算法
  7488. */
  7489. getMask: function(maskPattern, i, j) {
  7490. switch (maskPattern) {
  7491. case QRMaskPattern.PATTERN000:
  7492. return (i + j) % 2 == 0;
  7493. case QRMaskPattern.PATTERN001:
  7494. return i % 2 == 0;
  7495. case QRMaskPattern.PATTERN010:
  7496. return j % 3 == 0;
  7497. case QRMaskPattern.PATTERN011:
  7498. return (i + j) % 3 == 0;
  7499. case QRMaskPattern.PATTERN100:
  7500. return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
  7501. case QRMaskPattern.PATTERN101:
  7502. return i * j % 2 + i * j % 3 == 0;
  7503. case QRMaskPattern.PATTERN110:
  7504. return (i * j % 2 + i * j % 3) % 2 == 0;
  7505. case QRMaskPattern.PATTERN111:
  7506. return (i * j % 3 + (i + j) % 2) % 2 == 0;
  7507. default:
  7508. throw new Error("bad maskPattern:" + maskPattern);
  7509. }
  7510. },
  7511. /*
  7512. 获取RS的纠错多项式
  7513. */
  7514. getErrorCorrectPolynomial: function(errorCorrectLength) {
  7515. var a = new QRPolynomial([ 1 ], 0);
  7516. for (var i = 0; i < errorCorrectLength; i++) {
  7517. a = a.multiply(new QRPolynomial([ 1, QRMath.gexp(i) ], 0));
  7518. }
  7519. return a;
  7520. },
  7521. /*
  7522. 获取评价
  7523. */
  7524. getLostPoint: function(qrCode) {
  7525. var moduleCount = qrCode.getModuleCount(), lostPoint = 0, darkCount = 0;
  7526. for (var row = 0; row < moduleCount; row++) {
  7527. var sameCount = 0;
  7528. var head = qrCode.modules[row][0];
  7529. for (var col = 0; col < moduleCount; col++) {
  7530. var current = qrCode.modules[row][col];
  7531. //level 3 评价
  7532. if (col < moduleCount - 6) {
  7533. if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
  7534. if (col < moduleCount - 10) {
  7535. if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
  7536. lostPoint += 40;
  7537. }
  7538. } else if (col > 3) {
  7539. if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
  7540. lostPoint += 40;
  7541. }
  7542. }
  7543. }
  7544. }
  7545. //level 2 评价
  7546. if (row < moduleCount - 1 && col < moduleCount - 1) {
  7547. var count = 0;
  7548. if (current) count++;
  7549. if (qrCode.modules[row + 1][col]) count++;
  7550. if (qrCode.modules[row][col + 1]) count++;
  7551. if (qrCode.modules[row + 1][col + 1]) count++;
  7552. if (count == 0 || count == 4) {
  7553. lostPoint += 3;
  7554. }
  7555. }
  7556. //level 1 评价
  7557. if (head ^ current) {
  7558. sameCount++;
  7559. } else {
  7560. head = current;
  7561. if (sameCount >= 5) {
  7562. lostPoint += 3 + sameCount - 5;
  7563. }
  7564. sameCount = 1;
  7565. }
  7566. //level 4 评价
  7567. if (current) {
  7568. darkCount++;
  7569. }
  7570. }
  7571. }
  7572. for (var col = 0; col < moduleCount; col++) {
  7573. var sameCount = 0;
  7574. var head = qrCode.modules[0][col];
  7575. for (var row = 0; row < moduleCount; row++) {
  7576. var current = qrCode.modules[row][col];
  7577. //level 3 评价
  7578. if (row < moduleCount - 6) {
  7579. if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
  7580. if (row < moduleCount - 10) {
  7581. if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
  7582. lostPoint += 40;
  7583. }
  7584. } else if (row > 3) {
  7585. if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
  7586. lostPoint += 40;
  7587. }
  7588. }
  7589. }
  7590. }
  7591. //level 1 评价
  7592. if (head ^ current) {
  7593. sameCount++;
  7594. } else {
  7595. head = current;
  7596. if (sameCount >= 5) {
  7597. lostPoint += 3 + sameCount - 5;
  7598. }
  7599. sameCount = 1;
  7600. }
  7601. }
  7602. }
  7603. // LEVEL4
  7604. var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
  7605. lostPoint += ratio * 10;
  7606. return lostPoint;
  7607. }
  7608. };
  7609. //---------------------------------------------------------------------
  7610. // QRMath使用的数学工具
  7611. //---------------------------------------------------------------------
  7612. var QRMath = {
  7613. /*
  7614. 将n转化为a^m
  7615. */
  7616. glog: function(n) {
  7617. if (n < 1) {
  7618. throw new Error("glog(" + n + ")");
  7619. }
  7620. return QRMath.LOG_TABLE[n];
  7621. },
  7622. /*
  7623. 将a^m转化为n
  7624. */
  7625. gexp: function(n) {
  7626. while (n < 0) {
  7627. n += 255;
  7628. }
  7629. while (n >= 256) {
  7630. n -= 255;
  7631. }
  7632. return QRMath.EXP_TABLE[n];
  7633. },
  7634. EXP_TABLE: new Array(256),
  7635. LOG_TABLE: new Array(256)
  7636. };
  7637. for (var i = 0; i < 8; i++) {
  7638. QRMath.EXP_TABLE[i] = 1 << i;
  7639. }
  7640. for (var i = 8; i < 256; i++) {
  7641. QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
  7642. }
  7643. for (var i = 0; i < 255; i++) {
  7644. QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
  7645. }
  7646. //---------------------------------------------------------------------
  7647. // QRPolynomial 多项式
  7648. //---------------------------------------------------------------------
  7649. /**
  7650. * 多项式类
  7651. * @param {Array} num 系数
  7652. * @param {num} shift a^shift
  7653. */
  7654. function QRPolynomial(num, shift) {
  7655. if (num.length == undefined) {
  7656. throw new Error(num.length + "/" + shift);
  7657. }
  7658. var offset = 0;
  7659. while (offset < num.length && num[offset] == 0) {
  7660. offset++;
  7661. }
  7662. this.num = new Array(num.length - offset + shift);
  7663. for (var i = 0; i < num.length - offset; i++) {
  7664. this.num[i] = num[i + offset];
  7665. }
  7666. }
  7667. QRPolynomial.prototype = {
  7668. get: function(index) {
  7669. return this.num[index];
  7670. },
  7671. getLength: function() {
  7672. return this.num.length;
  7673. },
  7674. /**
  7675. * 多项式乘法
  7676. * @param {QRPolynomial} e 被乘多项式
  7677. * @return {[type]} [description]
  7678. */
  7679. multiply: function(e) {
  7680. var num = new Array(this.getLength() + e.getLength() - 1);
  7681. for (var i = 0; i < this.getLength(); i++) {
  7682. for (var j = 0; j < e.getLength(); j++) {
  7683. num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
  7684. }
  7685. }
  7686. return new QRPolynomial(num, 0);
  7687. },
  7688. /**
  7689. * 多项式模运算
  7690. * @param {QRPolynomial} e 模多项式
  7691. * @return {}
  7692. */
  7693. mod: function(e) {
  7694. var tl = this.getLength(), el = e.getLength();
  7695. if (tl - el < 0) {
  7696. return this;
  7697. }
  7698. var num = new Array(tl);
  7699. for (var i = 0; i < tl; i++) {
  7700. num[i] = this.get(i);
  7701. }
  7702. while (num.length >= el) {
  7703. var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
  7704. for (var i = 0; i < e.getLength(); i++) {
  7705. num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
  7706. }
  7707. while (num[0] == 0) {
  7708. num.shift();
  7709. }
  7710. }
  7711. return new QRPolynomial(num, 0);
  7712. }
  7713. };
  7714. //---------------------------------------------------------------------
  7715. // RS_BLOCK_TABLE
  7716. //---------------------------------------------------------------------
  7717. /*
  7718. 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
  7719. */
  7720. RS_BLOCK_TABLE = [ // L
  7721. // M
  7722. // Q
  7723. // H
  7724. // 1
  7725. [ 1, 26, 19 ], [ 1, 26, 16 ], [ 1, 26, 13 ], [ 1, 26, 9 ], // 2
  7726. [ 1, 44, 34 ], [ 1, 44, 28 ], [ 1, 44, 22 ], [ 1, 44, 16 ], // 3
  7727. [ 1, 70, 55 ], [ 1, 70, 44 ], [ 2, 35, 17 ], [ 2, 35, 13 ], // 4
  7728. [ 1, 100, 80 ], [ 2, 50, 32 ], [ 2, 50, 24 ], [ 4, 25, 9 ], // 5
  7729. [ 1, 134, 108 ], [ 2, 67, 43 ], [ 2, 33, 15, 2, 34, 16 ], [ 2, 33, 11, 2, 34, 12 ], // 6
  7730. [ 2, 86, 68 ], [ 4, 43, 27 ], [ 4, 43, 19 ], [ 4, 43, 15 ], // 7
  7731. [ 2, 98, 78 ], [ 4, 49, 31 ], [ 2, 32, 14, 4, 33, 15 ], [ 4, 39, 13, 1, 40, 14 ], // 8
  7732. [ 2, 121, 97 ], [ 2, 60, 38, 2, 61, 39 ], [ 4, 40, 18, 2, 41, 19 ], [ 4, 40, 14, 2, 41, 15 ], // 9
  7733. [ 2, 146, 116 ], [ 3, 58, 36, 2, 59, 37 ], [ 4, 36, 16, 4, 37, 17 ], [ 4, 36, 12, 4, 37, 13 ], // 10
  7734. [ 2, 86, 68, 2, 87, 69 ], [ 4, 69, 43, 1, 70, 44 ], [ 6, 43, 19, 2, 44, 20 ], [ 6, 43, 15, 2, 44, 16 ], // 11
  7735. [ 4, 101, 81 ], [ 1, 80, 50, 4, 81, 51 ], [ 4, 50, 22, 4, 51, 23 ], [ 3, 36, 12, 8, 37, 13 ], // 12
  7736. [ 2, 116, 92, 2, 117, 93 ], [ 6, 58, 36, 2, 59, 37 ], [ 4, 46, 20, 6, 47, 21 ], [ 7, 42, 14, 4, 43, 15 ], // 13
  7737. [ 4, 133, 107 ], [ 8, 59, 37, 1, 60, 38 ], [ 8, 44, 20, 4, 45, 21 ], [ 12, 33, 11, 4, 34, 12 ], // 14
  7738. [ 3, 145, 115, 1, 146, 116 ], [ 4, 64, 40, 5, 65, 41 ], [ 11, 36, 16, 5, 37, 17 ], [ 11, 36, 12, 5, 37, 13 ], // 15
  7739. [ 5, 109, 87, 1, 110, 88 ], [ 5, 65, 41, 5, 66, 42 ], [ 5, 54, 24, 7, 55, 25 ], [ 11, 36, 12 ], // 16
  7740. [ 5, 122, 98, 1, 123, 99 ], [ 7, 73, 45, 3, 74, 46 ], [ 15, 43, 19, 2, 44, 20 ], [ 3, 45, 15, 13, 46, 16 ], // 17
  7741. [ 1, 135, 107, 5, 136, 108 ], [ 10, 74, 46, 1, 75, 47 ], [ 1, 50, 22, 15, 51, 23 ], [ 2, 42, 14, 17, 43, 15 ], // 18
  7742. [ 5, 150, 120, 1, 151, 121 ], [ 9, 69, 43, 4, 70, 44 ], [ 17, 50, 22, 1, 51, 23 ], [ 2, 42, 14, 19, 43, 15 ], // 19
  7743. [ 3, 141, 113, 4, 142, 114 ], [ 3, 70, 44, 11, 71, 45 ], [ 17, 47, 21, 4, 48, 22 ], [ 9, 39, 13, 16, 40, 14 ], // 20
  7744. [ 3, 135, 107, 5, 136, 108 ], [ 3, 67, 41, 13, 68, 42 ], [ 15, 54, 24, 5, 55, 25 ], [ 15, 43, 15, 10, 44, 16 ], // 21
  7745. [ 4, 144, 116, 4, 145, 117 ], [ 17, 68, 42 ], [ 17, 50, 22, 6, 51, 23 ], [ 19, 46, 16, 6, 47, 17 ], // 22
  7746. [ 2, 139, 111, 7, 140, 112 ], [ 17, 74, 46 ], [ 7, 54, 24, 16, 55, 25 ], [ 34, 37, 13 ], // 23
  7747. [ 4, 151, 121, 5, 152, 122 ], [ 4, 75, 47, 14, 76, 48 ], [ 11, 54, 24, 14, 55, 25 ], [ 16, 45, 15, 14, 46, 16 ], // 24
  7748. [ 6, 147, 117, 4, 148, 118 ], [ 6, 73, 45, 14, 74, 46 ], [ 11, 54, 24, 16, 55, 25 ], [ 30, 46, 16, 2, 47, 17 ], // 25
  7749. [ 8, 132, 106, 4, 133, 107 ], [ 8, 75, 47, 13, 76, 48 ], [ 7, 54, 24, 22, 55, 25 ], [ 22, 45, 15, 13, 46, 16 ], // 26
  7750. [ 10, 142, 114, 2, 143, 115 ], [ 19, 74, 46, 4, 75, 47 ], [ 28, 50, 22, 6, 51, 23 ], [ 33, 46, 16, 4, 47, 17 ], // 27
  7751. [ 8, 152, 122, 4, 153, 123 ], [ 22, 73, 45, 3, 74, 46 ], [ 8, 53, 23, 26, 54, 24 ], [ 12, 45, 15, 28, 46, 16 ], // 28
  7752. [ 3, 147, 117, 10, 148, 118 ], [ 3, 73, 45, 23, 74, 46 ], [ 4, 54, 24, 31, 55, 25 ], [ 11, 45, 15, 31, 46, 16 ], // 29
  7753. [ 7, 146, 116, 7, 147, 117 ], [ 21, 73, 45, 7, 74, 46 ], [ 1, 53, 23, 37, 54, 24 ], [ 19, 45, 15, 26, 46, 16 ], // 30
  7754. [ 5, 145, 115, 10, 146, 116 ], [ 19, 75, 47, 10, 76, 48 ], [ 15, 54, 24, 25, 55, 25 ], [ 23, 45, 15, 25, 46, 16 ], // 31
  7755. [ 13, 145, 115, 3, 146, 116 ], [ 2, 74, 46, 29, 75, 47 ], [ 42, 54, 24, 1, 55, 25 ], [ 23, 45, 15, 28, 46, 16 ], // 32
  7756. [ 17, 145, 115 ], [ 10, 74, 46, 23, 75, 47 ], [ 10, 54, 24, 35, 55, 25 ], [ 19, 45, 15, 35, 46, 16 ], // 33
  7757. [ 17, 145, 115, 1, 146, 116 ], [ 14, 74, 46, 21, 75, 47 ], [ 29, 54, 24, 19, 55, 25 ], [ 11, 45, 15, 46, 46, 16 ], // 34
  7758. [ 13, 145, 115, 6, 146, 116 ], [ 14, 74, 46, 23, 75, 47 ], [ 44, 54, 24, 7, 55, 25 ], [ 59, 46, 16, 1, 47, 17 ], // 35
  7759. [ 12, 151, 121, 7, 152, 122 ], [ 12, 75, 47, 26, 76, 48 ], [ 39, 54, 24, 14, 55, 25 ], [ 22, 45, 15, 41, 46, 16 ], // 36
  7760. [ 6, 151, 121, 14, 152, 122 ], [ 6, 75, 47, 34, 76, 48 ], [ 46, 54, 24, 10, 55, 25 ], [ 2, 45, 15, 64, 46, 16 ], // 37
  7761. [ 17, 152, 122, 4, 153, 123 ], [ 29, 74, 46, 14, 75, 47 ], [ 49, 54, 24, 10, 55, 25 ], [ 24, 45, 15, 46, 46, 16 ], // 38
  7762. [ 4, 152, 122, 18, 153, 123 ], [ 13, 74, 46, 32, 75, 47 ], [ 48, 54, 24, 14, 55, 25 ], [ 42, 45, 15, 32, 46, 16 ], // 39
  7763. [ 20, 147, 117, 4, 148, 118 ], [ 40, 75, 47, 7, 76, 48 ], [ 43, 54, 24, 22, 55, 25 ], [ 10, 45, 15, 67, 46, 16 ], // 40
  7764. [ 19, 148, 118, 6, 149, 119 ], [ 18, 75, 47, 31, 76, 48 ], [ 34, 54, 24, 34, 55, 25 ], [ 20, 45, 15, 61, 46, 16 ] ];
  7765. /**
  7766. * 根据数据获取对应版本
  7767. * @return {[type]} [description]
  7768. */
  7769. QRCodeAlg.prototype.getRightType = function() {
  7770. for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
  7771. var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
  7772. if (rsBlock == undefined) {
  7773. throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
  7774. }
  7775. var length = rsBlock.length / 3;
  7776. var totalDataCount = 0;
  7777. for (var i = 0; i < length; i++) {
  7778. var count = rsBlock[i * 3 + 0];
  7779. var dataCount = rsBlock[i * 3 + 2];
  7780. totalDataCount += dataCount * count;
  7781. }
  7782. var lengthBytes = typeNumber > 9 ? 2 : 1;
  7783. if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
  7784. this.typeNumber = typeNumber;
  7785. this.rsBlock = rsBlock;
  7786. this.totalDataCount = totalDataCount;
  7787. break;
  7788. }
  7789. }
  7790. };
  7791. //---------------------------------------------------------------------
  7792. // QRBitBuffer
  7793. //---------------------------------------------------------------------
  7794. function QRBitBuffer() {
  7795. this.buffer = new Array();
  7796. this.length = 0;
  7797. }
  7798. QRBitBuffer.prototype = {
  7799. get: function(index) {
  7800. var bufIndex = Math.floor(index / 8);
  7801. return this.buffer[bufIndex] >>> 7 - index % 8 & 1;
  7802. },
  7803. put: function(num, length) {
  7804. for (var i = 0; i < length; i++) {
  7805. this.putBit(num >>> length - i - 1 & 1);
  7806. }
  7807. },
  7808. putBit: function(bit) {
  7809. var bufIndex = Math.floor(this.length / 8);
  7810. if (this.buffer.length <= bufIndex) {
  7811. this.buffer.push(0);
  7812. }
  7813. if (bit) {
  7814. this.buffer[bufIndex] |= 128 >>> this.length % 8;
  7815. }
  7816. this.length++;
  7817. }
  7818. };
  7819. /**
  7820. * 获取单个字符的utf8编码
  7821. * unicode BMP平面约65535个字符
  7822. * @param {num} code
  7823. * return {array}
  7824. */
  7825. function unicodeFormat8(code) {
  7826. // 1 byte
  7827. if (code < 128) {
  7828. return [ code ];
  7829. } else if (code < 2048) {
  7830. c0 = 192 + (code >> 6);
  7831. c1 = 128 + (code & 63);
  7832. return [ c0, c1 ];
  7833. } else {
  7834. c0 = 224 + (code >> 12);
  7835. c1 = 128 + (code >> 6 & 63);
  7836. c2 = 128 + (code & 63);
  7837. return [ c0, c1, c2 ];
  7838. }
  7839. }
  7840. /**
  7841. * 获取字符串的utf8编码字节串
  7842. * @param {string} string
  7843. * @return {array}
  7844. */
  7845. function getUTF8Bytes(string) {
  7846. var utf8codes = [];
  7847. for (var i = 0; i < string.length; i++) {
  7848. var code = string.charCodeAt(i);
  7849. var utf8 = unicodeFormat8(code);
  7850. for (var j = 0; j < utf8.length; j++) {
  7851. utf8codes.push(utf8[j]);
  7852. }
  7853. }
  7854. return utf8codes;
  7855. }
  7856. /**
  7857. * 二维码算法实现
  7858. * @param {string} data 要编码的信息字符串
  7859. * @param {num} errorCorrectLevel 纠错等级
  7860. */
  7861. function QRCodeAlg(data, errorCorrectLevel) {
  7862. this.typeNumber = -1;
  7863. //版本
  7864. this.errorCorrectLevel = errorCorrectLevel;
  7865. this.modules = null;
  7866. //二维矩阵,存放最终结果
  7867. this.moduleCount = 0;
  7868. //矩阵大小
  7869. this.dataCache = null;
  7870. //数据缓存
  7871. this.rsBlocks = null;
  7872. //版本数据信息
  7873. this.totalDataCount = -1;
  7874. //可使用的数据量
  7875. this.data = data;
  7876. this.utf8bytes = getUTF8Bytes(data);
  7877. this.make();
  7878. }
  7879. QRCodeAlg.prototype = {
  7880. constructor: QRCodeAlg,
  7881. /**
  7882. * 获取二维码矩阵大小
  7883. * @return {num} 矩阵大小
  7884. */
  7885. getModuleCount: function() {
  7886. return this.moduleCount;
  7887. },
  7888. /**
  7889. * 编码
  7890. */
  7891. make: function() {
  7892. this.getRightType();
  7893. this.dataCache = this.createData();
  7894. this.createQrcode();
  7895. },
  7896. /**
  7897. * 设置二位矩阵功能图形
  7898. * @param {bool} test 表示是否在寻找最好掩膜阶段
  7899. * @param {num} maskPattern 掩膜的版本
  7900. */
  7901. makeImpl: function(maskPattern) {
  7902. this.moduleCount = this.typeNumber * 4 + 17;
  7903. this.modules = new Array(this.moduleCount);
  7904. for (var row = 0; row < this.moduleCount; row++) {
  7905. this.modules[row] = new Array(this.moduleCount);
  7906. }
  7907. this.setupPositionProbePattern(0, 0);
  7908. this.setupPositionProbePattern(this.moduleCount - 7, 0);
  7909. this.setupPositionProbePattern(0, this.moduleCount - 7);
  7910. this.setupPositionAdjustPattern();
  7911. this.setupTimingPattern();
  7912. this.setupTypeInfo(true, maskPattern);
  7913. if (this.typeNumber >= 7) {
  7914. this.setupTypeNumber(true);
  7915. }
  7916. this.mapData(this.dataCache, maskPattern);
  7917. },
  7918. /**
  7919. * 设置二维码的位置探测图形
  7920. * @param {num} row 探测图形的中心横坐标
  7921. * @param {num} col 探测图形的中心纵坐标
  7922. */
  7923. setupPositionProbePattern: function(row, col) {
  7924. for (var r = -1; r <= 7; r++) {
  7925. if (row + r <= -1 || this.moduleCount <= row + r) continue;
  7926. for (var c = -1; c <= 7; c++) {
  7927. if (col + c <= -1 || this.moduleCount <= col + c) continue;
  7928. if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
  7929. this.modules[row + r][col + c] = true;
  7930. } else {
  7931. this.modules[row + r][col + c] = false;
  7932. }
  7933. }
  7934. }
  7935. },
  7936. /**
  7937. * 创建二维码
  7938. * @return {[type]} [description]
  7939. */
  7940. createQrcode: function() {
  7941. var minLostPoint = 0;
  7942. var pattern = 0;
  7943. var bestModules = null;
  7944. for (var i = 0; i < 8; i++) {
  7945. this.makeImpl(i);
  7946. var lostPoint = QRUtil.getLostPoint(this);
  7947. if (i == 0 || minLostPoint > lostPoint) {
  7948. minLostPoint = lostPoint;
  7949. pattern = i;
  7950. bestModules = this.modules;
  7951. }
  7952. }
  7953. this.modules = bestModules;
  7954. this.setupTypeInfo(false, pattern);
  7955. if (this.typeNumber >= 7) {
  7956. this.setupTypeNumber(false);
  7957. }
  7958. },
  7959. /**
  7960. * 设置定位图形
  7961. * @return {[type]} [description]
  7962. */
  7963. setupTimingPattern: function() {
  7964. for (var r = 8; r < this.moduleCount - 8; r++) {
  7965. if (this.modules[r][6] != null) {
  7966. continue;
  7967. }
  7968. this.modules[r][6] = r % 2 == 0;
  7969. if (this.modules[6][r] != null) {
  7970. continue;
  7971. }
  7972. this.modules[6][r] = r % 2 == 0;
  7973. }
  7974. },
  7975. /**
  7976. * 设置矫正图形
  7977. * @return {[type]} [description]
  7978. */
  7979. setupPositionAdjustPattern: function() {
  7980. var pos = QRUtil.getPatternPosition(this.typeNumber);
  7981. for (var i = 0; i < pos.length; i++) {
  7982. for (var j = 0; j < pos.length; j++) {
  7983. var row = pos[i];
  7984. var col = pos[j];
  7985. if (this.modules[row][col] != null) {
  7986. continue;
  7987. }
  7988. for (var r = -2; r <= 2; r++) {
  7989. for (var c = -2; c <= 2; c++) {
  7990. if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {
  7991. this.modules[row + r][col + c] = true;
  7992. } else {
  7993. this.modules[row + r][col + c] = false;
  7994. }
  7995. }
  7996. }
  7997. }
  7998. }
  7999. },
  8000. /**
  8001. * 设置版本信息(7以上版本才有)
  8002. * @param {bool} test 是否处于判断最佳掩膜阶段
  8003. * @return {[type]} [description]
  8004. */
  8005. setupTypeNumber: function(test) {
  8006. var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
  8007. for (var i = 0; i < 18; i++) {
  8008. var mod = !test && (bits >> i & 1) == 1;
  8009. this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
  8010. this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
  8011. }
  8012. },
  8013. /**
  8014. * 设置格式信息(纠错等级和掩膜版本)
  8015. * @param {bool} test
  8016. * @param {num} maskPattern 掩膜版本
  8017. * @return {}
  8018. */
  8019. setupTypeInfo: function(test, maskPattern) {
  8020. var data = QRErrorCorrectLevel[this.errorCorrectLevel] << 3 | maskPattern;
  8021. var bits = QRUtil.getBCHTypeInfo(data);
  8022. // vertical
  8023. for (var i = 0; i < 15; i++) {
  8024. var mod = !test && (bits >> i & 1) == 1;
  8025. if (i < 6) {
  8026. this.modules[i][8] = mod;
  8027. } else if (i < 8) {
  8028. this.modules[i + 1][8] = mod;
  8029. } else {
  8030. this.modules[this.moduleCount - 15 + i][8] = mod;
  8031. }
  8032. // horizontal
  8033. var mod = !test && (bits >> i & 1) == 1;
  8034. if (i < 8) {
  8035. this.modules[8][this.moduleCount - i - 1] = mod;
  8036. } else if (i < 9) {
  8037. this.modules[8][15 - i - 1 + 1] = mod;
  8038. } else {
  8039. this.modules[8][15 - i - 1] = mod;
  8040. }
  8041. }
  8042. // fixed module
  8043. this.modules[this.moduleCount - 8][8] = !test;
  8044. },
  8045. /**
  8046. * 数据编码
  8047. * @return {[type]} [description]
  8048. */
  8049. createData: function() {
  8050. var buffer = new QRBitBuffer();
  8051. var lengthBits = this.typeNumber > 9 ? 16 : 8;
  8052. buffer.put(4, 4);
  8053. //添加模式
  8054. buffer.put(this.utf8bytes.length, lengthBits);
  8055. for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
  8056. buffer.put(this.utf8bytes[i], 8);
  8057. }
  8058. if (buffer.length + 4 <= this.totalDataCount * 8) {
  8059. buffer.put(0, 4);
  8060. }
  8061. // padding
  8062. while (buffer.length % 8 != 0) {
  8063. buffer.putBit(false);
  8064. }
  8065. // padding
  8066. while (true) {
  8067. if (buffer.length >= this.totalDataCount * 8) {
  8068. break;
  8069. }
  8070. buffer.put(QRCodeAlg.PAD0, 8);
  8071. if (buffer.length >= this.totalDataCount * 8) {
  8072. break;
  8073. }
  8074. buffer.put(QRCodeAlg.PAD1, 8);
  8075. }
  8076. return this.createBytes(buffer);
  8077. },
  8078. /**
  8079. * 纠错码编码
  8080. * @param {buffer} buffer 数据编码
  8081. * @return {[type]}
  8082. */
  8083. createBytes: function(buffer) {
  8084. var offset = 0;
  8085. var maxDcCount = 0;
  8086. var maxEcCount = 0;
  8087. var length = this.rsBlock.length / 3;
  8088. var rsBlocks = new Array();
  8089. for (var i = 0; i < length; i++) {
  8090. var count = this.rsBlock[i * 3 + 0];
  8091. var totalCount = this.rsBlock[i * 3 + 1];
  8092. var dataCount = this.rsBlock[i * 3 + 2];
  8093. for (var j = 0; j < count; j++) {
  8094. rsBlocks.push([ dataCount, totalCount ]);
  8095. }
  8096. }
  8097. var dcdata = new Array(rsBlocks.length);
  8098. var ecdata = new Array(rsBlocks.length);
  8099. for (var r = 0; r < rsBlocks.length; r++) {
  8100. var dcCount = rsBlocks[r][0];
  8101. var ecCount = rsBlocks[r][1] - dcCount;
  8102. maxDcCount = Math.max(maxDcCount, dcCount);
  8103. maxEcCount = Math.max(maxEcCount, ecCount);
  8104. dcdata[r] = new Array(dcCount);
  8105. for (var i = 0; i < dcdata[r].length; i++) {
  8106. dcdata[r][i] = 255 & buffer.buffer[i + offset];
  8107. }
  8108. offset += dcCount;
  8109. var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
  8110. var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
  8111. var modPoly = rawPoly.mod(rsPoly);
  8112. ecdata[r] = new Array(rsPoly.getLength() - 1);
  8113. for (var i = 0; i < ecdata[r].length; i++) {
  8114. var modIndex = i + modPoly.getLength() - ecdata[r].length;
  8115. ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
  8116. }
  8117. }
  8118. var data = new Array(this.totalDataCount);
  8119. var index = 0;
  8120. for (var i = 0; i < maxDcCount; i++) {
  8121. for (var r = 0; r < rsBlocks.length; r++) {
  8122. if (i < dcdata[r].length) {
  8123. data[index++] = dcdata[r][i];
  8124. }
  8125. }
  8126. }
  8127. for (var i = 0; i < maxEcCount; i++) {
  8128. for (var r = 0; r < rsBlocks.length; r++) {
  8129. if (i < ecdata[r].length) {
  8130. data[index++] = ecdata[r][i];
  8131. }
  8132. }
  8133. }
  8134. return data;
  8135. },
  8136. /**
  8137. * 布置模块,构建最终信息
  8138. * @param {} data
  8139. * @param {} maskPattern
  8140. * @return {}
  8141. */
  8142. mapData: function(data, maskPattern) {
  8143. var inc = -1;
  8144. var row = this.moduleCount - 1;
  8145. var bitIndex = 7;
  8146. var byteIndex = 0;
  8147. for (var col = this.moduleCount - 1; col > 0; col -= 2) {
  8148. if (col == 6) col--;
  8149. while (true) {
  8150. for (var c = 0; c < 2; c++) {
  8151. if (this.modules[row][col - c] == null) {
  8152. var dark = false;
  8153. if (byteIndex < data.length) {
  8154. dark = (data[byteIndex] >>> bitIndex & 1) == 1;
  8155. }
  8156. var mask = QRUtil.getMask(maskPattern, row, col - c);
  8157. if (mask) {
  8158. dark = !dark;
  8159. }
  8160. this.modules[row][col - c] = dark;
  8161. bitIndex--;
  8162. if (bitIndex == -1) {
  8163. byteIndex++;
  8164. bitIndex = 7;
  8165. }
  8166. }
  8167. }
  8168. row += inc;
  8169. if (row < 0 || this.moduleCount <= row) {
  8170. row -= inc;
  8171. inc = -inc;
  8172. break;
  8173. }
  8174. }
  8175. }
  8176. }
  8177. };
  8178. /**
  8179. * 填充字段
  8180. */
  8181. QRCodeAlg.PAD0 = 236;
  8182. QRCodeAlg.PAD1 = 17;
  8183. //---------------------------------------------------------------------
  8184. // 纠错等级对应的编码
  8185. //---------------------------------------------------------------------
  8186. var QRErrorCorrectLevel = [ 1, 0, 3, 2 ];
  8187. //---------------------------------------------------------------------
  8188. // 掩膜版本
  8189. //---------------------------------------------------------------------
  8190. var QRMaskPattern = {
  8191. PATTERN000: 0,
  8192. PATTERN001: 1,
  8193. PATTERN010: 2,
  8194. PATTERN011: 3,
  8195. PATTERN100: 4,
  8196. PATTERN101: 5,
  8197. PATTERN110: 6,
  8198. PATTERN111: 7
  8199. };
  8200. //---------------------------------------------------------------------
  8201. // 工具类
  8202. //---------------------------------------------------------------------
  8203. var QRUtil = {
  8204. /*
  8205. 每个版本矫正图形的位置
  8206. */
  8207. PATTERN_POSITION_TABLE: [ [], [ 6, 18 ], [ 6, 22 ], [ 6, 26 ], [ 6, 30 ], [ 6, 34 ], [ 6, 22, 38 ], [ 6, 24, 42 ], [ 6, 26, 46 ], [ 6, 28, 50 ], [ 6, 30, 54 ], [ 6, 32, 58 ], [ 6, 34, 62 ], [ 6, 26, 46, 66 ], [ 6, 26, 48, 70 ], [ 6, 26, 50, 74 ], [ 6, 30, 54, 78 ], [ 6, 30, 56, 82 ], [ 6, 30, 58, 86 ], [ 6, 34, 62, 90 ], [ 6, 28, 50, 72, 94 ], [ 6, 26, 50, 74, 98 ], [ 6, 30, 54, 78, 102 ], [ 6, 28, 54, 80, 106 ], [ 6, 32, 58, 84, 110 ], [ 6, 30, 58, 86, 114 ], [ 6, 34, 62, 90, 118 ], [ 6, 26, 50, 74, 98, 122 ], [ 6, 30, 54, 78, 102, 126 ], [ 6, 26, 52, 78, 104, 130 ], [ 6, 30, 56, 82, 108, 134 ], [ 6, 34, 60, 86, 112, 138 ], [ 6, 30, 58, 86, 114, 142 ], [ 6, 34, 62, 90, 118, 146 ], [ 6, 30, 54, 78, 102, 126, 150 ], [ 6, 24, 50, 76, 102, 128, 154 ], [ 6, 28, 54, 80, 106, 132, 158 ], [ 6, 32, 58, 84, 110, 136, 162 ], [ 6, 26, 54, 82, 110, 138, 166 ], [ 6, 30, 58, 86, 114, 142, 170 ] ],
  8208. G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
  8209. G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
  8210. G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
  8211. /*
  8212. BCH编码格式信息
  8213. */
  8214. getBCHTypeInfo: function(data) {
  8215. var d = data << 10;
  8216. while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
  8217. d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
  8218. }
  8219. return (data << 10 | d) ^ QRUtil.G15_MASK;
  8220. },
  8221. /*
  8222. BCH编码版本信息
  8223. */
  8224. getBCHTypeNumber: function(data) {
  8225. var d = data << 12;
  8226. while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
  8227. d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
  8228. }
  8229. return data << 12 | d;
  8230. },
  8231. /*
  8232. 获取BCH位信息
  8233. */
  8234. getBCHDigit: function(data) {
  8235. var digit = 0;
  8236. while (data != 0) {
  8237. digit++;
  8238. data >>>= 1;
  8239. }
  8240. return digit;
  8241. },
  8242. /*
  8243. 获取版本对应的矫正图形位置
  8244. */
  8245. getPatternPosition: function(typeNumber) {
  8246. return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
  8247. },
  8248. /*
  8249. 掩膜算法
  8250. */
  8251. getMask: function(maskPattern, i, j) {
  8252. switch (maskPattern) {
  8253. case QRMaskPattern.PATTERN000:
  8254. return (i + j) % 2 == 0;
  8255. case QRMaskPattern.PATTERN001:
  8256. return i % 2 == 0;
  8257. case QRMaskPattern.PATTERN010:
  8258. return j % 3 == 0;
  8259. case QRMaskPattern.PATTERN011:
  8260. return (i + j) % 3 == 0;
  8261. case QRMaskPattern.PATTERN100:
  8262. return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
  8263. case QRMaskPattern.PATTERN101:
  8264. return i * j % 2 + i * j % 3 == 0;
  8265. case QRMaskPattern.PATTERN110:
  8266. return (i * j % 2 + i * j % 3) % 2 == 0;
  8267. case QRMaskPattern.PATTERN111:
  8268. return (i * j % 3 + (i + j) % 2) % 2 == 0;
  8269. default:
  8270. throw new Error("bad maskPattern:" + maskPattern);
  8271. }
  8272. },
  8273. /*
  8274. 获取RS的纠错多项式
  8275. */
  8276. getErrorCorrectPolynomial: function(errorCorrectLength) {
  8277. var a = new QRPolynomial([ 1 ], 0);
  8278. for (var i = 0; i < errorCorrectLength; i++) {
  8279. a = a.multiply(new QRPolynomial([ 1, QRMath.gexp(i) ], 0));
  8280. }
  8281. return a;
  8282. },
  8283. /*
  8284. 获取评价
  8285. */
  8286. getLostPoint: function(qrCode) {
  8287. var moduleCount = qrCode.getModuleCount(), lostPoint = 0, darkCount = 0;
  8288. for (var row = 0; row < moduleCount; row++) {
  8289. var sameCount = 0;
  8290. var head = qrCode.modules[row][0];
  8291. for (var col = 0; col < moduleCount; col++) {
  8292. var current = qrCode.modules[row][col];
  8293. //level 3 评价
  8294. if (col < moduleCount - 6) {
  8295. if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
  8296. if (col < moduleCount - 10) {
  8297. if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
  8298. lostPoint += 40;
  8299. }
  8300. } else if (col > 3) {
  8301. if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
  8302. lostPoint += 40;
  8303. }
  8304. }
  8305. }
  8306. }
  8307. //level 2 评价
  8308. if (row < moduleCount - 1 && col < moduleCount - 1) {
  8309. var count = 0;
  8310. if (current) count++;
  8311. if (qrCode.modules[row + 1][col]) count++;
  8312. if (qrCode.modules[row][col + 1]) count++;
  8313. if (qrCode.modules[row + 1][col + 1]) count++;
  8314. if (count == 0 || count == 4) {
  8315. lostPoint += 3;
  8316. }
  8317. }
  8318. //level 1 评价
  8319. if (head ^ current) {
  8320. sameCount++;
  8321. } else {
  8322. head = current;
  8323. if (sameCount >= 5) {
  8324. lostPoint += 3 + sameCount - 5;
  8325. }
  8326. sameCount = 1;
  8327. }
  8328. //level 4 评价
  8329. if (current) {
  8330. darkCount++;
  8331. }
  8332. }
  8333. }
  8334. for (var col = 0; col < moduleCount; col++) {
  8335. var sameCount = 0;
  8336. var head = qrCode.modules[0][col];
  8337. for (var row = 0; row < moduleCount; row++) {
  8338. var current = qrCode.modules[row][col];
  8339. //level 3 评价
  8340. if (row < moduleCount - 6) {
  8341. if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
  8342. if (row < moduleCount - 10) {
  8343. if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
  8344. lostPoint += 40;
  8345. }
  8346. } else if (row > 3) {
  8347. if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
  8348. lostPoint += 40;
  8349. }
  8350. }
  8351. }
  8352. }
  8353. //level 1 评价
  8354. if (head ^ current) {
  8355. sameCount++;
  8356. } else {
  8357. head = current;
  8358. if (sameCount >= 5) {
  8359. lostPoint += 3 + sameCount - 5;
  8360. }
  8361. sameCount = 1;
  8362. }
  8363. }
  8364. }
  8365. // LEVEL4
  8366. var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
  8367. lostPoint += ratio * 10;
  8368. return lostPoint;
  8369. }
  8370. };
  8371. //---------------------------------------------------------------------
  8372. // QRMath使用的数学工具
  8373. //---------------------------------------------------------------------
  8374. var QRMath = {
  8375. /*
  8376. 将n转化为a^m
  8377. */
  8378. glog: function(n) {
  8379. if (n < 1) {
  8380. throw new Error("glog(" + n + ")");
  8381. }
  8382. return QRMath.LOG_TABLE[n];
  8383. },
  8384. /*
  8385. 将a^m转化为n
  8386. */
  8387. gexp: function(n) {
  8388. while (n < 0) {
  8389. n += 255;
  8390. }
  8391. while (n >= 256) {
  8392. n -= 255;
  8393. }
  8394. return QRMath.EXP_TABLE[n];
  8395. },
  8396. EXP_TABLE: new Array(256),
  8397. LOG_TABLE: new Array(256)
  8398. };
  8399. for (var i = 0; i < 8; i++) {
  8400. QRMath.EXP_TABLE[i] = 1 << i;
  8401. }
  8402. for (var i = 8; i < 256; i++) {
  8403. QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
  8404. }
  8405. for (var i = 0; i < 255; i++) {
  8406. QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
  8407. }
  8408. //---------------------------------------------------------------------
  8409. // QRPolynomial 多项式
  8410. //---------------------------------------------------------------------
  8411. /**
  8412. * 多项式类
  8413. * @param {Array} num 系数
  8414. * @param {num} shift a^shift
  8415. */
  8416. function QRPolynomial(num, shift) {
  8417. if (num.length == undefined) {
  8418. throw new Error(num.length + "/" + shift);
  8419. }
  8420. var offset = 0;
  8421. while (offset < num.length && num[offset] == 0) {
  8422. offset++;
  8423. }
  8424. this.num = new Array(num.length - offset + shift);
  8425. for (var i = 0; i < num.length - offset; i++) {
  8426. this.num[i] = num[i + offset];
  8427. }
  8428. }
  8429. QRPolynomial.prototype = {
  8430. get: function(index) {
  8431. return this.num[index];
  8432. },
  8433. getLength: function() {
  8434. return this.num.length;
  8435. },
  8436. /**
  8437. * 多项式乘法
  8438. * @param {QRPolynomial} e 被乘多项式
  8439. * @return {[type]} [description]
  8440. */
  8441. multiply: function(e) {
  8442. var num = new Array(this.getLength() + e.getLength() - 1);
  8443. for (var i = 0; i < this.getLength(); i++) {
  8444. for (var j = 0; j < e.getLength(); j++) {
  8445. num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
  8446. }
  8447. }
  8448. return new QRPolynomial(num, 0);
  8449. },
  8450. /**
  8451. * 多项式模运算
  8452. * @param {QRPolynomial} e 模多项式
  8453. * @return {}
  8454. */
  8455. mod: function(e) {
  8456. var tl = this.getLength(), el = e.getLength();
  8457. if (tl - el < 0) {
  8458. return this;
  8459. }
  8460. var num = new Array(tl);
  8461. for (var i = 0; i < tl; i++) {
  8462. num[i] = this.get(i);
  8463. }
  8464. while (num.length >= el) {
  8465. var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
  8466. for (var i = 0; i < e.getLength(); i++) {
  8467. num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
  8468. }
  8469. while (num[0] == 0) {
  8470. num.shift();
  8471. }
  8472. }
  8473. return new QRPolynomial(num, 0);
  8474. }
  8475. };
  8476. //---------------------------------------------------------------------
  8477. // RS_BLOCK_TABLE
  8478. //---------------------------------------------------------------------
  8479. /*
  8480. 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
  8481. */
  8482. RS_BLOCK_TABLE = [ // L
  8483. // M
  8484. // Q
  8485. // H
  8486. // 1
  8487. [ 1, 26, 19 ], [ 1, 26, 16 ], [ 1, 26, 13 ], [ 1, 26, 9 ], // 2
  8488. [ 1, 44, 34 ], [ 1, 44, 28 ], [ 1, 44, 22 ], [ 1, 44, 16 ], // 3
  8489. [ 1, 70, 55 ], [ 1, 70, 44 ], [ 2, 35, 17 ], [ 2, 35, 13 ], // 4
  8490. [ 1, 100, 80 ], [ 2, 50, 32 ], [ 2, 50, 24 ], [ 4, 25, 9 ], // 5
  8491. [ 1, 134, 108 ], [ 2, 67, 43 ], [ 2, 33, 15, 2, 34, 16 ], [ 2, 33, 11, 2, 34, 12 ], // 6
  8492. [ 2, 86, 68 ], [ 4, 43, 27 ], [ 4, 43, 19 ], [ 4, 43, 15 ], // 7
  8493. [ 2, 98, 78 ], [ 4, 49, 31 ], [ 2, 32, 14, 4, 33, 15 ], [ 4, 39, 13, 1, 40, 14 ], // 8
  8494. [ 2, 121, 97 ], [ 2, 60, 38, 2, 61, 39 ], [ 4, 40, 18, 2, 41, 19 ], [ 4, 40, 14, 2, 41, 15 ], // 9
  8495. [ 2, 146, 116 ], [ 3, 58, 36, 2, 59, 37 ], [ 4, 36, 16, 4, 37, 17 ], [ 4, 36, 12, 4, 37, 13 ], // 10
  8496. [ 2, 86, 68, 2, 87, 69 ], [ 4, 69, 43, 1, 70, 44 ], [ 6, 43, 19, 2, 44, 20 ], [ 6, 43, 15, 2, 44, 16 ], // 11
  8497. [ 4, 101, 81 ], [ 1, 80, 50, 4, 81, 51 ], [ 4, 50, 22, 4, 51, 23 ], [ 3, 36, 12, 8, 37, 13 ], // 12
  8498. [ 2, 116, 92, 2, 117, 93 ], [ 6, 58, 36, 2, 59, 37 ], [ 4, 46, 20, 6, 47, 21 ], [ 7, 42, 14, 4, 43, 15 ], // 13
  8499. [ 4, 133, 107 ], [ 8, 59, 37, 1, 60, 38 ], [ 8, 44, 20, 4, 45, 21 ], [ 12, 33, 11, 4, 34, 12 ], // 14
  8500. [ 3, 145, 115, 1, 146, 116 ], [ 4, 64, 40, 5, 65, 41 ], [ 11, 36, 16, 5, 37, 17 ], [ 11, 36, 12, 5, 37, 13 ], // 15
  8501. [ 5, 109, 87, 1, 110, 88 ], [ 5, 65, 41, 5, 66, 42 ], [ 5, 54, 24, 7, 55, 25 ], [ 11, 36, 12 ], // 16
  8502. [ 5, 122, 98, 1, 123, 99 ], [ 7, 73, 45, 3, 74, 46 ], [ 15, 43, 19, 2, 44, 20 ], [ 3, 45, 15, 13, 46, 16 ], // 17
  8503. [ 1, 135, 107, 5, 136, 108 ], [ 10, 74, 46, 1, 75, 47 ], [ 1, 50, 22, 15, 51, 23 ], [ 2, 42, 14, 17, 43, 15 ], // 18
  8504. [ 5, 150, 120, 1, 151, 121 ], [ 9, 69, 43, 4, 70, 44 ], [ 17, 50, 22, 1, 51, 23 ], [ 2, 42, 14, 19, 43, 15 ], // 19
  8505. [ 3, 141, 113, 4, 142, 114 ], [ 3, 70, 44, 11, 71, 45 ], [ 17, 47, 21, 4, 48, 22 ], [ 9, 39, 13, 16, 40, 14 ], // 20
  8506. [ 3, 135, 107, 5, 136, 108 ], [ 3, 67, 41, 13, 68, 42 ], [ 15, 54, 24, 5, 55, 25 ], [ 15, 43, 15, 10, 44, 16 ], // 21
  8507. [ 4, 144, 116, 4, 145, 117 ], [ 17, 68, 42 ], [ 17, 50, 22, 6, 51, 23 ], [ 19, 46, 16, 6, 47, 17 ], // 22
  8508. [ 2, 139, 111, 7, 140, 112 ], [ 17, 74, 46 ], [ 7, 54, 24, 16, 55, 25 ], [ 34, 37, 13 ], // 23
  8509. [ 4, 151, 121, 5, 152, 122 ], [ 4, 75, 47, 14, 76, 48 ], [ 11, 54, 24, 14, 55, 25 ], [ 16, 45, 15, 14, 46, 16 ], // 24
  8510. [ 6, 147, 117, 4, 148, 118 ], [ 6, 73, 45, 14, 74, 46 ], [ 11, 54, 24, 16, 55, 25 ], [ 30, 46, 16, 2, 47, 17 ], // 25
  8511. [ 8, 132, 106, 4, 133, 107 ], [ 8, 75, 47, 13, 76, 48 ], [ 7, 54, 24, 22, 55, 25 ], [ 22, 45, 15, 13, 46, 16 ], // 26
  8512. [ 10, 142, 114, 2, 143, 115 ], [ 19, 74, 46, 4, 75, 47 ], [ 28, 50, 22, 6, 51, 23 ], [ 33, 46, 16, 4, 47, 17 ], // 27
  8513. [ 8, 152, 122, 4, 153, 123 ], [ 22, 73, 45, 3, 74, 46 ], [ 8, 53, 23, 26, 54, 24 ], [ 12, 45, 15, 28, 46, 16 ], // 28
  8514. [ 3, 147, 117, 10, 148, 118 ], [ 3, 73, 45, 23, 74, 46 ], [ 4, 54, 24, 31, 55, 25 ], [ 11, 45, 15, 31, 46, 16 ], // 29
  8515. [ 7, 146, 116, 7, 147, 117 ], [ 21, 73, 45, 7, 74, 46 ], [ 1, 53, 23, 37, 54, 24 ], [ 19, 45, 15, 26, 46, 16 ], // 30
  8516. [ 5, 145, 115, 10, 146, 116 ], [ 19, 75, 47, 10, 76, 48 ], [ 15, 54, 24, 25, 55, 25 ], [ 23, 45, 15, 25, 46, 16 ], // 31
  8517. [ 13, 145, 115, 3, 146, 116 ], [ 2, 74, 46, 29, 75, 47 ], [ 42, 54, 24, 1, 55, 25 ], [ 23, 45, 15, 28, 46, 16 ], // 32
  8518. [ 17, 145, 115 ], [ 10, 74, 46, 23, 75, 47 ], [ 10, 54, 24, 35, 55, 25 ], [ 19, 45, 15, 35, 46, 16 ], // 33
  8519. [ 17, 145, 115, 1, 146, 116 ], [ 14, 74, 46, 21, 75, 47 ], [ 29, 54, 24, 19, 55, 25 ], [ 11, 45, 15, 46, 46, 16 ], // 34
  8520. [ 13, 145, 115, 6, 146, 116 ], [ 14, 74, 46, 23, 75, 47 ], [ 44, 54, 24, 7, 55, 25 ], [ 59, 46, 16, 1, 47, 17 ], // 35
  8521. [ 12, 151, 121, 7, 152, 122 ], [ 12, 75, 47, 26, 76, 48 ], [ 39, 54, 24, 14, 55, 25 ], [ 22, 45, 15, 41, 46, 16 ], // 36
  8522. [ 6, 151, 121, 14, 152, 122 ], [ 6, 75, 47, 34, 76, 48 ], [ 46, 54, 24, 10, 55, 25 ], [ 2, 45, 15, 64, 46, 16 ], // 37
  8523. [ 17, 152, 122, 4, 153, 123 ], [ 29, 74, 46, 14, 75, 47 ], [ 49, 54, 24, 10, 55, 25 ], [ 24, 45, 15, 46, 46, 16 ], // 38
  8524. [ 4, 152, 122, 18, 153, 123 ], [ 13, 74, 46, 32, 75, 47 ], [ 48, 54, 24, 14, 55, 25 ], [ 42, 45, 15, 32, 46, 16 ], // 39
  8525. [ 20, 147, 117, 4, 148, 118 ], [ 40, 75, 47, 7, 76, 48 ], [ 43, 54, 24, 22, 55, 25 ], [ 10, 45, 15, 67, 46, 16 ], // 40
  8526. [ 19, 148, 118, 6, 149, 119 ], [ 18, 75, 47, 31, 76, 48 ], [ 34, 54, 24, 34, 55, 25 ], [ 20, 45, 15, 61, 46, 16 ] ];
  8527. /**
  8528. * 根据数据获取对应版本
  8529. * @return {[type]} [description]
  8530. */
  8531. QRCodeAlg.prototype.getRightType = function() {
  8532. for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
  8533. var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
  8534. if (rsBlock == undefined) {
  8535. throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
  8536. }
  8537. var length = rsBlock.length / 3;
  8538. var totalDataCount = 0;
  8539. for (var i = 0; i < length; i++) {
  8540. var count = rsBlock[i * 3 + 0];
  8541. var dataCount = rsBlock[i * 3 + 2];
  8542. totalDataCount += dataCount * count;
  8543. }
  8544. var lengthBytes = typeNumber > 9 ? 2 : 1;
  8545. if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
  8546. this.typeNumber = typeNumber;
  8547. this.rsBlock = rsBlock;
  8548. this.totalDataCount = totalDataCount;
  8549. break;
  8550. }
  8551. }
  8552. };
  8553. //---------------------------------------------------------------------
  8554. // QRBitBuffer
  8555. //---------------------------------------------------------------------
  8556. function QRBitBuffer() {
  8557. this.buffer = new Array();
  8558. this.length = 0;
  8559. }
  8560. QRBitBuffer.prototype = {
  8561. get: function(index) {
  8562. var bufIndex = Math.floor(index / 8);
  8563. return this.buffer[bufIndex] >>> 7 - index % 8 & 1;
  8564. },
  8565. put: function(num, length) {
  8566. for (var i = 0; i < length; i++) {
  8567. this.putBit(num >>> length - i - 1 & 1);
  8568. }
  8569. },
  8570. putBit: function(bit) {
  8571. var bufIndex = Math.floor(this.length / 8);
  8572. if (this.buffer.length <= bufIndex) {
  8573. this.buffer.push(0);
  8574. }
  8575. if (bit) {
  8576. this.buffer[bufIndex] |= 128 >>> this.length % 8;
  8577. }
  8578. this.length++;
  8579. }
  8580. };
  8581. });
  8582. define("zepto.flexslider", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  8583. var $ = window.Zepto;
  8584. require("core");
  8585. require("zepto.extend.data");
  8586. // Zepto $.data patch
  8587. $.data = function(elem, key, value) {
  8588. return $(elem).data(key, value);
  8589. };
  8590. //scrollLeft
  8591. [ "Left", "Top" ].forEach(function(name, i) {
  8592. var method = "scroll" + name;
  8593. function isWindow(obj) {
  8594. return obj && typeof obj === "object" && "setInterval" in obj;
  8595. }
  8596. function getWindow(elem) {
  8597. return isWindow(elem) ? elem : elem.nodeType === 9 ? elem.defaultView || elem.parentWindow : false;
  8598. }
  8599. $.fn[method] = function(val) {
  8600. var elem, win;
  8601. if (val === undefined) {
  8602. elem = this[0];
  8603. if (!elem) {
  8604. return null;
  8605. }
  8606. win = getWindow(elem);
  8607. // Return the scroll offset
  8608. return win ? "pageXOffset" in win ? win[i ? "pageYOffset" : "pageXOffset"] : win.document.documentElement[method] || win.document.body[method] : elem[method];
  8609. }
  8610. // Set the scroll offset
  8611. this.each(function() {
  8612. win = getWindow(this);
  8613. if (win) {
  8614. var xCoord = !i ? val : $(win).scrollLeft();
  8615. var yCoord = i ? val : $(win).scrollTop();
  8616. win.scrollTo(xCoord, yCoord);
  8617. } else {
  8618. this[method] = val;
  8619. }
  8620. });
  8621. };
  8622. });
  8623. // Create outerHeight and outerWidth methods
  8624. [ "width", "height" ].forEach(function(dimension) {
  8625. var offset, Dimension = dimension.replace(/./, function(m) {
  8626. return m[0].toUpperCase();
  8627. });
  8628. $.fn["outer" + Dimension] = function(margin) {
  8629. var elem = this;
  8630. if (elem) {
  8631. var size = elem[dimension]();
  8632. var sides = {
  8633. width: [ "left", "right" ],
  8634. height: [ "top", "bottom" ]
  8635. };
  8636. sides[dimension].forEach(function(side) {
  8637. if (margin) size += parseInt(elem.css("margin-" + side), 10);
  8638. });
  8639. return size;
  8640. } else {
  8641. return null;
  8642. }
  8643. };
  8644. });
  8645. /*
  8646. * Zepto FlexSlider v2.3
  8647. * @desc Porting from jQuery FlexSlider v2.3, Contributing Author: Tyler Smith
  8648. * @license Copyright 2012 WooThemes GPLv2
  8649. */
  8650. //FlexSlider: Object Instance
  8651. $.flexslider = function(el, options) {
  8652. var slider = $(el);
  8653. // making variables public
  8654. slider.vars = $.extend({}, $.flexslider.defaults, options);
  8655. var namespace = slider.vars.namespace, msGesture = window.navigator && window.navigator.msPointerEnabled && window.MSGesture, touch = ("ontouchstart" in window || msGesture || window.DocumentTouch && document instanceof DocumentTouch) && slider.vars.touch, // depricating this idea, as devices are being released with both of these events
  8656. //eventType = (touch) ? "touchend" : "click",
  8657. eventType = "click touchend MSPointerUp keyup", watchedEvent = "", watchedEventClearTimer, vertical = slider.vars.direction === "vertical", reverse = slider.vars.reverse, carousel = slider.vars.itemWidth > 0, fade = slider.vars.animation === "fade", asNav = slider.vars.asNavFor !== "", methods = {}, focused = true;
  8658. // Store a reference to the slider object
  8659. $.data(el, "flexslider", slider);
  8660. // Private slider methods
  8661. methods = {
  8662. init: function() {
  8663. slider.animating = false;
  8664. // Get current slide and make sure it is a number
  8665. slider.currentSlide = parseInt(slider.vars.startAt ? slider.vars.startAt : 0, 10);
  8666. if (isNaN(slider.currentSlide)) slider.currentSlide = 0;
  8667. slider.animatingTo = slider.currentSlide;
  8668. slider.atEnd = slider.currentSlide === 0 || slider.currentSlide === slider.last;
  8669. slider.containerSelector = slider.vars.selector.substr(0, slider.vars.selector.search(" "));
  8670. slider.slides = $(slider.vars.selector, slider);
  8671. slider.container = $(slider.containerSelector, slider);
  8672. slider.count = slider.slides.length;
  8673. // SYNC:
  8674. slider.syncExists = $(slider.vars.sync).length > 0;
  8675. // SLIDE:
  8676. if (slider.vars.animation === "slide") slider.vars.animation = "swing";
  8677. slider.prop = vertical ? "top" : "marginLeft";
  8678. slider.args = {};
  8679. // SLIDESHOW:
  8680. slider.manualPause = false;
  8681. slider.stopped = false;
  8682. //PAUSE WHEN INVISIBLE
  8683. slider.started = false;
  8684. slider.startTimeout = null;
  8685. // TOUCH/USECSS:
  8686. slider.transitions = !slider.vars.video && !fade && slider.vars.useCSS && function() {
  8687. var obj = document.createElement("div"), props = [ "perspectiveProperty", "WebkitPerspective", "MozPerspective", "OPerspective", "msPerspective" ];
  8688. for (var i in props) {
  8689. if (obj.style[props[i]] !== undefined) {
  8690. slider.pfx = props[i].replace("Perspective", "").toLowerCase();
  8691. slider.prop = "-" + slider.pfx + "-transform";
  8692. return true;
  8693. }
  8694. }
  8695. return false;
  8696. }();
  8697. slider.ensureAnimationEnd = "";
  8698. // CONTROLSCONTAINER:
  8699. if (slider.vars.controlsContainer !== "") slider.controlsContainer = $(slider.vars.controlsContainer).length > 0 && $(slider.vars.controlsContainer);
  8700. // MANUAL:
  8701. if (slider.vars.manualControls !== "") slider.manualControls = $(slider.vars.manualControls).length > 0 && $(slider.vars.manualControls);
  8702. // RANDOMIZE:
  8703. if (slider.vars.randomize) {
  8704. slider.slides.sort(function() {
  8705. return Math.round(Math.random()) - .5;
  8706. });
  8707. slider.container.empty().append(slider.slides);
  8708. }
  8709. slider.doMath();
  8710. // INIT
  8711. slider.setup("init");
  8712. // CONTROLNAV:
  8713. if (slider.vars.controlNav) methods.controlNav.setup();
  8714. // DIRECTIONNAV:
  8715. if (slider.vars.directionNav) methods.directionNav.setup();
  8716. // KEYBOARD:
  8717. if (slider.vars.keyboard && ($(slider.containerSelector).length === 1 || slider.vars.multipleKeyboard)) {
  8718. $(document).bind("keyup", function(event) {
  8719. var keycode = event.keyCode;
  8720. if (!slider.animating && (keycode === 39 || keycode === 37)) {
  8721. var target = keycode === 39 ? slider.getTarget("next") : keycode === 37 ? slider.getTarget("prev") : false;
  8722. slider.flexAnimate(target, slider.vars.pauseOnAction);
  8723. }
  8724. });
  8725. }
  8726. // MOUSEWHEEL:
  8727. if (slider.vars.mousewheel) {
  8728. slider.bind("mousewheel", function(event, delta, deltaX, deltaY) {
  8729. event.preventDefault();
  8730. var target = delta < 0 ? slider.getTarget("next") : slider.getTarget("prev");
  8731. slider.flexAnimate(target, slider.vars.pauseOnAction);
  8732. });
  8733. }
  8734. // PAUSEPLAY
  8735. if (slider.vars.pausePlay) methods.pausePlay.setup();
  8736. //PAUSE WHEN INVISIBLE
  8737. if (slider.vars.slideshow && slider.vars.pauseInvisible) methods.pauseInvisible.init();
  8738. // SLIDSESHOW
  8739. if (slider.vars.slideshow) {
  8740. if (slider.vars.pauseOnHover) {
  8741. /*slider.hover(function() {
  8742. if (!slider.manualPlay && !slider.manualPause) slider.pause();
  8743. }, function() {
  8744. if (!slider.manualPause && !slider.manualPlay && !slider.stopped) slider.play();
  8745. });*/
  8746. slider.on("mouseover", function() {
  8747. if (!slider.manualPlay && !slider.manualPause) slider.pause();
  8748. });
  8749. slider.on("mouseout", function() {
  8750. if (!slider.manualPause && !slider.manualPlay && !slider.stopped) slider.play();
  8751. });
  8752. }
  8753. // initialize animation
  8754. //If we're visible, or we don't use PageVisibility API
  8755. if (!slider.vars.pauseInvisible || !methods.pauseInvisible.isHidden()) {
  8756. slider.vars.initDelay > 0 ? slider.startTimeout = setTimeout(slider.play, slider.vars.initDelay) : slider.play();
  8757. }
  8758. }
  8759. // ASNAV:
  8760. if (asNav) methods.asNav.setup();
  8761. // TOUCH
  8762. if (touch && slider.vars.touch) methods.touch();
  8763. // FADE&&SMOOTHHEIGHT || SLIDE:
  8764. if (!fade || fade && slider.vars.smoothHeight) $(window).bind("resize orientationchange focus", methods.resize);
  8765. slider.find("img").attr("draggable", "false");
  8766. // API: start() Callback
  8767. setTimeout(function() {
  8768. slider.vars.start(slider);
  8769. }, 200);
  8770. },
  8771. asNav: {
  8772. setup: function() {
  8773. slider.asNav = true;
  8774. slider.animatingTo = Math.floor(slider.currentSlide / slider.move);
  8775. slider.currentItem = slider.currentSlide;
  8776. slider.slides.removeClass(namespace + "active-slide").eq(slider.currentItem).addClass(namespace + "active-slide");
  8777. if (!msGesture) {
  8778. slider.slides.on(eventType, function(e) {
  8779. e.preventDefault();
  8780. var $slide = $(this), target = $slide.index();
  8781. var posFromLeft = $slide.offset().left - $(slider).scrollLeft();
  8782. // Find position of slide relative to left of slider container
  8783. if (posFromLeft <= 0 && $slide.hasClass(namespace + "active-slide")) {
  8784. slider.flexAnimate(slider.getTarget("prev"), true);
  8785. } else if (!$(slider.vars.asNavFor).data("flexslider").animating && !$slide.hasClass(namespace + "active-slide")) {
  8786. slider.direction = slider.currentItem < target ? "next" : "prev";
  8787. slider.flexAnimate(target, slider.vars.pauseOnAction, false, true, true);
  8788. }
  8789. });
  8790. } else {
  8791. el._slider = slider;
  8792. slider.slides.each(function() {
  8793. var that = this;
  8794. that._gesture = new MSGesture();
  8795. that._gesture.target = that;
  8796. that.addEventListener("MSPointerDown", function(e) {
  8797. e.preventDefault();
  8798. if (e.currentTarget._gesture) e.currentTarget._gesture.addPointer(e.pointerId);
  8799. }, false);
  8800. that.addEventListener("MSGestureTap", function(e) {
  8801. e.preventDefault();
  8802. var $slide = $(this), target = $slide.index();
  8803. if (!$(slider.vars.asNavFor).data("flexslider").animating && !$slide.hasClass("active")) {
  8804. slider.direction = slider.currentItem < target ? "next" : "prev";
  8805. slider.flexAnimate(target, slider.vars.pauseOnAction, false, true, true);
  8806. }
  8807. });
  8808. });
  8809. }
  8810. }
  8811. },
  8812. controlNav: {
  8813. setup: function() {
  8814. if (!slider.manualControls) {
  8815. methods.controlNav.setupPaging();
  8816. } else {
  8817. // MANUALCONTROLS:
  8818. methods.controlNav.setupManual();
  8819. }
  8820. },
  8821. setupPaging: function() {
  8822. var type = slider.vars.controlNav === "thumbnails" ? "control-thumbs" : "control-paging", j = 1, item, slide;
  8823. slider.controlNavScaffold = $('<ol class="' + namespace + "control-nav " + namespace + type + '"></ol>');
  8824. if (slider.pagingCount > 1) {
  8825. for (var i = 0; i < slider.pagingCount; i++) {
  8826. slide = slider.slides.eq(i);
  8827. item = slider.vars.controlNav === "thumbnails" ? '<img src="' + slide.attr("data-thumb") + '"/>' : "<a>" + j + "</a>";
  8828. if ("thumbnails" === slider.vars.controlNav && true === slider.vars.thumbCaptions) {
  8829. var captn = slide.attr("data-thumbcaption");
  8830. if ("" != captn && undefined != captn) item += '<span class="' + namespace + 'caption">' + captn + "</span>";
  8831. }
  8832. //slider.controlNavScaffold.append('<li>' + item + '</li>');
  8833. slider.controlNavScaffold.append("<li>" + item + "<i></i></li>");
  8834. j++;
  8835. }
  8836. }
  8837. // CONTROLSCONTAINER:
  8838. slider.controlsContainer ? $(slider.controlsContainer).append(slider.controlNavScaffold) : slider.append(slider.controlNavScaffold);
  8839. methods.controlNav.set();
  8840. methods.controlNav.active();
  8841. slider.controlNavScaffold.delegate("a, img", eventType, function(event) {
  8842. event.preventDefault();
  8843. if (watchedEvent === "" || watchedEvent === event.type) {
  8844. var $this = $(this), target = slider.controlNav.index($this);
  8845. if (!$this.hasClass(namespace + "active")) {
  8846. slider.direction = target > slider.currentSlide ? "next" : "prev";
  8847. slider.flexAnimate(target, slider.vars.pauseOnAction);
  8848. }
  8849. }
  8850. // setup flags to prevent event duplication
  8851. if (watchedEvent === "") {
  8852. watchedEvent = event.type;
  8853. }
  8854. methods.setToClearWatchedEvent();
  8855. });
  8856. },
  8857. setupManual: function() {
  8858. slider.controlNav = slider.manualControls;
  8859. methods.controlNav.active();
  8860. slider.controlNav.bind(eventType, function(event) {
  8861. event.preventDefault();
  8862. if (watchedEvent === "" || watchedEvent === event.type) {
  8863. var $this = $(this), target = slider.controlNav.index($this);
  8864. if (!$this.hasClass(namespace + "active")) {
  8865. target > slider.currentSlide ? slider.direction = "next" : slider.direction = "prev";
  8866. slider.flexAnimate(target, slider.vars.pauseOnAction);
  8867. }
  8868. }
  8869. // setup flags to prevent event duplication
  8870. if (watchedEvent === "") {
  8871. watchedEvent = event.type;
  8872. }
  8873. methods.setToClearWatchedEvent();
  8874. });
  8875. },
  8876. set: function() {
  8877. var selector = slider.vars.controlNav === "thumbnails" ? "img" : "a";
  8878. slider.controlNav = $("." + namespace + "control-nav li " + selector, slider.controlsContainer ? slider.controlsContainer : slider);
  8879. },
  8880. active: function() {
  8881. slider.controlNav.removeClass(namespace + "active").eq(slider.animatingTo).addClass(namespace + "active");
  8882. },
  8883. update: function(action, pos) {
  8884. if (slider.pagingCount > 1 && action === "add") {
  8885. slider.controlNavScaffold.append($("<li><a>" + slider.count + "</a></li>"));
  8886. } else if (slider.pagingCount === 1) {
  8887. slider.controlNavScaffold.find("li").remove();
  8888. } else {
  8889. slider.controlNav.eq(pos).closest("li").remove();
  8890. }
  8891. methods.controlNav.set();
  8892. slider.pagingCount > 1 && slider.pagingCount !== slider.controlNav.length ? slider.update(pos, action) : methods.controlNav.active();
  8893. }
  8894. },
  8895. directionNav: {
  8896. setup: function() {
  8897. var directionNavScaffold = $('<ul class="' + namespace + 'direction-nav"><li><a class="' + namespace + 'prev" href="#">' + slider.vars.prevText + '</a></li><li><a class="' + namespace + 'next" href="#">' + slider.vars.nextText + "</a></li></ul>");
  8898. // CONTROLSCONTAINER:
  8899. if (slider.controlsContainer) {
  8900. $(slider.controlsContainer).append(directionNavScaffold);
  8901. slider.directionNav = $("." + namespace + "direction-nav li a", slider.controlsContainer);
  8902. } else {
  8903. slider.append(directionNavScaffold);
  8904. slider.directionNav = $("." + namespace + "direction-nav li a", slider);
  8905. }
  8906. methods.directionNav.update();
  8907. slider.directionNav.bind(eventType, function(event) {
  8908. event.preventDefault();
  8909. var target;
  8910. if (watchedEvent === "" || watchedEvent === event.type) {
  8911. target = $(this).hasClass(namespace + "next") ? slider.getTarget("next") : slider.getTarget("prev");
  8912. slider.flexAnimate(target, slider.vars.pauseOnAction);
  8913. }
  8914. // setup flags to prevent event duplication
  8915. if (watchedEvent === "") {
  8916. watchedEvent = event.type;
  8917. }
  8918. methods.setToClearWatchedEvent();
  8919. });
  8920. },
  8921. update: function() {
  8922. var disabledClass = namespace + "disabled";
  8923. if (slider.pagingCount === 1) {
  8924. slider.directionNav.addClass(disabledClass).attr("tabindex", "-1");
  8925. } else if (!slider.vars.animationLoop) {
  8926. if (slider.animatingTo === 0) {
  8927. slider.directionNav.removeClass(disabledClass).filter("." + namespace + "prev").addClass(disabledClass).attr("tabindex", "-1");
  8928. } else if (slider.animatingTo === slider.last) {
  8929. slider.directionNav.removeClass(disabledClass).filter("." + namespace + "next").addClass(disabledClass).attr("tabindex", "-1");
  8930. } else {
  8931. slider.directionNav.removeClass(disabledClass).removeAttr("tabindex");
  8932. }
  8933. } else {
  8934. slider.directionNav.removeClass(disabledClass).removeAttr("tabindex");
  8935. }
  8936. }
  8937. },
  8938. pausePlay: {
  8939. setup: function() {
  8940. var pausePlayScaffold = $('<div class="' + namespace + 'pauseplay"><a></a></div>');
  8941. // CONTROLSCONTAINER:
  8942. if (slider.controlsContainer) {
  8943. slider.controlsContainer.append(pausePlayScaffold);
  8944. slider.pausePlay = $("." + namespace + "pauseplay a", slider.controlsContainer);
  8945. } else {
  8946. slider.append(pausePlayScaffold);
  8947. slider.pausePlay = $("." + namespace + "pauseplay a", slider);
  8948. }
  8949. methods.pausePlay.update(slider.vars.slideshow ? namespace + "pause" : namespace + "play");
  8950. slider.pausePlay.bind(eventType, function(event) {
  8951. event.preventDefault();
  8952. if (watchedEvent === "" || watchedEvent === event.type) {
  8953. if ($(this).hasClass(namespace + "pause")) {
  8954. slider.manualPause = true;
  8955. slider.manualPlay = false;
  8956. slider.pause();
  8957. } else {
  8958. slider.manualPause = false;
  8959. slider.manualPlay = true;
  8960. slider.play();
  8961. }
  8962. }
  8963. // setup flags to prevent event duplication
  8964. if (watchedEvent === "") {
  8965. watchedEvent = event.type;
  8966. }
  8967. methods.setToClearWatchedEvent();
  8968. });
  8969. },
  8970. update: function(state) {
  8971. state === "play" ? slider.pausePlay.removeClass(namespace + "pause").addClass(namespace + "play").html(slider.vars.playText) : slider.pausePlay.removeClass(namespace + "play").addClass(namespace + "pause").html(slider.vars.pauseText);
  8972. }
  8973. },
  8974. touch: function() {
  8975. var startX, startY, offset, cwidth, dx, startT, scrolling = false, localX = 0, localY = 0, accDx = 0;
  8976. if (!msGesture) {
  8977. el.addEventListener("touchstart", onTouchStart, false);
  8978. function onTouchStart(e) {
  8979. if (slider.animating) {
  8980. e.preventDefault();
  8981. } else if (window.navigator.msPointerEnabled || e.touches.length === 1) {
  8982. slider.pause();
  8983. // CAROUSEL:
  8984. cwidth = vertical ? slider.h : slider.w;
  8985. startT = Number(new Date());
  8986. // CAROUSEL:
  8987. // Local vars for X and Y points.
  8988. localX = e.touches[0].pageX;
  8989. localY = e.touches[0].pageY;
  8990. offset = carousel && reverse && slider.animatingTo === slider.last ? 0 : carousel && reverse ? slider.limit - (slider.itemW + slider.vars.itemMargin) * slider.move * slider.animatingTo : carousel && slider.currentSlide === slider.last ? slider.limit : carousel ? (slider.itemW + slider.vars.itemMargin) * slider.move * slider.currentSlide : reverse ? (slider.last - slider.currentSlide + slider.cloneOffset) * cwidth : (slider.currentSlide + slider.cloneOffset) * cwidth;
  8991. startX = vertical ? localY : localX;
  8992. startY = vertical ? localX : localY;
  8993. el.addEventListener("touchmove", onTouchMove, false);
  8994. el.addEventListener("touchend", onTouchEnd, false);
  8995. }
  8996. }
  8997. function onTouchMove(e) {
  8998. // Local vars for X and Y points.
  8999. localX = e.touches[0].pageX;
  9000. localY = e.touches[0].pageY;
  9001. dx = vertical ? startX - localY : startX - localX;
  9002. scrolling = vertical ? Math.abs(dx) < Math.abs(localX - startY) : Math.abs(dx) < Math.abs(localY - startY);
  9003. var fxms = 500;
  9004. if (!scrolling || Number(new Date()) - startT > fxms) {
  9005. e.preventDefault();
  9006. if (!fade && slider.transitions) {
  9007. if (!slider.vars.animationLoop) {
  9008. dx = dx / (slider.currentSlide === 0 && dx < 0 || slider.currentSlide === slider.last && dx > 0 ? Math.abs(dx) / cwidth + 2 : 1);
  9009. }
  9010. slider.setProps(offset + dx, "setTouch");
  9011. }
  9012. }
  9013. }
  9014. function onTouchEnd(e) {
  9015. // finish the touch by undoing the touch session
  9016. el.removeEventListener("touchmove", onTouchMove, false);
  9017. if (slider.animatingTo === slider.currentSlide && !scrolling && !(dx === null)) {
  9018. var updateDx = reverse ? -dx : dx, target = updateDx > 0 ? slider.getTarget("next") : slider.getTarget("prev");
  9019. if (slider.canAdvance(target) && (Number(new Date()) - startT < 550 && Math.abs(updateDx) > 50 || Math.abs(updateDx) > cwidth / 2)) {
  9020. slider.flexAnimate(target, slider.vars.pauseOnAction);
  9021. } else {
  9022. if (!fade) slider.flexAnimate(slider.currentSlide, slider.vars.pauseOnAction, true);
  9023. }
  9024. }
  9025. el.removeEventListener("touchend", onTouchEnd, false);
  9026. startX = null;
  9027. startY = null;
  9028. dx = null;
  9029. offset = null;
  9030. }
  9031. } else {
  9032. el.style.msTouchAction = "none";
  9033. el._gesture = new MSGesture();
  9034. el._gesture.target = el;
  9035. el.addEventListener("MSPointerDown", onMSPointerDown, false);
  9036. el._slider = slider;
  9037. el.addEventListener("MSGestureChange", onMSGestureChange, false);
  9038. el.addEventListener("MSGestureEnd", onMSGestureEnd, false);
  9039. function onMSPointerDown(e) {
  9040. e.stopPropagation();
  9041. if (slider.animating) {
  9042. e.preventDefault();
  9043. } else {
  9044. slider.pause();
  9045. el._gesture.addPointer(e.pointerId);
  9046. accDx = 0;
  9047. cwidth = vertical ? slider.h : slider.w;
  9048. startT = Number(new Date());
  9049. // CAROUSEL:
  9050. offset = carousel && reverse && slider.animatingTo === slider.last ? 0 : carousel && reverse ? slider.limit - (slider.itemW + slider.vars.itemMargin) * slider.move * slider.animatingTo : carousel && slider.currentSlide === slider.last ? slider.limit : carousel ? (slider.itemW + slider.vars.itemMargin) * slider.move * slider.currentSlide : reverse ? (slider.last - slider.currentSlide + slider.cloneOffset) * cwidth : (slider.currentSlide + slider.cloneOffset) * cwidth;
  9051. }
  9052. }
  9053. function onMSGestureChange(e) {
  9054. e.stopPropagation();
  9055. var slider = e.target._slider;
  9056. if (!slider) {
  9057. return;
  9058. }
  9059. var transX = -e.translationX, transY = -e.translationY;
  9060. //Accumulate translations.
  9061. accDx = accDx + (vertical ? transY : transX);
  9062. dx = accDx;
  9063. scrolling = vertical ? Math.abs(accDx) < Math.abs(-transX) : Math.abs(accDx) < Math.abs(-transY);
  9064. if (e.detail === e.MSGESTURE_FLAG_INERTIA) {
  9065. setImmediate(function() {
  9066. el._gesture.stop();
  9067. });
  9068. return;
  9069. }
  9070. if (!scrolling || Number(new Date()) - startT > 500) {
  9071. e.preventDefault();
  9072. if (!fade && slider.transitions) {
  9073. if (!slider.vars.animationLoop) {
  9074. dx = accDx / (slider.currentSlide === 0 && accDx < 0 || slider.currentSlide === slider.last && accDx > 0 ? Math.abs(accDx) / cwidth + 2 : 1);
  9075. }
  9076. slider.setProps(offset + dx, "setTouch");
  9077. }
  9078. }
  9079. }
  9080. function onMSGestureEnd(e) {
  9081. e.stopPropagation();
  9082. var slider = e.target._slider;
  9083. if (!slider) {
  9084. return;
  9085. }
  9086. if (slider.animatingTo === slider.currentSlide && !scrolling && !(dx === null)) {
  9087. var updateDx = reverse ? -dx : dx, target = updateDx > 0 ? slider.getTarget("next") : slider.getTarget("prev");
  9088. if (slider.canAdvance(target) && (Number(new Date()) - startT < 550 && Math.abs(updateDx) > 50 || Math.abs(updateDx) > cwidth / 2)) {
  9089. slider.flexAnimate(target, slider.vars.pauseOnAction);
  9090. } else {
  9091. if (!fade) slider.flexAnimate(slider.currentSlide, slider.vars.pauseOnAction, true);
  9092. }
  9093. }
  9094. startX = null;
  9095. startY = null;
  9096. dx = null;
  9097. offset = null;
  9098. accDx = 0;
  9099. }
  9100. }
  9101. },
  9102. resize: function() {
  9103. if (!slider.animating && slider.is(":visible")) {
  9104. if (!carousel) slider.doMath();
  9105. if (fade) {
  9106. // SMOOTH HEIGHT:
  9107. methods.smoothHeight();
  9108. } else if (carousel) {
  9109. //CAROUSEL:
  9110. slider.slides.width(slider.computedW);
  9111. slider.update(slider.pagingCount);
  9112. slider.setProps();
  9113. } else if (vertical) {
  9114. //VERTICAL:
  9115. slider.viewport.height(slider.h);
  9116. slider.setProps(slider.h, "setTotal");
  9117. } else {
  9118. // SMOOTH HEIGHT:
  9119. if (slider.vars.smoothHeight) methods.smoothHeight();
  9120. slider.newSlides.width(slider.computedW);
  9121. slider.setProps(slider.computedW, "setTotal");
  9122. }
  9123. }
  9124. },
  9125. smoothHeight: function(dur) {
  9126. if (!vertical || fade) {
  9127. var $obj = fade ? slider : slider.viewport;
  9128. dur ? $obj.animate({
  9129. height: slider.slides.eq(slider.animatingTo).height()
  9130. }, dur) : $obj.height(slider.slides.eq(slider.animatingTo).height());
  9131. }
  9132. },
  9133. sync: function(action) {
  9134. var $obj = $(slider.vars.sync).data("flexslider"), target = slider.animatingTo;
  9135. switch (action) {
  9136. case "animate":
  9137. $obj.flexAnimate(target, slider.vars.pauseOnAction, false, true);
  9138. break;
  9139. case "play":
  9140. if (!$obj.playing && !$obj.asNav) {
  9141. $obj.play();
  9142. }
  9143. break;
  9144. case "pause":
  9145. $obj.pause();
  9146. break;
  9147. }
  9148. },
  9149. uniqueID: function($clone) {
  9150. // Append _clone to current level and children elements with id attributes
  9151. $clone.filter("[id]").add($clone.find("[id]")).each(function() {
  9152. var $this = $(this);
  9153. $this.attr("id", $this.attr("id") + "_clone");
  9154. });
  9155. return $clone;
  9156. },
  9157. pauseInvisible: {
  9158. visProp: null,
  9159. init: function() {
  9160. var prefixes = [ "webkit", "moz", "ms", "o" ];
  9161. if ("hidden" in document) return "hidden";
  9162. for (var i = 0; i < prefixes.length; i++) {
  9163. if (prefixes[i] + "Hidden" in document) methods.pauseInvisible.visProp = prefixes[i] + "Hidden";
  9164. }
  9165. if (methods.pauseInvisible.visProp) {
  9166. var evtname = methods.pauseInvisible.visProp.replace(/[H|h]idden/, "") + "visibilitychange";
  9167. document.addEventListener(evtname, function() {
  9168. if (methods.pauseInvisible.isHidden()) {
  9169. if (slider.startTimeout) clearTimeout(slider.startTimeout); else slider.pause();
  9170. } else {
  9171. if (slider.started) slider.play(); else slider.vars.initDelay > 0 ? setTimeout(slider.play, slider.vars.initDelay) : slider.play();
  9172. }
  9173. });
  9174. }
  9175. },
  9176. isHidden: function() {
  9177. return document[methods.pauseInvisible.visProp] || false;
  9178. }
  9179. },
  9180. setToClearWatchedEvent: function() {
  9181. clearTimeout(watchedEventClearTimer);
  9182. watchedEventClearTimer = setTimeout(function() {
  9183. watchedEvent = "";
  9184. }, 3e3);
  9185. }
  9186. };
  9187. // public methods
  9188. slider.flexAnimate = function(target, pause, override, withSync, fromNav) {
  9189. if (!slider.vars.animationLoop && target !== slider.currentSlide) {
  9190. slider.direction = target > slider.currentSlide ? "next" : "prev";
  9191. }
  9192. if (asNav && slider.pagingCount === 1) slider.direction = slider.currentItem < target ? "next" : "prev";
  9193. if (!slider.animating && (slider.canAdvance(target, fromNav) || override) && slider.is(":visible")) {
  9194. if (asNav && withSync) {
  9195. var master = $(slider.vars.asNavFor).data("flexslider");
  9196. slider.atEnd = target === 0 || target === slider.count - 1;
  9197. master.flexAnimate(target, true, false, true, fromNav);
  9198. slider.direction = slider.currentItem < target ? "next" : "prev";
  9199. master.direction = slider.direction;
  9200. if (Math.ceil((target + 1) / slider.visible) - 1 !== slider.currentSlide && target !== 0) {
  9201. slider.currentItem = target;
  9202. slider.slides.removeClass(namespace + "active-slide").eq(target).addClass(namespace + "active-slide");
  9203. target = Math.floor(target / slider.visible);
  9204. } else {
  9205. slider.currentItem = target;
  9206. slider.slides.removeClass(namespace + "active-slide").eq(target).addClass(namespace + "active-slide");
  9207. return false;
  9208. }
  9209. }
  9210. slider.animating = true;
  9211. slider.animatingTo = target;
  9212. // SLIDESHOW:
  9213. if (pause) slider.pause();
  9214. // API: before() animation Callback
  9215. slider.vars.before(slider);
  9216. // SYNC:
  9217. if (slider.syncExists && !fromNav) methods.sync("animate");
  9218. // CONTROLNAV
  9219. if (slider.vars.controlNav) methods.controlNav.active();
  9220. // !CAROUSEL:
  9221. // CANDIDATE: slide active class (for add/remove slide)
  9222. if (!carousel) slider.slides.removeClass(namespace + "active-slide").eq(target).addClass(namespace + "active-slide");
  9223. // INFINITE LOOP:
  9224. // CANDIDATE: atEnd
  9225. slider.atEnd = target === 0 || target === slider.last;
  9226. // DIRECTIONNAV:
  9227. if (slider.vars.directionNav) methods.directionNav.update();
  9228. if (target === slider.last) {
  9229. // API: end() of cycle Callback
  9230. slider.vars.end(slider);
  9231. // SLIDESHOW && !INFINITE LOOP:
  9232. if (!slider.vars.animationLoop) slider.pause();
  9233. }
  9234. // SLIDE:
  9235. if (!fade) {
  9236. var dimension = vertical ? slider.slides.filter(":first").height() : slider.computedW, margin, slideString, calcNext;
  9237. // INFINITE LOOP / REVERSE:
  9238. if (carousel) {
  9239. //margin = (slider.vars.itemWidth > slider.w) ? slider.vars.itemMargin * 2 : slider.vars.itemMargin;
  9240. margin = slider.vars.itemMargin;
  9241. calcNext = (slider.itemW + margin) * slider.move * slider.animatingTo;
  9242. slideString = calcNext > slider.limit && slider.visible !== 1 ? slider.limit : calcNext;
  9243. } else if (slider.currentSlide === 0 && target === slider.count - 1 && slider.vars.animationLoop && slider.direction !== "next") {
  9244. slideString = reverse ? (slider.count + slider.cloneOffset) * dimension : 0;
  9245. } else if (slider.currentSlide === slider.last && target === 0 && slider.vars.animationLoop && slider.direction !== "prev") {
  9246. slideString = reverse ? 0 : (slider.count + 1) * dimension;
  9247. } else {
  9248. slideString = reverse ? (slider.count - 1 - target + slider.cloneOffset) * dimension : (target + slider.cloneOffset) * dimension;
  9249. }
  9250. slider.setProps(slideString, "", slider.vars.animationSpeed);
  9251. if (slider.transitions) {
  9252. if (!slider.vars.animationLoop || !slider.atEnd) {
  9253. slider.animating = false;
  9254. slider.currentSlide = slider.animatingTo;
  9255. }
  9256. // Unbind previous transitionEnd events and re-bind new transitionEnd event
  9257. slider.container.unbind("webkitTransitionEnd transitionend");
  9258. slider.container.bind("webkitTransitionEnd transitionend", function() {
  9259. clearTimeout(slider.ensureAnimationEnd);
  9260. slider.wrapup(dimension);
  9261. });
  9262. // Insurance for the ever-so-fickle transitionEnd event
  9263. clearTimeout(slider.ensureAnimationEnd);
  9264. slider.ensureAnimationEnd = setTimeout(function() {
  9265. slider.wrapup(dimension);
  9266. }, slider.vars.animationSpeed + 100);
  9267. } else {
  9268. slider.container.animate(slider.args, slider.vars.animationSpeed, slider.vars.easing, function() {
  9269. slider.wrapup(dimension);
  9270. });
  9271. }
  9272. } else {
  9273. // FADE:
  9274. if (!touch) {
  9275. //slider.slides.eq(slider.currentSlide).fadeOut(slider.vars.animationSpeed, slider.vars.easing);
  9276. //slider.slides.eq(target).fadeIn(slider.vars.animationSpeed, slider.vars.easing, slider.wrapup);
  9277. slider.slides.eq(slider.currentSlide).css({
  9278. zIndex: 1
  9279. }).animate({
  9280. opacity: 0
  9281. }, slider.vars.animationSpeed, slider.vars.easing);
  9282. slider.slides.eq(target).css({
  9283. zIndex: 2
  9284. }).animate({
  9285. opacity: 1
  9286. }, slider.vars.animationSpeed, slider.vars.easing, slider.wrapup);
  9287. } else {
  9288. slider.slides.eq(slider.currentSlide).css({
  9289. opacity: 0,
  9290. zIndex: 1
  9291. });
  9292. slider.slides.eq(target).css({
  9293. opacity: 1,
  9294. zIndex: 2
  9295. });
  9296. slider.wrapup(dimension);
  9297. }
  9298. }
  9299. // SMOOTH HEIGHT:
  9300. if (slider.vars.smoothHeight) methods.smoothHeight(slider.vars.animationSpeed);
  9301. }
  9302. };
  9303. slider.wrapup = function(dimension) {
  9304. // SLIDE:
  9305. if (!fade && !carousel) {
  9306. if (slider.currentSlide === 0 && slider.animatingTo === slider.last && slider.vars.animationLoop) {
  9307. slider.setProps(dimension, "jumpEnd");
  9308. } else if (slider.currentSlide === slider.last && slider.animatingTo === 0 && slider.vars.animationLoop) {
  9309. slider.setProps(dimension, "jumpStart");
  9310. }
  9311. }
  9312. slider.animating = false;
  9313. slider.currentSlide = slider.animatingTo;
  9314. // API: after() animation Callback
  9315. slider.vars.after(slider);
  9316. };
  9317. // SLIDESHOW:
  9318. slider.animateSlides = function() {
  9319. if (!slider.animating && focused) slider.flexAnimate(slider.getTarget("next"));
  9320. };
  9321. // SLIDESHOW:
  9322. slider.pause = function() {
  9323. clearInterval(slider.animatedSlides);
  9324. slider.animatedSlides = null;
  9325. slider.playing = false;
  9326. // PAUSEPLAY:
  9327. if (slider.vars.pausePlay) methods.pausePlay.update("play");
  9328. // SYNC:
  9329. if (slider.syncExists) methods.sync("pause");
  9330. };
  9331. // SLIDESHOW:
  9332. slider.play = function() {
  9333. if (slider.playing) clearInterval(slider.animatedSlides);
  9334. slider.animatedSlides = slider.animatedSlides || setInterval(slider.animateSlides, slider.vars.slideshowSpeed);
  9335. slider.started = slider.playing = true;
  9336. // PAUSEPLAY:
  9337. if (slider.vars.pausePlay) methods.pausePlay.update("pause");
  9338. // SYNC:
  9339. if (slider.syncExists) methods.sync("play");
  9340. };
  9341. // STOP:
  9342. slider.stop = function() {
  9343. slider.pause();
  9344. slider.stopped = true;
  9345. };
  9346. slider.canAdvance = function(target, fromNav) {
  9347. // ASNAV:
  9348. var last = asNav ? slider.pagingCount - 1 : slider.last;
  9349. return fromNav ? true : asNav && slider.currentItem === slider.count - 1 && target === 0 && slider.direction === "prev" ? true : asNav && slider.currentItem === 0 && target === slider.pagingCount - 1 && slider.direction !== "next" ? false : target === slider.currentSlide && !asNav ? false : slider.vars.animationLoop ? true : slider.atEnd && slider.currentSlide === 0 && target === last && slider.direction !== "next" ? false : slider.atEnd && slider.currentSlide === last && target === 0 && slider.direction === "next" ? false : true;
  9350. };
  9351. slider.getTarget = function(dir) {
  9352. slider.direction = dir;
  9353. if (dir === "next") {
  9354. return slider.currentSlide === slider.last ? 0 : slider.currentSlide + 1;
  9355. } else {
  9356. return slider.currentSlide === 0 ? slider.last : slider.currentSlide - 1;
  9357. }
  9358. };
  9359. // SLIDE:
  9360. slider.setProps = function(pos, special, dur) {
  9361. var target = function() {
  9362. var posCheck = pos ? pos : (slider.itemW + slider.vars.itemMargin) * slider.move * slider.animatingTo, posCalc = function() {
  9363. if (carousel) {
  9364. return special === "setTouch" ? pos : reverse && slider.animatingTo === slider.last ? 0 : reverse ? slider.limit - (slider.itemW + slider.vars.itemMargin) * slider.move * slider.animatingTo : slider.animatingTo === slider.last ? slider.limit : posCheck;
  9365. } else {
  9366. switch (special) {
  9367. case "setTotal":
  9368. return reverse ? (slider.count - 1 - slider.currentSlide + slider.cloneOffset) * pos : (slider.currentSlide + slider.cloneOffset) * pos;
  9369. case "setTouch":
  9370. return reverse ? pos : pos;
  9371. case "jumpEnd":
  9372. return reverse ? pos : slider.count * pos;
  9373. case "jumpStart":
  9374. return reverse ? slider.count * pos : pos;
  9375. default:
  9376. return pos;
  9377. }
  9378. }
  9379. }();
  9380. return posCalc * -1 + "px";
  9381. }();
  9382. if (slider.transitions) {
  9383. target = vertical ? "translate3d(0," + target + ",0)" : "translate3d(" + target + ",0,0)";
  9384. dur = dur !== undefined ? dur / 1e3 + "s" : "0s";
  9385. slider.container.css("-" + slider.pfx + "-transition-duration", dur);
  9386. slider.container.css("transition-duration", dur);
  9387. }
  9388. slider.args[slider.prop] = target;
  9389. if (slider.transitions || dur === undefined) slider.container.css(slider.args);
  9390. slider.container.css("transform", target);
  9391. };
  9392. slider.setup = function(type) {
  9393. // SLIDE:
  9394. if (!fade) {
  9395. var sliderOffset, arr;
  9396. if (type === "init") {
  9397. slider.viewport = $('<div class="' + namespace + 'viewport"></div>').css({
  9398. overflow: "hidden",
  9399. position: "relative"
  9400. }).appendTo(slider).append(slider.container);
  9401. // INFINITE LOOP:
  9402. slider.cloneCount = 0;
  9403. slider.cloneOffset = 0;
  9404. // REVERSE:
  9405. if (reverse) {
  9406. arr = $.makeArray(slider.slides).reverse();
  9407. slider.slides = $(arr);
  9408. slider.container.empty().append(slider.slides);
  9409. }
  9410. }
  9411. // INFINITE LOOP && !CAROUSEL:
  9412. if (slider.vars.animationLoop && !carousel) {
  9413. slider.cloneCount = 2;
  9414. slider.cloneOffset = 1;
  9415. // clear out old clones
  9416. if (type !== "init") slider.container.find(".clone").remove();
  9417. slider.container.append(methods.uniqueID(slider.slides.first().clone().addClass("clone")).attr("aria-hidden", "true")).prepend(methods.uniqueID(slider.slides.last().clone().addClass("clone")).attr("aria-hidden", "true"));
  9418. }
  9419. slider.newSlides = $(slider.vars.selector, slider);
  9420. sliderOffset = reverse ? slider.count - 1 - slider.currentSlide + slider.cloneOffset : slider.currentSlide + slider.cloneOffset;
  9421. // VERTICAL:
  9422. if (vertical && !carousel) {
  9423. slider.container.height((slider.count + slider.cloneCount) * 200 + "%").css("position", "absolute").width("100%");
  9424. setTimeout(function() {
  9425. slider.newSlides.css({
  9426. display: "block"
  9427. });
  9428. slider.doMath();
  9429. slider.viewport.height(slider.h);
  9430. slider.setProps(sliderOffset * slider.h, "init");
  9431. }, type === "init" ? 100 : 0);
  9432. } else {
  9433. slider.container.width((slider.count + slider.cloneCount) * 200 + "%");
  9434. slider.setProps(sliderOffset * slider.computedW, "init");
  9435. setTimeout(function() {
  9436. slider.doMath();
  9437. slider.newSlides.css({
  9438. width: slider.computedW,
  9439. "float": "left",
  9440. display: "block"
  9441. });
  9442. // SMOOTH HEIGHT:
  9443. if (slider.vars.smoothHeight) methods.smoothHeight();
  9444. }, type === "init" ? 100 : 0);
  9445. }
  9446. } else {
  9447. // FADE:
  9448. slider.slides.css({
  9449. width: "100%",
  9450. "float": "left",
  9451. marginRight: "-100%",
  9452. position: "relative"
  9453. });
  9454. if (type === "init") {
  9455. if (!touch) {
  9456. //slider.slides.eq(slider.currentSlide).fadeIn(slider.vars.animationSpeed, slider.vars.easing);
  9457. if (slider.vars.fadeFirstSlide == false) {
  9458. slider.slides.css({
  9459. opacity: 0,
  9460. display: "block",
  9461. zIndex: 1
  9462. }).eq(slider.currentSlide).css({
  9463. zIndex: 2
  9464. }).css({
  9465. opacity: 1
  9466. });
  9467. } else {
  9468. slider.slides.css({
  9469. opacity: 0,
  9470. display: "block",
  9471. zIndex: 1
  9472. }).eq(slider.currentSlide).css({
  9473. zIndex: 2
  9474. }).animate({
  9475. opacity: 1
  9476. }, slider.vars.animationSpeed, slider.vars.easing);
  9477. }
  9478. } else {
  9479. slider.slides.css({
  9480. opacity: 0,
  9481. display: "block",
  9482. webkitTransition: "opacity " + slider.vars.animationSpeed / 1e3 + "s ease",
  9483. zIndex: 1
  9484. }).eq(slider.currentSlide).css({
  9485. opacity: 1,
  9486. zIndex: 2
  9487. });
  9488. }
  9489. }
  9490. // SMOOTH HEIGHT:
  9491. if (slider.vars.smoothHeight) methods.smoothHeight();
  9492. }
  9493. // !CAROUSEL:
  9494. // CANDIDATE: active slide
  9495. if (!carousel) slider.slides.removeClass(namespace + "active-slide").eq(slider.currentSlide).addClass(namespace + "active-slide");
  9496. //FlexSlider: init() Callback
  9497. slider.vars.init(slider);
  9498. };
  9499. slider.doMath = function() {
  9500. var slide = slider.slides.first(), slideMargin = slider.vars.itemMargin, minItems = slider.vars.minItems, maxItems = slider.vars.maxItems;
  9501. slider.w = slider.viewport === undefined ? slider.width() : slider.viewport.width();
  9502. slider.h = slide.height();
  9503. slider.boxPadding = slide.outerWidth() - slide.width();
  9504. // CAROUSEL:
  9505. if (carousel) {
  9506. slider.itemT = slider.vars.itemWidth + slideMargin;
  9507. slider.minW = minItems ? minItems * slider.itemT : slider.w;
  9508. slider.maxW = maxItems ? maxItems * slider.itemT - slideMargin : slider.w;
  9509. slider.itemW = slider.minW > slider.w ? (slider.w - slideMargin * (minItems - 1)) / minItems : slider.maxW < slider.w ? (slider.w - slideMargin * (maxItems - 1)) / maxItems : slider.vars.itemWidth > slider.w ? slider.w : slider.vars.itemWidth;
  9510. slider.visible = Math.floor(slider.w / slider.itemW);
  9511. slider.move = slider.vars.move > 0 && slider.vars.move < slider.visible ? slider.vars.move : slider.visible;
  9512. slider.pagingCount = Math.ceil((slider.count - slider.visible) / slider.move + 1);
  9513. slider.last = slider.pagingCount - 1;
  9514. slider.limit = slider.pagingCount === 1 ? 0 : slider.vars.itemWidth > slider.w ? slider.itemW * (slider.count - 1) + slideMargin * (slider.count - 1) : (slider.itemW + slideMargin) * slider.count - slider.w - slideMargin;
  9515. } else {
  9516. slider.itemW = slider.w;
  9517. slider.pagingCount = slider.count;
  9518. slider.last = slider.count - 1;
  9519. }
  9520. slider.computedW = slider.itemW - slider.boxPadding;
  9521. };
  9522. slider.update = function(pos, action) {
  9523. slider.doMath();
  9524. // update currentSlide and slider.animatingTo if necessary
  9525. if (!carousel) {
  9526. if (pos < slider.currentSlide) {
  9527. slider.currentSlide += 1;
  9528. } else if (pos <= slider.currentSlide && pos !== 0) {
  9529. slider.currentSlide -= 1;
  9530. }
  9531. slider.animatingTo = slider.currentSlide;
  9532. }
  9533. // update controlNav
  9534. if (slider.vars.controlNav && !slider.manualControls) {
  9535. if (action === "add" && !carousel || slider.pagingCount > slider.controlNav.length) {
  9536. methods.controlNav.update("add");
  9537. } else if (action === "remove" && !carousel || slider.pagingCount < slider.controlNav.length) {
  9538. if (carousel && slider.currentSlide > slider.last) {
  9539. slider.currentSlide -= 1;
  9540. slider.animatingTo -= 1;
  9541. }
  9542. methods.controlNav.update("remove", slider.last);
  9543. }
  9544. }
  9545. // update directionNav
  9546. if (slider.vars.directionNav) methods.directionNav.update();
  9547. };
  9548. slider.addSlide = function(obj, pos) {
  9549. var $obj = $(obj);
  9550. slider.count += 1;
  9551. slider.last = slider.count - 1;
  9552. // append new slide
  9553. if (vertical && reverse) {
  9554. pos !== undefined ? slider.slides.eq(slider.count - pos).after($obj) : slider.container.prepend($obj);
  9555. } else {
  9556. pos !== undefined ? slider.slides.eq(pos).before($obj) : slider.container.append($obj);
  9557. }
  9558. // update currentSlide, animatingTo, controlNav, and directionNav
  9559. slider.update(pos, "add");
  9560. // update slider.slides
  9561. slider.slides = $(slider.vars.selector + ":not(.clone)", slider);
  9562. // re-setup the slider to accomdate new slide
  9563. slider.setup();
  9564. //FlexSlider: added() Callback
  9565. slider.vars.added(slider);
  9566. };
  9567. slider.removeSlide = function(obj) {
  9568. var pos = isNaN(obj) ? slider.slides.index($(obj)) : obj;
  9569. // update count
  9570. slider.count -= 1;
  9571. slider.last = slider.count - 1;
  9572. // remove slide
  9573. if (isNaN(obj)) {
  9574. $(obj, slider.slides).remove();
  9575. } else {
  9576. vertical && reverse ? slider.slides.eq(slider.last).remove() : slider.slides.eq(obj).remove();
  9577. }
  9578. // update currentSlide, animatingTo, controlNav, and directionNav
  9579. slider.doMath();
  9580. slider.update(pos, "remove");
  9581. // update slider.slides
  9582. slider.slides = $(slider.vars.selector + ":not(.clone)", slider);
  9583. // re-setup the slider to accomdate new slide
  9584. slider.setup();
  9585. // FlexSlider: removed() Callback
  9586. slider.vars.removed(slider);
  9587. };
  9588. //FlexSlider: Initialize
  9589. methods.init();
  9590. };
  9591. // Ensure the slider isn't focussed if the window loses focus.
  9592. $(window).blur(function(e) {
  9593. focused = false;
  9594. }).focus(function(e) {
  9595. focused = true;
  9596. });
  9597. //FlexSlider: Default Settings
  9598. $.flexslider.defaults = {
  9599. namespace: "am-",
  9600. //{NEW} String: Prefix string attached to the class of every element generated by the plugin
  9601. selector: ".am-slides > li",
  9602. //{NEW} Selector: Must match a simple pattern. '{container} > {slide}' -- Ignore pattern at your own peril
  9603. animation: "slide",
  9604. //String: Select your animation type, "fade" or "slide"
  9605. easing: "swing",
  9606. //{NEW} String: Determines the easing method used in jQuery transitions. jQuery easing plugin is supported!
  9607. direction: "horizontal",
  9608. //String: Select the sliding direction, "horizontal" or "vertical"
  9609. reverse: false,
  9610. //{NEW} Boolean: Reverse the animation direction
  9611. animationLoop: true,
  9612. //Boolean: Should the animation loop? If false, directionNav will received "disable" classes at either end
  9613. smoothHeight: false,
  9614. //{NEW} Boolean: Allow height of the slider to animate smoothly in horizontal mode
  9615. startAt: 0,
  9616. //Integer: The slide that the slider should start on. Array notation (0 = first slide)
  9617. slideshow: true,
  9618. //Boolean: Animate slider automatically
  9619. slideshowSpeed: 5e3,
  9620. //Integer: Set the speed of the slideshow cycling, in milliseconds
  9621. animationSpeed: 600,
  9622. //Integer: Set the speed of animations, in milliseconds
  9623. initDelay: 0,
  9624. //{NEW} Integer: Set an initialization delay, in milliseconds
  9625. randomize: false,
  9626. //Boolean: Randomize slide order
  9627. thumbCaptions: false,
  9628. //Boolean: Whether or not to put captions on thumbnails when using the "thumbnails" controlNav.
  9629. // Usability features
  9630. pauseOnAction: true,
  9631. //Boolean: Pause the slideshow when interacting with control elements, highly recommended.
  9632. pauseOnHover: false,
  9633. //Boolean: Pause the slideshow when hovering over slider, then resume when no longer hovering
  9634. pauseInvisible: true,
  9635. //{NEW} Boolean: Pause the slideshow when tab is invisible, resume when visible. Provides better UX, lower CPU usage.
  9636. useCSS: true,
  9637. //{NEW} Boolean: Slider will use CSS3 transitions if available
  9638. touch: true,
  9639. //{NEW} Boolean: Allow touch swipe navigation of the slider on touch-enabled devices
  9640. video: false,
  9641. //{NEW} Boolean: If using video in the slider, will prevent CSS3 3D Transforms to avoid graphical glitches
  9642. // Primary Controls
  9643. controlNav: true,
  9644. //Boolean: Create navigation for paging control of each clide? Note: Leave true for manualControls usage
  9645. directionNav: true,
  9646. //Boolean: Create navigation for previous/next navigation? (true/false)
  9647. prevText: "Previous",
  9648. //String: Set the text for the "previous" directionNav item
  9649. nextText: "Next",
  9650. //String: Set the text for the "next" directionNav item
  9651. // Secondary Navigation
  9652. keyboard: true,
  9653. //Boolean: Allow slider navigating via keyboard left/right keys
  9654. multipleKeyboard: false,
  9655. //{NEW} Boolean: Allow keyboard navigation to affect multiple sliders. Default behavior cuts out keyboard navigation with more than one slider present.
  9656. mousewheel: false,
  9657. //{UPDATED} Boolean: Requires jquery.mousewheel.js (https://github.com/brandonaaron/jquery-mousewheel) - Allows slider navigating via mousewheel
  9658. pausePlay: false,
  9659. //Boolean: Create pause/play dynamic element
  9660. pauseText: "Pause",
  9661. //String: Set the text for the "pause" pausePlay item
  9662. playText: "Play",
  9663. //String: Set the text for the "play" pausePlay item
  9664. // Special properties
  9665. controlsContainer: "",
  9666. //{UPDATED} jQuery Object/Selector: Declare which container the navigation elements should be appended too. Default container is the FlexSlider element. Example use would be $(".flexslider-container"). Property is ignored if given element is not found.
  9667. manualControls: "",
  9668. //{UPDATED} jQuery Object/Selector: Declare custom control navigation. Examples would be $(".flex-control-nav li") or "#tabs-nav li img", etc. The number of elements in your controlNav should match the number of slides/tabs.
  9669. sync: "",
  9670. //{NEW} Selector: Mirror the actions performed on this slider with another slider. Use with care.
  9671. asNavFor: "",
  9672. //{NEW} Selector: Internal property exposed for turning the slider into a thumbnail navigation for another slider
  9673. // Carousel Options
  9674. itemWidth: 0,
  9675. //{NEW} Integer: Box-model width of individual carousel items, including horizontal borders and padding.
  9676. itemMargin: 0,
  9677. //{NEW} Integer: Margin between carousel items.
  9678. minItems: 1,
  9679. //{NEW} Integer: Minimum number of carousel items that should be visible. Items will resize fluidly when below this.
  9680. maxItems: 0,
  9681. //{NEW} Integer: Maxmimum number of carousel items that should be visible. Items will resize fluidly when above this limit.
  9682. move: 0,
  9683. //{NEW} Integer: Number of carousel items that should move on animation. If 0, slider will move all visible items.
  9684. allowOneSlide: true,
  9685. //{NEW} Boolean: Whether or not to allow a slider comprised of a single slide
  9686. // Callback API
  9687. start: function() {},
  9688. //Callback: function(slider) - Fires when the slider loads the first slide
  9689. before: function() {},
  9690. //Callback: function(slider) - Fires asynchronously with each slider animation
  9691. after: function() {},
  9692. //Callback: function(slider) - Fires after each slider animation completes
  9693. end: function() {},
  9694. //Callback: function(slider) - Fires when the slider reaches the last slide (asynchronous)
  9695. added: function() {},
  9696. //{NEW} Callback: function(slider) - Fires after a slide is added
  9697. removed: function() {},
  9698. //{NEW} Callback: function(slider) - Fires after a slide is removed
  9699. init: function() {}
  9700. };
  9701. //FlexSlider: Plugin Function
  9702. $.fn.flexslider = function(options) {
  9703. if (options === undefined) options = {};
  9704. if (typeof options === "object") {
  9705. return this.each(function() {
  9706. var $this = $(this), selector = options.selector ? options.selector : ".am-slides > li", $slides = $this.find(selector);
  9707. if ($slides.length === 1 && options.allowOneSlide === true || $slides.length === 0) {
  9708. $slides.animate({
  9709. opacity: 1
  9710. }, 400);
  9711. if (options.start) options.start($this);
  9712. } else if ($this.data("flexslider") === undefined) {
  9713. new $.flexslider(this, options);
  9714. }
  9715. });
  9716. } else {
  9717. // Helper strings to quickly perform functions on the slider
  9718. var $slider = $(this).data("flexslider");
  9719. switch (options) {
  9720. case "play":
  9721. $slider.play();
  9722. break;
  9723. case "pause":
  9724. $slider.pause();
  9725. break;
  9726. case "stop":
  9727. $slider.stop();
  9728. break;
  9729. case "next":
  9730. $slider.flexAnimate($slider.getTarget("next"), true);
  9731. break;
  9732. case "prev":
  9733. case "previous":
  9734. $slider.flexAnimate($slider.getTarget("prev"), true);
  9735. break;
  9736. default:
  9737. if (typeof options === "number") $slider.flexAnimate(options, true);
  9738. }
  9739. }
  9740. };
  9741. });
  9742. define("zepto.pinchzoom", [], function(require, exports, module) {
  9743. /**
  9744. * @via https://github.com/manuelstofer/pinchzoom/blob/master/src/pinchzoom.js
  9745. * @license the MIT License.
  9746. */
  9747. var definePinchZoom = function($) {
  9748. /**
  9749. * Pinch zoom using jQuery
  9750. * @version 0.0.2
  9751. * @author Manuel Stofer <mst@rtp.ch>
  9752. * @param el
  9753. * @param options
  9754. * @constructor
  9755. */
  9756. var PinchZoom = function(el, options) {
  9757. this.el = $(el);
  9758. this.zoomFactor = 1;
  9759. this.lastScale = 1;
  9760. this.offset = {
  9761. x: 0,
  9762. y: 0
  9763. };
  9764. this.options = $.extend({}, this.defaults, options);
  9765. this.setupMarkup();
  9766. this.bindEvents();
  9767. this.update();
  9768. // default enable.
  9769. this.enable();
  9770. }, sum = function(a, b) {
  9771. return a + b;
  9772. }, isCloseTo = function(value, expected) {
  9773. return value > expected - .01 && value < expected + .01;
  9774. };
  9775. PinchZoom.prototype = {
  9776. defaults: {
  9777. tapZoomFactor: 2,
  9778. zoomOutFactor: 1.3,
  9779. animationDuration: 300,
  9780. animationInterval: 5,
  9781. maxZoom: 5,
  9782. minZoom: .5,
  9783. lockDragAxis: false,
  9784. use2d: false,
  9785. zoomStartEventName: "pz_zoomstart",
  9786. zoomEndEventName: "pz_zoomend",
  9787. dragStartEventName: "pz_dragstart",
  9788. dragEndEventName: "pz_dragend",
  9789. doubleTapEventName: "pz_doubletap"
  9790. },
  9791. /**
  9792. * Event handler for 'dragstart'
  9793. * @param event
  9794. */
  9795. handleDragStart: function(event) {
  9796. this.el.trigger(this.options.dragStartEventName);
  9797. this.stopAnimation();
  9798. this.lastDragPosition = false;
  9799. this.hasInteraction = true;
  9800. this.handleDrag(event);
  9801. },
  9802. /**
  9803. * Event handler for 'drag'
  9804. * @param event
  9805. */
  9806. handleDrag: function(event) {
  9807. if (this.zoomFactor > 1) {
  9808. var touch = this.getTouches(event)[0];
  9809. this.drag(touch, this.lastDragPosition);
  9810. this.offset = this.sanitizeOffset(this.offset);
  9811. this.lastDragPosition = touch;
  9812. }
  9813. },
  9814. handleDragEnd: function() {
  9815. this.el.trigger(this.options.dragEndEventName);
  9816. this.end();
  9817. },
  9818. /**
  9819. * Event handler for 'zoomstart'
  9820. * @param event
  9821. */
  9822. handleZoomStart: function(event) {
  9823. this.el.trigger(this.options.zoomStartEventName);
  9824. this.stopAnimation();
  9825. this.lastScale = 1;
  9826. this.nthZoom = 0;
  9827. this.lastZoomCenter = false;
  9828. this.hasInteraction = true;
  9829. },
  9830. /**
  9831. * Event handler for 'zoom'
  9832. * @param event
  9833. */
  9834. handleZoom: function(event, newScale) {
  9835. // a relative scale factor is used
  9836. var touchCenter = this.getTouchCenter(this.getTouches(event)), scale = newScale / this.lastScale;
  9837. this.lastScale = newScale;
  9838. // the first touch events are thrown away since they are not precise
  9839. this.nthZoom += 1;
  9840. if (this.nthZoom > 3) {
  9841. this.scale(scale, touchCenter);
  9842. this.drag(touchCenter, this.lastZoomCenter);
  9843. }
  9844. this.lastZoomCenter = touchCenter;
  9845. },
  9846. handleZoomEnd: function() {
  9847. this.el.trigger(this.options.zoomEndEventName);
  9848. this.end();
  9849. },
  9850. /**
  9851. * Event handler for 'doubletap'
  9852. * @param event
  9853. */
  9854. handleDoubleTap: function(event) {
  9855. var center = this.getTouches(event)[0], zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor, startZoomFactor = this.zoomFactor, updateProgress = function(progress) {
  9856. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
  9857. }.bind(this);
  9858. if (this.hasInteraction) {
  9859. return;
  9860. }
  9861. if (startZoomFactor > zoomFactor) {
  9862. center = this.getCurrentZoomCenter();
  9863. }
  9864. this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing);
  9865. this.el.trigger(this.options.doubleTapEventName);
  9866. },
  9867. /**
  9868. * Max / min values for the offset
  9869. * @param offset
  9870. * @return {Object} the sanitized offset
  9871. */
  9872. sanitizeOffset: function(offset) {
  9873. var maxX = (this.zoomFactor - 1) * this.getContainerX(), maxY = (this.zoomFactor - 1) * this.getContainerY(), maxOffsetX = Math.max(maxX, 0), maxOffsetY = Math.max(maxY, 0), minOffsetX = Math.min(maxX, 0), minOffsetY = Math.min(maxY, 0);
  9874. return {
  9875. x: Math.min(Math.max(offset.x, minOffsetX), maxOffsetX),
  9876. y: Math.min(Math.max(offset.y, minOffsetY), maxOffsetY)
  9877. };
  9878. },
  9879. /**
  9880. * Scale to a specific zoom factor (not relative)
  9881. * @param zoomFactor
  9882. * @param center
  9883. */
  9884. scaleTo: function(zoomFactor, center) {
  9885. this.scale(zoomFactor / this.zoomFactor, center);
  9886. },
  9887. /**
  9888. * Scales the element from specified center
  9889. * @param scale
  9890. * @param center
  9891. */
  9892. scale: function(scale, center) {
  9893. scale = this.scaleZoomFactor(scale);
  9894. this.addOffset({
  9895. x: (scale - 1) * (center.x + this.offset.x),
  9896. y: (scale - 1) * (center.y + this.offset.y)
  9897. });
  9898. },
  9899. /**
  9900. * Scales the zoom factor relative to current state
  9901. * @param scale
  9902. * @return the actual scale (can differ because of max min zoom factor)
  9903. */
  9904. scaleZoomFactor: function(scale) {
  9905. var originalZoomFactor = this.zoomFactor;
  9906. this.zoomFactor *= scale;
  9907. this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom));
  9908. return this.zoomFactor / originalZoomFactor;
  9909. },
  9910. /**
  9911. * Drags the element
  9912. * @param center
  9913. * @param lastCenter
  9914. */
  9915. drag: function(center, lastCenter) {
  9916. if (lastCenter) {
  9917. if (this.options.lockDragAxis) {
  9918. // lock scroll to position that was changed the most
  9919. if (Math.abs(center.x - lastCenter.x) > Math.abs(center.y - lastCenter.y)) {
  9920. this.addOffset({
  9921. x: -(center.x - lastCenter.x),
  9922. y: 0
  9923. });
  9924. } else {
  9925. this.addOffset({
  9926. y: -(center.y - lastCenter.y),
  9927. x: 0
  9928. });
  9929. }
  9930. } else {
  9931. this.addOffset({
  9932. y: -(center.y - lastCenter.y),
  9933. x: -(center.x - lastCenter.x)
  9934. });
  9935. }
  9936. }
  9937. },
  9938. /**
  9939. * Calculates the touch center of multiple touches
  9940. * @param touches
  9941. * @return {Object}
  9942. */
  9943. getTouchCenter: function(touches) {
  9944. return this.getVectorAvg(touches);
  9945. },
  9946. /**
  9947. * Calculates the average of multiple vectors (x, y values)
  9948. */
  9949. getVectorAvg: function(vectors) {
  9950. return {
  9951. x: vectors.map(function(v) {
  9952. return v.x;
  9953. }).reduce(sum) / vectors.length,
  9954. y: vectors.map(function(v) {
  9955. return v.y;
  9956. }).reduce(sum) / vectors.length
  9957. };
  9958. },
  9959. /**
  9960. * Adds an offset
  9961. * @param offset the offset to add
  9962. * @return return true when the offset change was accepted
  9963. */
  9964. addOffset: function(offset) {
  9965. this.offset = {
  9966. x: this.offset.x + offset.x,
  9967. y: this.offset.y + offset.y
  9968. };
  9969. },
  9970. sanitize: function() {
  9971. if (this.zoomFactor < this.options.zoomOutFactor) {
  9972. this.zoomOutAnimation();
  9973. } else if (this.isInsaneOffset(this.offset)) {
  9974. this.sanitizeOffsetAnimation();
  9975. }
  9976. },
  9977. /**
  9978. * Checks if the offset is ok with the current zoom factor
  9979. * @param offset
  9980. * @return {Boolean}
  9981. */
  9982. isInsaneOffset: function(offset) {
  9983. var sanitizedOffset = this.sanitizeOffset(offset);
  9984. return sanitizedOffset.x !== offset.x || sanitizedOffset.y !== offset.y;
  9985. },
  9986. /**
  9987. * Creates an animation moving to a sane offset
  9988. */
  9989. sanitizeOffsetAnimation: function() {
  9990. var targetOffset = this.sanitizeOffset(this.offset), startOffset = {
  9991. x: this.offset.x,
  9992. y: this.offset.y
  9993. }, updateProgress = function(progress) {
  9994. this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x);
  9995. this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y);
  9996. this.update();
  9997. }.bind(this);
  9998. this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing);
  9999. },
  10000. /**
  10001. * Zooms back to the original position,
  10002. * (no offset and zoom factor 1)
  10003. */
  10004. zoomOutAnimation: function() {
  10005. var startZoomFactor = this.zoomFactor, zoomFactor = 1, center = this.getCurrentZoomCenter(), updateProgress = function(progress) {
  10006. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
  10007. }.bind(this);
  10008. this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing);
  10009. },
  10010. /**
  10011. * Updates the aspect ratio
  10012. */
  10013. updateAspectRatio: function() {
  10014. // this.setContainerY(this.getContainerX() / this.getAspectRatio());
  10015. // @modified
  10016. this.setContainerY();
  10017. },
  10018. /**
  10019. * Calculates the initial zoom factor (for the element to fit into the container)
  10020. * @return the initial zoom factor
  10021. */
  10022. getInitialZoomFactor: function() {
  10023. // use .offsetWidth instead of width()
  10024. // because jQuery-width() return the original width but Zepto-width() will calculate width with transform.
  10025. // the same as .height()
  10026. return this.container[0].offsetWidth / this.el[0].offsetWidth;
  10027. },
  10028. /**
  10029. * Calculates the aspect ratio of the element
  10030. * @return the aspect ratio
  10031. */
  10032. getAspectRatio: function() {
  10033. return this.el[0].offsetWidth / this.el[0].offsetHeight;
  10034. },
  10035. /**
  10036. * Calculates the virtual zoom center for the current offset and zoom factor
  10037. * (used for reverse zoom)
  10038. * @return {Object} the current zoom center
  10039. */
  10040. getCurrentZoomCenter: function() {
  10041. // uses following formula to calculate the zoom center x value
  10042. // offset_left / offset_right = zoomcenter_x / (container_x - zoomcenter_x)
  10043. var length = this.container[0].offsetWidth * this.zoomFactor, offsetLeft = this.offset.x, offsetRight = length - offsetLeft - this.container[0].offsetWidth, widthOffsetRatio = offsetLeft / offsetRight, centerX = widthOffsetRatio * this.container[0].offsetWidth / (widthOffsetRatio + 1), // the same for the zoomcenter y
  10044. height = this.container[0].offsetHeight * this.zoomFactor, offsetTop = this.offset.y, offsetBottom = height - offsetTop - this.container[0].offsetHeight, heightOffsetRatio = offsetTop / offsetBottom, centerY = heightOffsetRatio * this.container[0].offsetHeight / (heightOffsetRatio + 1);
  10045. // prevents division by zero
  10046. if (offsetRight === 0) {
  10047. centerX = this.container[0].offsetWidth;
  10048. }
  10049. if (offsetBottom === 0) {
  10050. centerY = this.container[0].offsetHeight;
  10051. }
  10052. return {
  10053. x: centerX,
  10054. y: centerY
  10055. };
  10056. },
  10057. canDrag: function() {
  10058. return !isCloseTo(this.zoomFactor, 1);
  10059. },
  10060. /**
  10061. * Returns the touches of an event relative to the container offset
  10062. * @param event
  10063. * @return array touches
  10064. */
  10065. getTouches: function(event) {
  10066. var position = this.container.offset();
  10067. return Array.prototype.slice.call(event.touches).map(function(touch) {
  10068. return {
  10069. x: touch.pageX - position.left,
  10070. y: touch.pageY - position.top
  10071. };
  10072. });
  10073. },
  10074. /**
  10075. * Animation loop
  10076. * does not support simultaneous animations
  10077. * @param duration
  10078. * @param interval
  10079. * @param framefn
  10080. * @param timefn
  10081. * @param callback
  10082. */
  10083. animate: function(duration, interval, framefn, timefn, callback) {
  10084. var startTime = new Date().getTime(), renderFrame = function() {
  10085. if (!this.inAnimation) {
  10086. return;
  10087. }
  10088. var frameTime = new Date().getTime() - startTime, progress = frameTime / duration;
  10089. if (frameTime >= duration) {
  10090. framefn(1);
  10091. if (callback) {
  10092. callback();
  10093. }
  10094. this.update();
  10095. this.stopAnimation();
  10096. this.update();
  10097. } else {
  10098. if (timefn) {
  10099. progress = timefn(progress);
  10100. }
  10101. framefn(progress);
  10102. this.update();
  10103. setTimeout(renderFrame, interval);
  10104. }
  10105. }.bind(this);
  10106. this.inAnimation = true;
  10107. renderFrame();
  10108. },
  10109. /**
  10110. * Stops the animation
  10111. */
  10112. stopAnimation: function() {
  10113. this.inAnimation = false;
  10114. },
  10115. /**
  10116. * Swing timing function for animations
  10117. * @param p
  10118. * @return {Number}
  10119. */
  10120. swing: function(p) {
  10121. return -Math.cos(p * Math.PI) / 2 + .5;
  10122. },
  10123. getContainerX: function() {
  10124. // return this.container[0].offsetWidth;
  10125. // @modified
  10126. return window.innerWidth;
  10127. },
  10128. getContainerY: function() {
  10129. // return this.container[0].offsetHeight;
  10130. // @modified
  10131. return window.innerHeight;
  10132. },
  10133. setContainerY: function(y) {
  10134. // return this.container.height(y);
  10135. // @modified
  10136. var t = window.innerHeight;
  10137. return this.el.css({
  10138. height: t
  10139. }), this.container.height(t);
  10140. },
  10141. /**
  10142. * Creates the expected html structure
  10143. */
  10144. setupMarkup: function() {
  10145. this.container = $('<div class="pinch-zoom-container"></div>');
  10146. this.el.before(this.container);
  10147. this.container.append(this.el);
  10148. this.container.css({
  10149. overflow: "hidden",
  10150. position: "relative"
  10151. });
  10152. // Zepto doesn't recognize `webkitTransform..` style
  10153. this.el.css({
  10154. "-webkit-transform-origin": "0% 0%",
  10155. "-moz-transform-origin": "0% 0%",
  10156. "-ms-transform-origin": "0% 0%",
  10157. "-o-transform-origin": "0% 0%",
  10158. "transform-origin": "0% 0%",
  10159. position: "absolute"
  10160. });
  10161. },
  10162. end: function() {
  10163. this.hasInteraction = false;
  10164. this.sanitize();
  10165. this.update();
  10166. },
  10167. /**
  10168. * Binds all required event listeners
  10169. */
  10170. bindEvents: function() {
  10171. detectGestures(this.container.get(0), this);
  10172. // Zepto and jQuery both know about `on`
  10173. $(window).on("resize", this.update.bind(this));
  10174. $(this.el).find("img").on("load", this.update.bind(this));
  10175. },
  10176. /**
  10177. * Updates the css values according to the current zoom factor and offset
  10178. */
  10179. update: function() {
  10180. if (this.updatePlaned) {
  10181. return;
  10182. }
  10183. this.updatePlaned = true;
  10184. setTimeout(function() {
  10185. this.updatePlaned = false;
  10186. this.updateAspectRatio();
  10187. var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor, offsetX = -this.offset.x / zoomFactor, offsetY = -this.offset.y / zoomFactor, transform3d = "scale3d(" + zoomFactor + ", " + zoomFactor + ",1) " + "translate3d(" + offsetX + "px," + offsetY + "px,0px)", transform2d = "scale(" + zoomFactor + ", " + zoomFactor + ") " + "translate(" + offsetX + "px," + offsetY + "px)", removeClone = function() {
  10188. if (this.clone) {
  10189. this.clone.remove();
  10190. delete this.clone;
  10191. }
  10192. }.bind(this);
  10193. // Scale 3d and translate3d are faster (at least on ios)
  10194. // but they also reduce the quality.
  10195. // PinchZoom uses the 3d transformations during interactions
  10196. // after interactions it falls back to 2d transformations
  10197. if (!this.options.use2d || this.hasInteraction || this.inAnimation) {
  10198. this.is3d = true;
  10199. removeClone();
  10200. this.el.css({
  10201. "-webkit-transform": transform3d,
  10202. "-o-transform": transform2d,
  10203. "-ms-transform": transform2d,
  10204. "-moz-transform": transform2d,
  10205. transform: transform3d
  10206. });
  10207. } else {
  10208. // When changing from 3d to 2d transform webkit has some glitches.
  10209. // To avoid this, a copy of the 3d transformed element is displayed in the
  10210. // foreground while the element is converted from 3d to 2d transform
  10211. if (this.is3d) {
  10212. this.clone = this.el.clone();
  10213. this.clone.css("pointer-events", "none");
  10214. this.clone.appendTo(this.container);
  10215. setTimeout(removeClone, 200);
  10216. }
  10217. this.el.css({
  10218. "-webkit-transform": transform2d,
  10219. "-o-transform": transform2d,
  10220. "-ms-transform": transform2d,
  10221. "-moz-transform": transform2d,
  10222. transform: transform2d
  10223. });
  10224. this.is3d = false;
  10225. }
  10226. }.bind(this), 0);
  10227. },
  10228. /**
  10229. * Enables event handling for gestures
  10230. */
  10231. enable: function() {
  10232. this.enabled = true;
  10233. },
  10234. /**
  10235. * Disables event handling for gestures
  10236. */
  10237. disable: function() {
  10238. this.enabled = false;
  10239. }
  10240. };
  10241. var detectGestures = function(el, target) {
  10242. var interaction = null, fingers = 0, lastTouchStart = null, startTouches = null, setInteraction = function(newInteraction, event) {
  10243. if (interaction !== newInteraction) {
  10244. if (interaction && !newInteraction) {
  10245. switch (interaction) {
  10246. case "zoom":
  10247. target.handleZoomEnd(event);
  10248. break;
  10249. case "drag":
  10250. target.handleDragEnd(event);
  10251. break;
  10252. }
  10253. }
  10254. switch (newInteraction) {
  10255. case "zoom":
  10256. target.handleZoomStart(event);
  10257. break;
  10258. case "drag":
  10259. target.handleDragStart(event);
  10260. break;
  10261. }
  10262. }
  10263. interaction = newInteraction;
  10264. }, updateInteraction = function(event) {
  10265. if (fingers === 2) {
  10266. setInteraction("zoom");
  10267. } else if (fingers === 1 && target.canDrag()) {
  10268. setInteraction("drag", event);
  10269. } else {
  10270. setInteraction(null, event);
  10271. }
  10272. }, targetTouches = function(touches) {
  10273. return Array.prototype.slice.call(touches).map(function(touch) {
  10274. return {
  10275. x: touch.pageX,
  10276. y: touch.pageY
  10277. };
  10278. });
  10279. }, getDistance = function(a, b) {
  10280. var x, y;
  10281. x = a.x - b.x;
  10282. y = a.y - b.y;
  10283. return Math.sqrt(x * x + y * y);
  10284. }, calculateScale = function(startTouches, endTouches) {
  10285. var startDistance = getDistance(startTouches[0], startTouches[1]), endDistance = getDistance(endTouches[0], endTouches[1]);
  10286. return endDistance / startDistance;
  10287. }, cancelEvent = function(event) {
  10288. event.stopPropagation();
  10289. event.preventDefault();
  10290. }, detectDoubleTap = function(event) {
  10291. var time = new Date().getTime();
  10292. if (fingers > 1) {
  10293. lastTouchStart = null;
  10294. }
  10295. if (time - lastTouchStart < 300) {
  10296. cancelEvent(event);
  10297. target.handleDoubleTap(event);
  10298. switch (interaction) {
  10299. case "zoom":
  10300. target.handleZoomEnd(event);
  10301. break;
  10302. case "drag":
  10303. target.handleDragEnd(event);
  10304. break;
  10305. }
  10306. }
  10307. if (fingers === 1) {
  10308. lastTouchStart = time;
  10309. }
  10310. }, firstMove = true;
  10311. el.addEventListener("touchstart", function(event) {
  10312. if (target.enabled) {
  10313. firstMove = true;
  10314. fingers = event.touches.length;
  10315. detectDoubleTap(event);
  10316. }
  10317. });
  10318. el.addEventListener("touchmove", function(event) {
  10319. if (target.enabled) {
  10320. if (firstMove) {
  10321. updateInteraction(event);
  10322. if (interaction) {
  10323. cancelEvent(event);
  10324. }
  10325. startTouches = targetTouches(event.touches);
  10326. } else {
  10327. switch (interaction) {
  10328. case "zoom":
  10329. target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches)));
  10330. break;
  10331. case "drag":
  10332. target.handleDrag(event);
  10333. break;
  10334. }
  10335. if (interaction) {
  10336. cancelEvent(event);
  10337. target.update();
  10338. }
  10339. }
  10340. firstMove = false;
  10341. }
  10342. });
  10343. el.addEventListener("touchend", function(event) {
  10344. if (target.enabled) {
  10345. fingers = event.touches.length;
  10346. updateInteraction(event);
  10347. }
  10348. });
  10349. };
  10350. return PinchZoom;
  10351. };
  10352. module.exports = definePinchZoom(window.Zepto);
  10353. });
  10354. define("accordion", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.collapse" ], function(require, exports, module) {
  10355. require("core");
  10356. require("ui.collapse");
  10357. var $ = window.Zepto, UI = $.AMUI, accordionInit = function() {
  10358. var $accordion = $('[data-am-widget="accordion"]'), selector = {
  10359. item: ".am-accordion-item",
  10360. title: ".am-accordion-title",
  10361. content: ".am-accordion-content"
  10362. };
  10363. $accordion.each(function(i, item) {
  10364. var options = UI.utils.parseOptions($(item).attr("data-am-accordion")), $title = $accordion.find(selector.title);
  10365. $title.on("click", function() {
  10366. var $content = $(this).next(selector.content), $parent = $(this).parent(selector.item), data = $content.data("amui.collapse");
  10367. $parent.toggleClass("am-active");
  10368. if (!data) {
  10369. $content.collapse();
  10370. } else {
  10371. $content.collapse("toggle");
  10372. }
  10373. !options.multiple && $(item).children(".am-active").not($parent).removeClass("am-active").find(".am-accordion-content.am-in").collapse("close");
  10374. });
  10375. });
  10376. };
  10377. // Init on DOM ready
  10378. $(function() {
  10379. accordionInit();
  10380. });
  10381. exports.init = accordionInit;
  10382. });
  10383. define("divider", [], function(require, exports, module) {});
  10384. define("duoshuo", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  10385. require("core");
  10386. var $ = window.Zepto;
  10387. function duoshuoInit() {
  10388. var $dsThread = $(".ds-thread"), dsShortName = $dsThread.parent('[data-am-widget="duoshuo"]').attr("data-ds-short-name"), dsSrc = (document.location.protocol == "https:" ? "https:" : "http:") + "//static.duoshuo.com/embed.js";
  10389. if (!$dsThread.length || !dsShortName) return;
  10390. window.duoshuoQuery = {
  10391. short_name: dsShortName
  10392. };
  10393. // 已经有多说脚本
  10394. if ($('script[src="' + dsSrc + '"]').length) return;
  10395. var $dsJS = $("<script>", {
  10396. async: true,
  10397. type: "text/javascript",
  10398. src: dsSrc,
  10399. charset: "utf-8"
  10400. });
  10401. $("body").append($dsJS);
  10402. }
  10403. $(window).on("load", duoshuoInit);
  10404. exports.init = duoshuoInit;
  10405. });
  10406. define("figure", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.pureview", "zepto.pinchzoom", "util.hammer" ], function(require, exports, module) {
  10407. require("core");
  10408. require("ui.pureview");
  10409. var $ = window.Zepto, UI = $.AMUI;
  10410. /**
  10411. * Is Images zoomable
  10412. * @return {Boolean}
  10413. */
  10414. $.isImgZoomAble = function(element) {
  10415. var t = new Image();
  10416. t.src = element.src;
  10417. var zoomAble = $(element).width() < t.width;
  10418. if (zoomAble) {
  10419. $(element).parent(".am-figure").addClass("am-figure-zoomable");
  10420. }
  10421. return zoomAble;
  10422. };
  10423. var figureInit = function() {
  10424. $(".am-figure").each(function(i, item) {
  10425. var options = UI.utils.parseOptions($(item).attr("data-am-figure"));
  10426. if (options.pureview) {
  10427. $(item).addClass("am-figure-zoomable").pureview();
  10428. } else if (options.autoZoom) {
  10429. var zoomAble = $.isImgZoomAble($(item).find("img")[0]);
  10430. zoomAble && $(item).pureview();
  10431. }
  10432. });
  10433. };
  10434. $(window).on("load", function() {
  10435. figureInit();
  10436. });
  10437. exports.init = figureInit;
  10438. });
  10439. define("footer", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.add2home", "util.cookie", "ui.modal", "ui.dimmer" ], function(require, exports, module) {
  10440. require("core");
  10441. // add2home
  10442. var addToHS = require("ui.add2home");
  10443. var cookie = require("util.cookie"), modal = require("ui.modal"), $ = window.Zepto, footerInit = function() {
  10444. // modal mode
  10445. $(".am-footer-ysp").on("click", function() {
  10446. $("#am-footer-mode").modal();
  10447. });
  10448. addToHS();
  10449. // switch mode
  10450. // switch to desktop
  10451. $('[data-rel="desktop"]').on("click", function(e) {
  10452. e.preventDefault();
  10453. if (window.AMPlatform) {
  10454. // front end
  10455. AMPlatform.util.goDesktop();
  10456. } else {
  10457. // back end
  10458. cookie.set("allmobilize", "desktop", "", "/");
  10459. window.location = window.location;
  10460. }
  10461. });
  10462. };
  10463. $(window).on("load", function() {
  10464. footerInit();
  10465. });
  10466. exports.init = footerInit;
  10467. });
  10468. define("gallery", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.pureview", "zepto.pinchzoom", "util.hammer" ], function(require, exports, module) {
  10469. require("core");
  10470. var PureView = require("ui.pureview");
  10471. var $ = window.Zepto, UI = $.AMUI;
  10472. var galleryInit = function() {
  10473. var $gallery = $('[data-am-widget="gallery"]'), $galleryOne = $gallery.filter(".am-gallery-one");
  10474. $gallery.each(function() {
  10475. var options = UI.utils.parseOptions($(this).attr("data-am-gallery"));
  10476. if (options.pureview) {
  10477. typeof options.pureview === "object" ? $(this).pureview(options.pureview) : $(this).pureview();
  10478. }
  10479. });
  10480. $galleryOne.each(function() {
  10481. galleryMore($(this));
  10482. });
  10483. };
  10484. function galleryMore(object) {
  10485. var moreData = $("<li class='am-gallery-more'><a href='javascript:;'>更多 &gt;&gt;</a></li>");
  10486. if (object.children().length > 6) {
  10487. object.children().each(function(index) {
  10488. if (index > 5) {
  10489. $(this).hide();
  10490. }
  10491. });
  10492. object.find(".am-gallery-more").remove();
  10493. object.append(moreData);
  10494. }
  10495. object.find(".am-gallery-more").on("click", function() {
  10496. object.children().show();
  10497. $(this).hide();
  10498. });
  10499. }
  10500. $(function() {
  10501. galleryInit();
  10502. });
  10503. exports.init = galleryInit;
  10504. });
  10505. define("gotop", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.smooth-scroll" ], function(require, exports, module) {
  10506. require("core");
  10507. require("ui.smooth-scroll");
  10508. var $ = window.Zepto;
  10509. var UI = $.AMUI;
  10510. var goTopInit = function() {
  10511. var $goTop = $('[data-am-widget="gotop"]'), $fixed = $goTop.filter(".am-gotop-fixed"), $win = $(window);
  10512. $goTop.find("a").on("click", function(e) {
  10513. e.preventDefault();
  10514. $win.smoothScroll();
  10515. });
  10516. function checkPosition() {
  10517. $fixed[($win.scrollTop() > 50 ? "add" : "remove") + "Class"]("am-active");
  10518. }
  10519. checkPosition();
  10520. $win.on("scroll.gotop.amui", $.AMUI.utils.debounce(checkPosition, 100));
  10521. };
  10522. $(function() {
  10523. goTopInit();
  10524. });
  10525. exports.init = goTopInit;
  10526. });
  10527. define("header", [], function(require, exports, module) {
  10528. var $ = window.Zepto;
  10529. });
  10530. define("intro", [], function(require, exports, module) {
  10531. var $ = window.Zepto;
  10532. });
  10533. define("list_news", [], function(require, exports, module) {
  10534. var $ = window.Zepto, listNewsInit = function() {
  10535. $(".am-list-news-one").each(function() {
  10536. amListNewsMore($(this));
  10537. });
  10538. };
  10539. function amListNewsMore(object) {
  10540. var $amList = object.find(".am-list");
  10541. var $listMore = "<a class='am-list-news-more am-btn am-btn-default' href='###'>更多 &gt;&gt;</a>";
  10542. if ($amList.children().length > 6) {
  10543. $amList.children().each(function(index) {
  10544. if (index > 5) {
  10545. $(this).hide();
  10546. }
  10547. });
  10548. object.find(".am-list-news-more").remove();
  10549. object.append($listMore);
  10550. }
  10551. object.find(".am-list-news-more").on("click", function() {
  10552. $amList.children().show();
  10553. $(this).hide();
  10554. });
  10555. }
  10556. $(function() {
  10557. listNewsInit();
  10558. });
  10559. exports.init = listNewsInit;
  10560. });
  10561. define("map", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector" ], function(require, exports, module) {
  10562. require("core");
  10563. var $ = window.Zepto;
  10564. function addMapApi(callback) {
  10565. var $mapApi0 = $("<script/>", {
  10566. src: "http://api.map.baidu.com/getscript?type=quick&file=api&ak=WVAXZ05oyNRXS5egLImmentg&t=20140109092002"
  10567. });
  10568. $("body").append($mapApi0);
  10569. $mapApi0.on("load", function() {
  10570. var $mapApi1 = $("<script/>", {
  10571. src: "http://api.map.baidu.com/getscript?type=quick&file=feature&ak=WVAXZ05oyNRXS5egLImmentg&t=20140109092002"
  10572. });
  10573. $("body").append($mapApi1);
  10574. $mapApi1.on("load", function() {
  10575. var script = document.createElement("script");
  10576. script.textContent = "(" + callback.toString() + ")();";
  10577. $("body")[0].appendChild(script);
  10578. });
  10579. });
  10580. }
  10581. function addBdMap() {
  10582. // 如果使用 $ 选择符,minify 以后会报错: $ is undefined
  10583. // 即使传入 $ 也无效,改为使用原生方法
  10584. // 这个函数作为 callback 会插入到 body 以后才执行,应该是 $ 引用错误导致
  10585. var content = document.querySelector(".am-map"), defaultLng = 116.331398, //经度默认值
  10586. defaultLat = 39.897445, //纬度默认值
  10587. name = content.getAttribute("data-name"), address = content.getAttribute("data-address"), lng = content.getAttribute("data-longitude") || defaultLng, lat = content.getAttribute("data-latitude") || defaultLat;
  10588. var map = new BMap.Map("bd-map");
  10589. //实例化一个地理坐标点
  10590. var point = new BMap.Point(lng, lat);
  10591. //设初始化地图, options: 3-18
  10592. map.centerAndZoom(point, 18);
  10593. //添加地图缩放控件
  10594. map.addControl(new BMap.ZoomControl());
  10595. var opts = {
  10596. width: 200,
  10597. // 信息窗口宽度
  10598. //height: 'auto', // 信息窗口高度
  10599. title: name
  10600. };
  10601. // 创建信息窗口对象
  10602. var infoWindow = new BMap.InfoWindow("地址:" + address, opts);
  10603. // 创建地址解析器实例
  10604. var myGeo = new BMap.Geocoder();
  10605. //判断有没有使用经纬度
  10606. if (lng == defaultLng && lat == defaultLat) {
  10607. // 使用地址反解析来设置地图
  10608. // 将地址解析结果显示在地图上,并调整地图视野
  10609. myGeo.getPoint(address, function(point) {
  10610. if (point) {
  10611. map.centerAndZoom(point, 17);
  10612. map.addOverlay(new BMap.Marker(point));
  10613. map.openInfoWindow(infoWindow, point);
  10614. }
  10615. }, "");
  10616. } else {
  10617. // 使用经纬度来设置地图
  10618. myGeo.getLocation(point, function(result) {
  10619. map.centerAndZoom(point, 17);
  10620. map.addOverlay(new BMap.Marker(point));
  10621. if (address) {
  10622. map.openInfoWindow(infoWindow, point);
  10623. } else {
  10624. map.openInfoWindow(new BMap.InfoWindow(address, opts), point);
  10625. }
  10626. });
  10627. }
  10628. }
  10629. var mapInit = function() {
  10630. $(".am-map").length && addMapApi(addBdMap);
  10631. };
  10632. $(document).on("ready", mapInit);
  10633. exports.init = mapInit;
  10634. });
  10635. define("mechat", [], function(require, exports, module) {
  10636. var $ = window.Zepto;
  10637. var mechatInit = function() {
  10638. if (!$("#mechat").length) return;
  10639. var $mechat = $('[data-am-widget="mechat"]'), unitid = $mechat.data("am-mechat-unitid"), $mechatData = $("<script>", {
  10640. charset: "utf-8",
  10641. src: "http://mechatim.com/js/unit/button.js?id=" + unitid
  10642. });
  10643. $("body").append($mechatData);
  10644. };
  10645. // Lazy load
  10646. $(window).on("load", mechatInit);
  10647. exports.init = mechatInit;
  10648. });
  10649. define("menu", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.offcanvas", "zepto.outerdemension", "ui.collapse", "ui.iscroll-lite" ], function(require, exports, module) {
  10650. require("core");
  10651. require("ui.offcanvas");
  10652. require("ui.collapse");
  10653. var IScroll = require("ui.iscroll-lite");
  10654. var $ = window.Zepto;
  10655. var UI = $.AMUI;
  10656. var menuInit = function() {
  10657. var $menus = $('[data-am-widget="menu"]');
  10658. $menus.find(".am-menu-nav .am-parent > a").on("click", function(e) {
  10659. e.preventDefault();
  10660. var $clicked = $(this), $parent = $clicked.parent(), $subMenu = $clicked.next(".am-menu-sub");
  10661. $parent.toggleClass("am-open");
  10662. $subMenu.collapse("toggle");
  10663. $parent.siblings(".am-parent").removeClass("am-open").children(".am-menu-sub.am-in").collapse("close");
  10664. });
  10665. // Dropdown/slidedown menu
  10666. $menus.filter("[data-am-menu-collapse]").find("> .am-menu-toggle").on("click", function(e) {
  10667. e.preventDefault();
  10668. var $this = $(this), $nav = $this.next(".am-menu-nav");
  10669. $this.toggleClass("am-active");
  10670. $nav.collapse("toggle");
  10671. });
  10672. // OffCanvas menu
  10673. $menus.filter("[data-am-menu-offcanvas]").find("> .am-menu-toggle").on("click", function(e) {
  10674. e.preventDefault();
  10675. var $this = $(this), $nav = $this.next(".am-offcanvas");
  10676. $this.toggleClass("am-active");
  10677. $nav.offCanvas("open");
  10678. });
  10679. // Close offCanvas when link clicked
  10680. var autoCloseOffCanvas = '.am-offcanvas[data-dismiss-on="click"]', $autoCloseOffCanvas = $(autoCloseOffCanvas);
  10681. $autoCloseOffCanvas.find("a").not(".am-parent>a").on("click", function(e) {
  10682. $(this).parents(autoCloseOffCanvas).offCanvas("close");
  10683. });
  10684. // one theme
  10685. $menus.filter(".am-menu-one").each(function(index) {
  10686. var $this = $(this), $wrap = $('<div class="am-menu-nav-sub-wrap"></div>'), allWidth = 0, prevIndex, $nav = $this.find(".am-menu-nav"), $navTopItem = $nav.children("li");
  10687. $navTopItem.filter(".am-parent").each(function(index) {
  10688. $(this).attr("data-rel", "#am-menu-sub-" + index);
  10689. $(this).find(".am-menu-sub").attr("id", "am-menu-sub-" + index).appendTo($wrap);
  10690. });
  10691. $this.append($wrap);
  10692. $nav.wrap('<div class="am-menu-nav-wrap" id="am-menu-' + index + '">');
  10693. // $navTopItem.eq(0).addClass('am-active');
  10694. // 计算出所有 li 宽度
  10695. $navTopItem.each(function(i) {
  10696. allWidth += parseFloat($(this).css("width"));
  10697. });
  10698. $nav.width(allWidth);
  10699. var menuScroll = new IScroll("#am-menu-" + index, {
  10700. eventPassthrough: true,
  10701. scrollX: true,
  10702. scrollY: false,
  10703. preventDefault: false
  10704. });
  10705. $navTopItem.on("click", function() {
  10706. var $clicked = $(this);
  10707. $clicked.addClass("am-active").siblings().removeClass("am-active");
  10708. $wrap.find(".am-menu-sub.am-in").collapse("close");
  10709. if ($clicked.is(".am-parent")) {
  10710. !$clicked.hasClass(".am-open") && $wrap.find($clicked.attr("data-rel")).collapse("open");
  10711. } else {
  10712. $clicked.siblings().removeClass("am-open");
  10713. }
  10714. // 第一次调用,没有prevIndex
  10715. if (prevIndex === undefined) {
  10716. prevIndex = $(this).index() ? 0 : 1;
  10717. }
  10718. // 判断方向
  10719. var dir = $(this).index() > prevIndex;
  10720. var target = $(this)[dir ? "next" : "prev"]();
  10721. // 点击的按钮,显示一半
  10722. var offset = target.offset() || $(this).offset();
  10723. var within = $this.offset();
  10724. // 父类左边距
  10725. var listOffset, parentLeft = parseInt($this.css("padding-left"));
  10726. if (dir ? offset.left + offset.width > within.left + within.width : offset.left < within.left) {
  10727. listOffset = $nav.offset();
  10728. menuScroll.scrollTo(dir ? within.width - offset.left + listOffset.left - offset.width - parentLeft : listOffset.left - offset.left, 0, 400);
  10729. }
  10730. prevIndex = $(this).index();
  10731. });
  10732. $this.on("touchmove", function(event) {
  10733. event.preventDefault();
  10734. });
  10735. });
  10736. };
  10737. $(function() {
  10738. menuInit();
  10739. });
  10740. exports.init = menuInit;
  10741. });
  10742. define("navbar", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "util.qrcode", "ui.modal", "ui.dimmer", "ui.share" ], function(require, exports, module) {
  10743. require("core");
  10744. var $ = window.Zepto, UI = $.AMUI, QRCode = require("util.qrcode"), modal = require("ui.modal"), share = require("ui.share");
  10745. var navbarInit = function() {
  10746. var $navBar = $('[data-am-widget="navbar"]');
  10747. if (!$navBar.length) return;
  10748. var $win = $(window), $body = $("body"), $navBarNav = $navBar.find(".am-navbar-nav"), $navItems = $navBar.find("li"), navItemsCounter = $navItems.length;
  10749. configItems = $navBarNav.attr("class") && parseInt($navBarNav.attr("class").match(/sm-block-grid-(\d)/)[1]) || 3,
  10750. navMinWidth = 60, //每个li最小宽度
  10751. offsetWidth = 16, $share = $navItems.filter("[data-am-navbar-share]"), $qrcode = $navItems.filter("[data-am-navbar-qrcode]"),
  10752. activeStatus = "am-active", $moreActions = $('<ul class="am-navbar-actions"></ul>', {
  10753. id: UI.utils.generateGUID("am-navbar-actions")
  10754. }), $moreLink = $('<li class="am-navbar-labels am-navbar-more"><a href="javascript: void(0);"><span class="am-icon-angle-up"></span><span class="am-navbar-label">更多</span></a></li>');
  10755. // 如果有 Fix 的工具栏则设置 body 的 padding-bottom
  10756. if ($navBar.css("position") == "fixed") {
  10757. $body.addClass("am-with-fixed-navbar");
  10758. }
  10759. if ($qrcode.length) {
  10760. var qrId = "am-navbar-qrcode";
  10761. $qrModal = $("#" + qrId);
  10762. if (!$qrModal.length) {
  10763. var qrImg = $qrcode.attr("data-am-navbar-qrcode"), $qrModal = $('<div class="am-modal am-modal-no-btn" id="">' + '<div class="am-modal-dialog"><div class="am-modal-bd"></div></div>' + "</div>", {
  10764. id: qrId
  10765. }), $qrContainer = $qrModal.find(".am-modal-bd");
  10766. // 判断上传自定义的二维码没有,否则生成二维码
  10767. if (qrImg) {
  10768. $qrContainer.html('<img src="' + qrImg + '"/>');
  10769. } else {
  10770. var qrnode = new QRCode({
  10771. render: "canvas",
  10772. correctLevel: 0,
  10773. text: window.location.href,
  10774. width: 200,
  10775. height: 200,
  10776. background: "#fff",
  10777. foreground: "#000"
  10778. });
  10779. $qrContainer.html(qrnode);
  10780. }
  10781. $body.append($qrModal);
  10782. }
  10783. $qrcode.on("click", function(e) {
  10784. e.preventDefault();
  10785. $qrModal.modal();
  10786. });
  10787. }
  10788. if (navItemsCounter > configItems && navItemsCounter > calcSuiteItems()) {
  10789. initActions();
  10790. }
  10791. function initActions() {
  10792. $navBarNav.append($moreLink);
  10793. $navBarNav.find("li").not(".am-navbar-more").slice(calcSuiteItems() - 1).appendTo($moreActions);
  10794. // Append more actions
  10795. $navBar.append($moreActions);
  10796. }
  10797. function checkNavBarItems() {
  10798. // console.log('best: %d', calcSuiteItems());
  10799. if (calcSuiteItems() >= navItemsCounter) {
  10800. // 显示所有链接,隐藏 more
  10801. $moreLink.hide();
  10802. $moreActions.find("li").insertBefore($moreLink);
  10803. return;
  10804. }
  10805. !$navBar.find(".am-navbar-actions").length && initActions();
  10806. $moreLink.show();
  10807. if ($navBarNav.find("li").length < calcSuiteItems()) {
  10808. $moreActions.find("li").slice(0, calcSuiteItems() - $navBarNav.find("li").length).insertBefore($moreLink);
  10809. } else if ($navBarNav.find("li").length > calcSuiteItems()) {
  10810. if ($moreActions.find("li").length) {
  10811. $navBarNav.find("li").not($moreLink).slice(calcSuiteItems() - 1).insertBefore($moreActions.find("li").first());
  10812. } else {
  10813. $navBarNav.find("li").not($moreLink).slice(calcSuiteItems() - 1).appendTo($moreActions);
  10814. }
  10815. }
  10816. }
  10817. /**
  10818. * 计算最适合显示的条目个数
  10819. * @returns {number}
  10820. */
  10821. function calcSuiteItems() {
  10822. return Math.floor(($win.width() - offsetWidth) / navMinWidth);
  10823. }
  10824. $navBar.on("click.navbar.amui", ".am-navbar-more", function(e) {
  10825. e.preventDefault();
  10826. $moreLink[$moreActions.hasClass(activeStatus) ? "removeClass" : "addClass"](activeStatus);
  10827. $moreActions.toggleClass(activeStatus);
  10828. });
  10829. if ($share.length) {
  10830. $share.on("click.navbar.amui", function(e) {
  10831. e.preventDefault();
  10832. share.toggle();
  10833. });
  10834. }
  10835. $win.on("resize.navbar.amui orientationchange.navbar.amui", UI.utils.debounce(checkNavBarItems, 150));
  10836. };
  10837. // DOMContent ready
  10838. $(function() {
  10839. navbarInit();
  10840. });
  10841. exports.init = navbarInit;
  10842. });
  10843. define("pagination", [], function(require, exports, module) {
  10844. var $ = window.Zepto;
  10845. });
  10846. define("paragraph", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.iscroll-lite" ], function(require, exports, module) {
  10847. require("core");
  10848. var $ = window.Zepto, UI = $.AMUI;
  10849. // Iscroll-lite Plugin
  10850. var IScroll = require("ui.iscroll-lite");
  10851. var paragraphInit;
  10852. /**
  10853. * 表格滚动
  10854. * @param index ID 标识,多个 paragraph 里面多个 table
  10855. */
  10856. $.fn.scrollTable = function(index) {
  10857. var $this = $(this), $parent;
  10858. $this.wrap("<div class='am-paragraph-table-container' id='am-paragraph-table-" + index + "'><div class='am-paragraph-table-scroller'></div></div>");
  10859. $parent = $this.parent();
  10860. $parent.width($this.width());
  10861. $parent.height($this.height());
  10862. var tableScroll = new IScroll("#am-paragraph-table-" + index, {
  10863. eventPassthrough: true,
  10864. scrollX: true,
  10865. scrollY: false,
  10866. preventDefault: false
  10867. });
  10868. };
  10869. paragraphInit = function() {
  10870. var $body = $("body"), $paragraph = $('[data-am-widget="paragraph"]');
  10871. $paragraph.each(function(index) {
  10872. var $this = $(this), options = UI.utils.parseOptions($this.attr("data-am-paragraph")), $index = index;
  10873. if (options.pureview) {
  10874. $this.pureview();
  10875. }
  10876. if (options.tableScrollable) {
  10877. $this.find("table").each(function(index) {
  10878. if ($(this).width() > $(window).width()) {
  10879. $(this).scrollTable($index + "-" + index);
  10880. }
  10881. });
  10882. }
  10883. });
  10884. };
  10885. $(window).on("load", function() {
  10886. paragraphInit();
  10887. });
  10888. exports.init = paragraphInit;
  10889. });
  10890. define("slider", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "zepto.flexslider" ], function(require, exports, module) {
  10891. require("core");
  10892. require("zepto.flexslider");
  10893. var $ = window.Zepto, UI = $.AMUI;
  10894. var sliderInit = function() {
  10895. var $sliders = $('[data-am-widget="slider"]');
  10896. $sliders.not(".am-slider-manual").each(function(i, item) {
  10897. var options = UI.utils.parseOptions($(item).attr("data-am-slider"));
  10898. $(item).flexslider(options);
  10899. });
  10900. };
  10901. $(document).on("ready", sliderInit);
  10902. exports.init = sliderInit;
  10903. });
  10904. define("sohucs", [], function(require, exports, module) {
  10905. var $ = window.Zepto;
  10906. var sohuCSInit = function() {
  10907. if (!$("#SOHUCS").length) return;
  10908. var $sohucs = $('[data-am-widget="sohucs"]'), appid = $sohucs.attr("data-am-sohucs-appid"), conf = $sohucs.attr("data-am-sohucs-conf"), $cy = $("<script></script>", {
  10909. charset: "utf-8",
  10910. id: "changyan_mobile_js",
  10911. src: "http://changyan.sohu.com/upload/mobile/wap-js/changyan_mobile.js?client_id=" + appid + "&conf=" + conf
  10912. });
  10913. $("body").append($cy);
  10914. };
  10915. // Lazy load
  10916. $(window).on("load", sohuCSInit);
  10917. exports.init = sohuCSInit;
  10918. });
  10919. define("tabs", [ "core", "zepto.extend.fx", "zepto.extend.data", "zepto.extend.selector", "ui.tabs", "util.hammer" ], function(require, exports, module) {
  10920. require("core");
  10921. require("ui.tabs");
  10922. var $ = window.Zepto;
  10923. var tabsInit = function() {
  10924. $('[data-am-widget="tabs"]').tabs();
  10925. };
  10926. $(function() {
  10927. tabsInit();
  10928. });
  10929. exports.init = tabsInit;
  10930. });
  10931. define("titlebar", [], function(require, exports, module) {
  10932. var $ = window.Zepto;
  10933. });
  10934. seajs.use(["core","util.fastclick","util.hammer","zepto.outerdemension","zepto.extend.data","zepto.extend.fx","zepto.extend.selector","ui.add2home","ui.alert","ui.button","ui.collapse","ui.dimmer","ui.dropdown","ui.iscroll-lite","ui.modal","ui.offcanvas","ui.popover","ui.progress","ui.pureview","ui.scrollspy","ui.scrollspynav","ui.share","ui.smooth-scroll","ui.sticky","ui.tabs","util.cookie","util.fullscreen","util.qrcode","zepto.flexslider","zepto.pinchzoom","accordion","divider","duoshuo","figure","footer","gallery","gotop","header","intro","list_news","map","mechat","menu","navbar","pagination","paragraph","slider","sohucs","tabs","titlebar"]);