pulldown.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. ;(function() {
  2. var util = {}, events = {}, base = {}, plugins_pulldown = {};
  3. util = function (exports) {
  4. var SUBSTITUTE_REG = /\\?\{([^{}]+)\}/g, EMPTY = '';
  5. var RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g, trim = String.prototype.trim;
  6. var _trim = trim ? function (str) {
  7. return str == null ? EMPTY : trim.call(str);
  8. } : function (str) {
  9. return str == null ? EMPTY : (str + '').replace(RE_TRIM, EMPTY);
  10. };
  11. function upperCase() {
  12. return arguments[1].toUpperCase();
  13. }
  14. function Empty() {
  15. }
  16. function createObject(proto, constructor) {
  17. var newProto;
  18. if (Object.create) {
  19. newProto = Object.create(proto);
  20. } else {
  21. Empty.prototype = proto;
  22. newProto = new Empty();
  23. }
  24. newProto.constructor = constructor;
  25. return newProto;
  26. }
  27. function getNodes(node, rootNode) {
  28. if (!node)
  29. return;
  30. if (node.nodeType)
  31. return [node];
  32. var rootNode = rootNode && rootNode.nodeType ? rootNode : document;
  33. if (node && typeof node === 'string') {
  34. return rootNode.querySelectorAll(node);
  35. }
  36. return;
  37. }
  38. // Useful for temporary DOM ids.
  39. var idCounter = 0;
  40. var getOffsetTop = function (el) {
  41. var offset = el.offsetTop;
  42. if (el.offsetParent != null)
  43. offset += getOffsetTop(el.offsetParent);
  44. return offset;
  45. };
  46. var getOffsetLeft = function (el) {
  47. var offset = el.offsetLeft;
  48. if (el.offsetParent != null)
  49. offset += getOffsetLeft(el.offsetParent);
  50. return offset;
  51. };
  52. var Util = {
  53. // Is a given variable an object?
  54. isObject: function (obj) {
  55. return obj === Object(obj);
  56. },
  57. isArray: Array.isArray || function (obj) {
  58. return toString.call(obj) == '[object Array]';
  59. },
  60. // Is a given array, string, or object empty?
  61. // An "empty" object has no enumerable own-properties.
  62. isEmpty: function (obj) {
  63. if (obj == null)
  64. return true;
  65. if (this.isArray(obj) || this.isString(obj))
  66. return obj.length === 0;
  67. for (var key in obj)
  68. if (this.has(obj, key))
  69. return false;
  70. return true;
  71. },
  72. mix: function (to, from, deep) {
  73. for (var i in from) {
  74. to[i] = from[i];
  75. }
  76. return to;
  77. },
  78. extend: function (r, s, px, sx) {
  79. if (!s || !r) {
  80. return r;
  81. }
  82. var sp = s.prototype, rp;
  83. // add prototype chain
  84. rp = createObject(sp, r);
  85. r.prototype = this.mix(rp, r.prototype);
  86. r.superclass = createObject(sp, s);
  87. // add prototype overrides
  88. if (px) {
  89. this.mix(rp, px);
  90. }
  91. // add object overrides
  92. if (sx) {
  93. this.mix(r, sx);
  94. }
  95. return r;
  96. },
  97. /**
  98. * test whether a string start with a specified substring
  99. * @param {String} str the whole string
  100. * @param {String} prefix a specified substring
  101. * @return {Boolean} whether str start with prefix
  102. * @member util
  103. */
  104. startsWith: function (str, prefix) {
  105. return str.lastIndexOf(prefix, 0) === 0;
  106. },
  107. /**
  108. * test whether a string end with a specified substring
  109. * @param {String} str the whole string
  110. * @param {String} suffix a specified substring
  111. * @return {Boolean} whether str end with suffix
  112. * @member util
  113. */
  114. endsWith: function (str, suffix) {
  115. var ind = str.length - suffix.length;
  116. return ind >= 0 && str.indexOf(suffix, ind) === ind;
  117. },
  118. /**
  119. * Removes the whitespace from the beginning and end of a string.
  120. * @method
  121. * @member util
  122. */
  123. trim: _trim,
  124. /**
  125. * Substitutes keywords in a string using an object/array.
  126. * Removes undef keywords and ignores escaped keywords.
  127. * @param {String} str template string
  128. * @param {Object} o json data
  129. * @member util
  130. * @param {RegExp} [regexp] to match a piece of template string
  131. */
  132. substitute: function (str, o, regexp) {
  133. if (typeof str !== 'string' || !o) {
  134. return str;
  135. }
  136. return str.replace(regexp || SUBSTITUTE_REG, function (match, name) {
  137. if (match.charAt(0) === '\\') {
  138. return match.slice(1);
  139. }
  140. return o[name] === undefined ? EMPTY : o[name];
  141. });
  142. },
  143. /**
  144. * vendors
  145. * @return { String } webkit|moz|ms|o
  146. * @memberOf Util
  147. */
  148. vendor: function () {
  149. var el = document.createElement('div').style;
  150. var vendors = [
  151. 't',
  152. 'webkitT',
  153. 'MozT',
  154. 'msT',
  155. 'OT'
  156. ], transform, i = 0, l = vendors.length;
  157. for (; i < l; i++) {
  158. transform = vendors[i] + 'ransform';
  159. if (transform in el)
  160. return vendors[i].substr(0, vendors[i].length - 1);
  161. }
  162. return false;
  163. }(),
  164. /**
  165. * add vendor to attribute
  166. * @memberOf Util
  167. * @param {String} attrName name of attribute
  168. * @return { String }
  169. **/
  170. prefixStyle: function (attrName) {
  171. if (this.vendor === false)
  172. return false;
  173. if (this.vendor === '')
  174. return attrName;
  175. return this.vendor + attrName.charAt(0).toUpperCase() + attrName.substr(1);
  176. },
  177. /**
  178. * judge if has class
  179. * @memberOf Util
  180. * @param {HTMLElement} el
  181. * @param {String} className
  182. * @return {Boolean}
  183. */
  184. hasClass: function (el, className) {
  185. return el && el.className && className && el.className.indexOf(className) != -1;
  186. },
  187. /**
  188. * add className for the element
  189. * @memberOf Util
  190. * @param {HTMLElement} el
  191. * @param {String} className
  192. */
  193. addClass: function (el, className) {
  194. if (el && className && !this.hasClass(el, className)) {
  195. el.className += ' ' + className;
  196. }
  197. },
  198. /**
  199. * remove className for the element
  200. * @memberOf Util
  201. * @param {HTMLElement} el
  202. * @param {String} className
  203. */
  204. removeClass: function (el, className) {
  205. if (el && el.className && className) {
  206. el.className = el.className.replace(className, '');
  207. }
  208. },
  209. /**
  210. * remove an element
  211. * @memberOf Util
  212. * @param {HTMLElement} el
  213. */
  214. remove: function (el) {
  215. if (!el || !el.parentNode)
  216. return;
  217. el.parentNode.removeChild(el);
  218. },
  219. /**
  220. * get offset top
  221. * @memberOf Util
  222. * @param {HTMLElement} el
  223. * @return {Number} offsetTop
  224. */
  225. getOffsetTop: getOffsetTop,
  226. /**
  227. * get offset left
  228. * @memberOf Util
  229. * @param {HTMLElement} el
  230. * @return {Number} offsetLeft
  231. */
  232. getOffsetLeft: getOffsetLeft,
  233. /**
  234. * get offset left
  235. * @memberOf Util
  236. * @param {HTMLElement} el
  237. * @param {String} selector
  238. * @param {HTMLElement} rootNode
  239. * @return {HTMLElement} parent element
  240. */
  241. findParentEl: function (el, selector, rootNode) {
  242. var rs = null, parent = null;
  243. var type = /^#/.test(selector) ? 'id' : /^\./.test(selector) ? 'class' : 'tag';
  244. var sel = selector.replace(/\.|#/g, '');
  245. if (rootNode && typeof rootNode === 'string') {
  246. rootNode = document.querySelector(rootNode);
  247. }
  248. rootNode = rootNode || document.body;
  249. if (!el || !selector)
  250. return;
  251. if (type == 'class' && el.className && el.className.match(sel)) {
  252. return el;
  253. } else if (type == 'id' && el.id && _trim(el.id) == sel) {
  254. return el;
  255. } else if (type == 'tag' && el.tagName.toLowerCase() == sel) {
  256. return el;
  257. }
  258. while (!rs) {
  259. if (parent == rootNode)
  260. break;
  261. parent = el.parentNode;
  262. if (!parent)
  263. break;
  264. if (type == 'class' && parent.className && parent.className.match(sel) || type == 'id' && parent.id && _trim(parent.id) == sel || type == 'tag' && parent.tagName && parent.tagName.toLowerCase() == sel) {
  265. rs = parent;
  266. return rs;
  267. break;
  268. } else {
  269. el = parent;
  270. }
  271. }
  272. return null;
  273. },
  274. /**
  275. * Generate a unique integer id (unique within the entire client session).
  276. * @param {String} prefix
  277. * @return {String} guid
  278. */
  279. guid: function (prefix) {
  280. var id = ++idCounter + '';
  281. return prefix ? prefix + id : id;
  282. },
  283. /**
  284. * judge if is an android os
  285. * @return {Boolean} [description]
  286. */
  287. isAndroid: function () {
  288. return /Android /.test(window.navigator.appVersion);
  289. },
  290. /**
  291. * judge if is an android device with low performance
  292. * @return {Boolean}
  293. */
  294. isBadAndroid: function () {
  295. return /Android /.test(window.navigator.appVersion) && !/Chrome\/\d/.test(window.navigator.appVersion);
  296. },
  297. px2Num: function (px) {
  298. return Number(px.replace(/px/, ''));
  299. },
  300. getNodes: getNodes,
  301. getNode: function (node, rootNode) {
  302. var nodes = getNodes(node, rootNode);
  303. return nodes && nodes[0];
  304. },
  305. stringifyStyle: function (style) {
  306. var styleStr = '';
  307. for (var i in style) {
  308. styleStr += [
  309. i,
  310. ':',
  311. style[i],
  312. ';'
  313. ].join('');
  314. }
  315. return styleStr;
  316. }
  317. };
  318. // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
  319. var names = [
  320. 'Arguments',
  321. 'Function',
  322. 'String',
  323. 'Number',
  324. 'Date',
  325. 'RegExp'
  326. ];
  327. for (var i = 0; i < names.length; i++) {
  328. Util['is' + names[i]] = function (obj) {
  329. return toString.call(obj) == '[object ' + names[i] + ']';
  330. };
  331. }
  332. if (typeof module == 'object' && module.exports) {
  333. exports = Util;
  334. } /** ignored by jsdoc **/ else {
  335. return Util;
  336. }
  337. return exports;
  338. }(util);
  339. events = function (exports) {
  340. var Util = util;
  341. // Returns a function that will be executed at most one time, no matter how
  342. // often you call it. Useful for lazy initialization.
  343. var _once = function (func) {
  344. var ran = false, memo;
  345. return function () {
  346. if (ran)
  347. return memo;
  348. ran = true;
  349. memo = func.apply(this, arguments);
  350. func = null;
  351. return memo;
  352. };
  353. };
  354. /**
  355. * @discription events
  356. * @mixin
  357. */
  358. var Events = {
  359. // Bind an event to a `callback` function. Passing `"all"` will bind
  360. // the callback to all events fired.
  361. on: function (name, callback, context) {
  362. if (!eventsApi(this, 'on', name, [
  363. callback,
  364. context
  365. ]) || !callback)
  366. return this;
  367. this._events || (this._events = {});
  368. var events = this._events[name] || (this._events[name] = []);
  369. events.push({
  370. callback: callback,
  371. context: context,
  372. ctx: context || this
  373. });
  374. return this;
  375. },
  376. // Bind an event to only be triggered a single time. After the first time
  377. // the callback is invoked, it will be removed.
  378. once: function (name, callback, context) {
  379. if (!eventsApi(this, 'once', name, [
  380. callback,
  381. context
  382. ]) || !callback)
  383. return this;
  384. var self = this;
  385. var once = _once(function () {
  386. self.off(name, once);
  387. callback.apply(this, arguments);
  388. });
  389. once._callback = callback;
  390. return this.on(name, once, context);
  391. },
  392. // Remove one or many callbacks. If `context` is null, removes all
  393. // callbacks with that function. If `callback` is null, removes all
  394. // callbacks for the event. If `name` is null, removes all bound
  395. // callbacks for all events.
  396. off: function (name, callback, context) {
  397. if (!this._events || !eventsApi(this, 'off', name, [
  398. callback,
  399. context
  400. ]))
  401. return this;
  402. // Remove all callbacks for all events.
  403. if (!name && !callback && !context) {
  404. this._events = void 0;
  405. return this;
  406. }
  407. var names = name ? [name] : Object.keys(this._events);
  408. for (var i = 0, length = names.length; i < length; i++) {
  409. name = names[i];
  410. // Bail out if there are no events stored.
  411. var events = this._events[name];
  412. if (!events)
  413. continue;
  414. // Remove all callbacks for this event.
  415. if (!callback && !context) {
  416. delete this._events[name];
  417. continue;
  418. }
  419. // Find any remaining events.
  420. var remaining = [];
  421. for (var j = 0, k = events.length; j < k; j++) {
  422. var event = events[j];
  423. if (callback && callback !== event.callback && callback !== event.callback._callback || context && context !== event.context) {
  424. remaining.push(event);
  425. }
  426. }
  427. // Replace events if there are any remaining. Otherwise, clean up.
  428. if (remaining.length) {
  429. this._events[name] = remaining;
  430. } else {
  431. delete this._events[name];
  432. }
  433. }
  434. return this;
  435. },
  436. // Trigger one or many events, firing all bound callbacks. Callbacks are
  437. // passed the same arguments as `trigger` is, apart from the event name
  438. // (unless you're listening on `"all"`, which will cause your callback to
  439. // receive the true name of the event as the first argument).
  440. trigger: function (name) {
  441. if (!this._events)
  442. return this;
  443. var args = Array.prototype.slice.call(arguments, 1);
  444. if (!eventsApi(this, 'trigger', name, args))
  445. return this;
  446. var events = this._events[name];
  447. var allEvents = this._events.all;
  448. if (events)
  449. triggerEvents(events, args);
  450. if (allEvents)
  451. triggerEvents(allEvents, arguments);
  452. return this;
  453. },
  454. // Inversion-of-control versions of `on` and `once`. Tell *this* object to
  455. // listen to an event in another object ... keeping track of what it's
  456. // listening to.
  457. listenTo: function (obj, name, callback) {
  458. var listeningTo = this._listeningTo || (this._listeningTo = {});
  459. var id = obj._listenId || (obj._listenId = Util.guid('l'));
  460. listeningTo[id] = obj;
  461. if (!callback && typeof name === 'object')
  462. callback = this;
  463. obj.on(name, callback, this);
  464. return this;
  465. },
  466. listenToOnce: function (obj, name, callback) {
  467. if (typeof name === 'object') {
  468. for (var event in name)
  469. this.listenToOnce(obj, event, name[event]);
  470. return this;
  471. }
  472. var cb = _once(function () {
  473. this.stopListening(obj, name, cb);
  474. callback.apply(this, arguments);
  475. });
  476. cb._callback = callback;
  477. return this.listenTo(obj, name, cb);
  478. },
  479. // Tell this object to stop listening to either specific events ... or
  480. // to every object it's currently listening to.
  481. stopListening: function (obj, name, callback) {
  482. var listeningTo = this._listeningTo;
  483. if (!listeningTo)
  484. return this;
  485. var remove = !name && !callback;
  486. if (!callback && typeof name === 'object')
  487. callback = this;
  488. if (obj)
  489. (listeningTo = {})[obj._listenId] = obj;
  490. for (var id in listeningTo) {
  491. obj = listeningTo[id];
  492. obj.off(name, callback, this);
  493. if (remove || Util.isEmpty(obj._events))
  494. delete this._listeningTo[id];
  495. }
  496. return this;
  497. }
  498. };
  499. // Regular expression used to split event strings.
  500. var eventSplitter = /\s+/;
  501. // Implement fancy features of the Events API such as multiple event
  502. // names `"change blur"` and jQuery-style event maps `{change: action}`
  503. // in terms of the existing API.
  504. var eventsApi = function (obj, action, name, rest) {
  505. if (!name)
  506. return true;
  507. // Handle event maps.
  508. if (typeof name === 'object') {
  509. for (var key in name) {
  510. obj[action].apply(obj, [
  511. key,
  512. name[key]
  513. ].concat(rest));
  514. }
  515. return false;
  516. }
  517. // Handle space separated event names.
  518. if (eventSplitter.test(name)) {
  519. var names = name.split(eventSplitter);
  520. for (var i = 0, length = names.length; i < length; i++) {
  521. obj[action].apply(obj, [names[i]].concat(rest));
  522. }
  523. return false;
  524. }
  525. return true;
  526. };
  527. // A difficult-to-believe, but optimized internal dispatch function for
  528. // triggering events. Tries to keep the usual cases speedy (most internal
  529. var triggerEvents = function (events, args) {
  530. var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
  531. switch (args.length) {
  532. case 0:
  533. while (++i < l)
  534. (ev = events[i]).callback.call(ev.ctx);
  535. return;
  536. case 1:
  537. while (++i < l)
  538. (ev = events[i]).callback.call(ev.ctx, a1);
  539. return;
  540. case 2:
  541. while (++i < l)
  542. (ev = events[i]).callback.call(ev.ctx, a1, a2);
  543. return;
  544. case 3:
  545. while (++i < l)
  546. (ev = events[i]).callback.call(ev.ctx, a1, a2, a3);
  547. return;
  548. default:
  549. while (++i < l)
  550. (ev = events[i]).callback.apply(ev.ctx, args);
  551. return;
  552. }
  553. };
  554. // Aliases for backwards compatibility.
  555. Events.bind = Events.on;
  556. Events.unbind = Events.off;
  557. if (typeof module == 'object' && module.exports) {
  558. exports = Events;
  559. } /** ignored by jsdoc **/ else {
  560. return Events;
  561. }
  562. return exports;
  563. }(events);
  564. base = function (exports) {
  565. var Util = util;
  566. var Events = events;
  567. /**
  568. @constructor
  569. @mixes Events
  570. */
  571. var Base = function () {
  572. };
  573. Util.mix(Base.prototype, Events);
  574. Util.mix(Base.prototype, {
  575. /**
  576. * @memberof Base
  577. * @param {object} plugin plug a plugin
  578. */
  579. plug: function (plugin) {
  580. var self = this;
  581. if (!plugin || !plugin.pluginId)
  582. return;
  583. if (!self.__plugins) {
  584. self.__plugins = [];
  585. }
  586. var __plugin = self.getPlugin(plugin.pluginId);
  587. __plugin && self.unplug(plugin.pluginId);
  588. plugin.pluginInitializer(self);
  589. self.__plugins.push(plugin);
  590. return self;
  591. },
  592. /**
  593. * @memberof Base
  594. * @param {object|string} plugin unplug a plugin by pluginId or plugin instance
  595. */
  596. unplug: function (plugin) {
  597. var self = this;
  598. if (!plugin || !self.__plugins)
  599. return;
  600. var _plugin = typeof plugin == 'string' ? self.getPlugin(plugin) : plugin;
  601. _plugin.pluginDestructor(self);
  602. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  603. if (self.__plugins[i] == _plugin) {
  604. return self.__plugins.splice(i, 1);
  605. }
  606. }
  607. },
  608. /**
  609. * @memberof Base
  610. * @param {object|string} plugin get plugin by pluginId
  611. */
  612. getPlugin: function (pluginId) {
  613. var self = this;
  614. var plugins = [];
  615. if (!self.__plugins)
  616. return;
  617. for (var i = 0, l = self.__plugins.length; i < l; i++) {
  618. if (self.__plugins[i] && self.__plugins[i].pluginId == pluginId) {
  619. plugins.push(self.__plugins[i]);
  620. }
  621. }
  622. return plugins.length > 1 ? plugins : plugins[0] || null;
  623. }
  624. });
  625. if (typeof module == 'object' && module.exports) {
  626. exports = Base;
  627. } /** ignored by jsdoc **/ else {
  628. return Base;
  629. }
  630. return exports;
  631. }(base);
  632. plugins_pulldown = function (exports) {
  633. var Util = util;
  634. var Base = base;
  635. var clsPrefix;
  636. var containerCls;
  637. var content = 'Pull Down To Refresh';
  638. var loadingContent = 'Loading...';
  639. /**
  640. * A pulldown to refresh plugin for xscroll.
  641. * @constructor
  642. * @param {object} cfg
  643. * @param {number} cfg.height
  644. * @param {string} cfg.content default html for pulldown
  645. * @param {string} cfg.downContent html for pulldown when scrollTop is smaller than cfg.height
  646. * @param {string} cfg.upContent html for pulldown when scrollTop is larger than cfg.height
  647. * @param {string} cfg.loadingContent html for pulldown when released
  648. * @param {string} cfg.clsPrefix class prefix which default value is "xs-plugin-pulldown-"
  649. * @extends {Base}
  650. */
  651. var PullDown = function (cfg) {
  652. PullDown.superclass.constructor.call(this, cfg);
  653. this.userConfig = Util.mix({
  654. content: content,
  655. height: 60,
  656. autoRefresh: true,
  657. downContent: 'Pull Down To Refresh',
  658. upContent: 'Release To Refresh',
  659. loadingContent: loadingContent,
  660. clsPrefix: 'xs-plugin-pulldown-'
  661. }, cfg);
  662. };
  663. Util.extend(PullDown, Base, {
  664. /**
  665. * a pluginId
  666. * @memberOf PullDown
  667. * @type {string}
  668. */
  669. pluginId: 'pulldown',
  670. /**
  671. * plugin initializer
  672. * @memberOf PullDown
  673. * @override Base
  674. * @return {PullDown}
  675. */
  676. pluginInitializer: function (xscroll) {
  677. var self = this;
  678. self.xscroll = xscroll.render();
  679. clsPrefix = self.userConfig.clsPrefix;
  680. self.render();
  681. return self;
  682. },
  683. /**
  684. * detroy the plugin
  685. * @memberOf PullDown
  686. * @override Base
  687. * @return {PullDown}
  688. */
  689. pluginDestructor: function () {
  690. var self = this;
  691. Util.remove(self.pulldown);
  692. self.xscroll.off('panstart', self._panStartHandler, self);
  693. self.xscroll.off('pan', self._panHandler, self);
  694. self.xscroll.off('panend', self._panEndHandler, self);
  695. self.__isRender = false;
  696. self._evtBinded = false;
  697. },
  698. /**
  699. * render pulldown plugin
  700. * @memberOf PullDown
  701. * @return {PullDown}
  702. */
  703. render: function () {
  704. var self = this;
  705. if (self.__isRender)
  706. return;
  707. self.__isRender = true;
  708. if (!self.userConfig.container) {
  709. var containerCls = clsPrefix + 'container';
  710. var height = self.userConfig.height || 60;
  711. var pulldown = self.pulldown = document.createElement('div');
  712. pulldown.className = containerCls;
  713. pulldown.style.position = 'absolute';
  714. pulldown.style.width = '100%';
  715. pulldown.style.height = height + 'px';
  716. pulldown.style.lineHeight = height + 'px';
  717. pulldown.style.top = -height + 'px';
  718. pulldown.style.textAlign = 'center';
  719. self.xscroll.container.appendChild(pulldown);
  720. self.status = 'up';
  721. Util.addClass(pulldown, clsPrefix + self.status);
  722. pulldown.innerHTML = self.userConfig[self.status + 'Content'] || self.userConfig.content;
  723. } else {
  724. // has customed container
  725. self.pulldown = self.userConfig.container;
  726. }
  727. self._bindEvt();
  728. return self;
  729. },
  730. _bindEvt: function () {
  731. var self = this;
  732. if (self._evtBinded)
  733. return;
  734. self._evtBinded = true;
  735. var pulldown = self.pulldown;
  736. var xscroll = self.xscroll;
  737. xscroll.on('pan', self._panHandler, self);
  738. xscroll.on('panstart', self._panStartHandler, self);
  739. xscroll.on('panend', self._panEndHandler, self);
  740. },
  741. _changeStatus: function (status) {
  742. var prevVal = this.status;
  743. this.status = status;
  744. if (!this.userConfig.container) {
  745. Util.removeClass(this.pulldown, clsPrefix + prevVal);
  746. Util.addClass(this.pulldown, clsPrefix + status);
  747. if (this.userConfig[status + 'Content']) {
  748. this.pulldown.innerHTML = this.userConfig[status + 'Content'];
  749. }
  750. }
  751. if (prevVal != status) {
  752. this.trigger('statuschange', {
  753. prevVal: prevVal,
  754. newVal: status
  755. });
  756. if (status == 'loading') {
  757. this.trigger('loading');
  758. }
  759. }
  760. },
  761. /**
  762. * reset the pulldown plugin
  763. * @memberOf PullDown
  764. * @param {function} callback
  765. * @return {PullDown}
  766. */
  767. reset: function (callback) {
  768. this.xscroll.boundry.resetTop();
  769. this.xscroll.boundryCheckY(callback);
  770. this._expanded = false;
  771. return this;
  772. },
  773. _panStartHandler: function (e) {
  774. clearTimeout(this.loadingItv);
  775. },
  776. _panHandler: function (e) {
  777. var self = this;
  778. var scrollTop = self.xscroll.getScrollTop();
  779. if (scrollTop > 0)
  780. return;
  781. self._changeStatus(Math.abs(scrollTop) < self.userConfig.height ? 'down' : 'up');
  782. },
  783. _panEndHandler: function (e) {
  784. var self = this;
  785. var xscroll = self.xscroll;
  786. var height = self.userConfig.height || 60;
  787. var scrollTop = xscroll.getScrollTop();
  788. if (scrollTop < -height) {
  789. //prevent default bounce
  790. e.preventDefault();
  791. xscroll.boundry.resetTop();
  792. self._changeStatus('loading');
  793. xscroll.boundry.expandTop(height);
  794. xscroll.boundryCheckY(function () {
  795. });
  796. if (self.userConfig.autoRefresh) {
  797. clearTimeout(self.loadingItv);
  798. self.loadingItv = setTimeout(function () {
  799. xscroll.boundry.resetTop();
  800. xscroll.boundryCheckY(function () {
  801. window.location.reload();
  802. });
  803. }, 800);
  804. }
  805. }
  806. }
  807. });
  808. if (typeof module == 'object' && module.exports) {
  809. exports = PullDown;
  810. } /** ignored by jsdoc **/ else if (window.XScroll && window.XScroll.Plugins) {
  811. return XScroll.Plugins.PullDown = PullDown;
  812. }
  813. return exports;
  814. }(plugins_pulldown);
  815. }());