slideout.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Slideout=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. 'use strict';
  3. /**
  4. * Module dependencies
  5. */
  6. var decouple = require('decouple');
  7. var Emitter = require('emitter');
  8. /**
  9. * Privates
  10. */
  11. var scrollTimeout;
  12. var scrolling = false;
  13. var doc = window.document;
  14. var html = doc.documentElement;
  15. var msPointerSupported = window.navigator.msPointerEnabled;
  16. var touch = {
  17. 'start': msPointerSupported ? 'MSPointerDown' : 'touchstart',
  18. 'move': msPointerSupported ? 'MSPointerMove' : 'touchmove',
  19. 'end': msPointerSupported ? 'MSPointerUp' : 'touchend'
  20. };
  21. var prefix = (function prefix() {
  22. var regex = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/;
  23. var styleDeclaration = doc.getElementsByTagName('script')[0].style;
  24. for (var prop in styleDeclaration) {
  25. if (regex.test(prop)) {
  26. return '-' + prop.match(regex)[0].toLowerCase() + '-';
  27. }
  28. }
  29. // Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
  30. // However (prop in style) returns the correct value, so we'll have to test for
  31. // the precence of a specific property
  32. if ('WebkitOpacity' in styleDeclaration) { return '-webkit-'; }
  33. if ('KhtmlOpacity' in styleDeclaration) { return '-khtml-'; }
  34. return '';
  35. }());
  36. function extend(destination, from) {
  37. for (var prop in from) {
  38. if (from[prop]) {
  39. destination[prop] = from[prop];
  40. }
  41. }
  42. return destination;
  43. }
  44. function inherits(child, uber) {
  45. child.prototype = extend(child.prototype || {}, uber.prototype);
  46. }
  47. /**
  48. * Slideout constructor
  49. */
  50. function Slideout(options) {
  51. options = options || {};
  52. // Sets default values
  53. this._startOffsetX = 0;
  54. this._currentOffsetX = 0;
  55. this._opening = false;
  56. this._moved = false;
  57. this._opened = false;
  58. this._preventOpen = false;
  59. this._touch = options.touch === undefined ? true : options.touch && true;
  60. // Sets panel
  61. this.panel = options.panel;
  62. this.menu = options.menu;
  63. // Sets classnames
  64. if(this.panel.className.search('slideout-panel') === -1) { this.panel.className += ' slideout-panel'; }
  65. if(this.menu.className.search('slideout-menu') === -1) { this.menu.className += ' slideout-menu'; }
  66. // Sets options
  67. this._fx = options.fx || 'ease';
  68. this._duration = parseInt(options.duration, 10) || 300;
  69. this._tolerance = parseInt(options.tolerance, 10) || 70;
  70. this._padding = this._translateTo = parseInt(options.padding, 10) || 256;
  71. this._orientation = options.side === 'right' ? -1 : 1;
  72. this._translateTo *= this._orientation;
  73. // Init touch events
  74. if (this._touch) {
  75. this._initTouchEvents();
  76. }
  77. }
  78. /**
  79. * Inherits from Emitter
  80. */
  81. inherits(Slideout, Emitter);
  82. /**
  83. * Opens the slideout menu.
  84. */
  85. Slideout.prototype.open = function() {
  86. var self = this;
  87. this.emit('beforeopen');
  88. if (html.className.search('slideout-open') === -1) { html.className += ' slideout-open'; }
  89. this._setTransition();
  90. this._translateXTo(this._translateTo);
  91. this._opened = true;
  92. setTimeout(function() {
  93. self.panel.style.transition = self.panel.style['-webkit-transition'] = '';
  94. self.emit('open');
  95. }, this._duration + 50);
  96. return this;
  97. };
  98. /**
  99. * Closes slideout menu.
  100. */
  101. Slideout.prototype.close = function() {
  102. var self = this;
  103. if (!this.isOpen() && !this._opening) { return this; }
  104. this.emit('beforeclose');
  105. this._setTransition();
  106. this._translateXTo(0);
  107. this._opened = false;
  108. setTimeout(function() {
  109. html.className = html.className.replace(/ slideout-open/, '');
  110. self.panel.style.transition = self.panel.style['-webkit-transition'] = self.panel.style[prefix + 'transform'] = self.panel.style.transform = '';
  111. self.emit('close');
  112. }, this._duration + 50);
  113. return this;
  114. };
  115. /**
  116. * Toggles (open/close) slideout menu.
  117. */
  118. Slideout.prototype.toggle = function() {
  119. return this.isOpen() ? this.close() : this.open();
  120. };
  121. /**
  122. * Returns true if the slideout is currently open, and false if it is closed.
  123. */
  124. Slideout.prototype.isOpen = function() {
  125. return this._opened;
  126. };
  127. /**
  128. * Translates panel and updates currentOffset with a given X point
  129. */
  130. Slideout.prototype._translateXTo = function(translateX) {
  131. this._currentOffsetX = translateX;
  132. this.panel.style[prefix + 'transform'] = this.panel.style.transform = 'translate3d(' + translateX + 'px, 0, 0)';
  133. };
  134. /**
  135. * Set transition properties
  136. */
  137. Slideout.prototype._setTransition = function() {
  138. this.panel.style[prefix + 'transition'] = this.panel.style.transition = prefix + 'transform ' + this._duration + 'ms ' + this._fx;
  139. };
  140. /**
  141. * Initializes touch event
  142. */
  143. Slideout.prototype._initTouchEvents = function() {
  144. var self = this;
  145. /**
  146. * Decouple scroll event
  147. */
  148. decouple(doc, 'scroll', function() {
  149. if (!self._moved) {
  150. clearTimeout(scrollTimeout);
  151. scrolling = true;
  152. scrollTimeout = setTimeout(function() {
  153. scrolling = false;
  154. }, 250);
  155. }
  156. });
  157. /**
  158. * Prevents touchmove event if slideout is moving
  159. */
  160. doc.addEventListener(touch.move, function(eve) {
  161. if (self._moved) {
  162. eve.preventDefault();
  163. }
  164. });
  165. /**
  166. * Resets values on touchstart
  167. */
  168. this.panel.addEventListener(touch.start, function(eve) {
  169. if (typeof eve.touches === 'undefined') { return; }
  170. self._moved = false;
  171. self._opening = false;
  172. self._startOffsetX = eve.touches[0].pageX;
  173. self._preventOpen = (!self._touch || (!self.isOpen() && self.menu.clientWidth !== 0));
  174. });
  175. /**
  176. * Resets values on touchcancel
  177. */
  178. this.panel.addEventListener('touchcancel', function() {
  179. self._moved = false;
  180. self._opening = false;
  181. });
  182. /**
  183. * Toggles slideout on touchend
  184. */
  185. this.panel.addEventListener(touch.end, function() {
  186. if (self._moved) {
  187. (self._opening && Math.abs(self._currentOffsetX) > self._tolerance) ? self.open() : self.close();
  188. }
  189. self._moved = false;
  190. });
  191. /**
  192. * Translates panel on touchmove
  193. */
  194. this.panel.addEventListener(touch.move, function(eve) {
  195. if (scrolling || self._preventOpen || typeof eve.touches === 'undefined') { return; }
  196. var dif_x = eve.touches[0].clientX - self._startOffsetX;
  197. var translateX = self._currentOffsetX = dif_x;
  198. if (Math.abs(translateX) > self._padding) { return; }
  199. if (Math.abs(dif_x) > 20) {
  200. self._opening = true;
  201. var oriented_dif_x = dif_x * self._orientation;
  202. if (self._opened && oriented_dif_x > 0 || !self._opened && oriented_dif_x < 0) { return; }
  203. if (oriented_dif_x <= 0) {
  204. translateX = dif_x + self._padding * self._orientation;
  205. self._opening = false;
  206. document.querySelector('.dingombg').style.display="none";
  207. }
  208. if (!self._moved && html.className.search('slideout-open') === -1) {
  209. html.className += ' slideout-open';
  210. document.querySelector('.dingombg').style.display="block";
  211. }
  212. self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translate3d(' + translateX + 'px, 0, 0)';
  213. self.emit('translate', translateX);
  214. self._moved = true;
  215. }
  216. });
  217. };
  218. Slideout.prototype.enableTouch = function() {
  219. this._touch = true;
  220. return this;
  221. };
  222. Slideout.prototype.disableTouch = function() {
  223. this._touch = false;
  224. return this;
  225. };
  226. /**
  227. * Expose Slideout
  228. */
  229. module.exports = Slideout;
  230. },{"decouple":2,"emitter":3}],2:[function(require,module,exports){
  231. 'use strict';
  232. var requestAnimFrame = (function() {
  233. return window.requestAnimationFrame ||
  234. window.webkitRequestAnimationFrame ||
  235. function (callback) {
  236. window.setTimeout(callback, 1000 / 60);
  237. };
  238. }());
  239. function decouple(node, event, fn) {
  240. var eve,
  241. tracking = false;
  242. function captureEvent(e) {
  243. eve = e;
  244. track();
  245. }
  246. function track() {
  247. if (!tracking) {
  248. requestAnimFrame(update);
  249. tracking = true;
  250. }
  251. }
  252. function update() {
  253. fn.call(node, eve);
  254. tracking = false;
  255. }
  256. node.addEventListener(event, captureEvent, false);
  257. }
  258. /**
  259. * Expose decouple
  260. */
  261. module.exports = decouple;
  262. },{}],3:[function(require,module,exports){
  263. "use strict";
  264. var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
  265. exports.__esModule = true;
  266. /**
  267. * Creates a new instance of Emitter.
  268. * @class
  269. * @returns {Object} Returns a new instance of Emitter.
  270. * @example
  271. * // Creates a new instance of Emitter.
  272. * var Emitter = require('emitter');
  273. *
  274. * var emitter = new Emitter();
  275. */
  276. var Emitter = (function () {
  277. function Emitter() {
  278. _classCallCheck(this, Emitter);
  279. }
  280. /**
  281. * Adds a listener to the collection for the specified event.
  282. * @memberof! Emitter.prototype
  283. * @function
  284. * @param {String} event - The event name.
  285. * @param {Function} listener - A listener function to add.
  286. * @returns {Object} Returns an instance of Emitter.
  287. * @example
  288. * // Add an event listener to "foo" event.
  289. * emitter.on('foo', listener);
  290. */
  291. Emitter.prototype.on = function on(event, listener) {
  292. // Use the current collection or create it.
  293. this._eventCollection = this._eventCollection || {};
  294. // Use the current collection of an event or create it.
  295. this._eventCollection[event] = this._eventCollection[event] || [];
  296. // Appends the listener into the collection of the given event
  297. this._eventCollection[event].push(listener);
  298. return this;
  299. };
  300. /**
  301. * Adds a listener to the collection for the specified event that will be called only once.
  302. * @memberof! Emitter.prototype
  303. * @function
  304. * @param {String} event - The event name.
  305. * @param {Function} listener - A listener function to add.
  306. * @returns {Object} Returns an instance of Emitter.
  307. * @example
  308. * // Will add an event handler to "foo" event once.
  309. * emitter.once('foo', listener);
  310. */
  311. Emitter.prototype.once = function once(event, listener) {
  312. var self = this;
  313. function fn() {
  314. self.off(event, fn);
  315. listener.apply(this, arguments);
  316. }
  317. fn.listener = listener;
  318. this.on(event, fn);
  319. return this;
  320. };
  321. /**
  322. * Removes a listener from the collection for the specified event.
  323. * @memberof! Emitter.prototype
  324. * @function
  325. * @param {String} event - The event name.
  326. * @param {Function} listener - A listener function to remove.
  327. * @returns {Object} Returns an instance of Emitter.
  328. * @example
  329. * // Remove a given listener.
  330. * emitter.off('foo', listener);
  331. */
  332. Emitter.prototype.off = function off(event, listener) {
  333. var listeners = undefined;
  334. // Defines listeners value.
  335. if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
  336. return this;
  337. }
  338. listeners.forEach(function (fn, i) {
  339. if (fn === listener || fn.listener === listener) {
  340. // Removes the given listener.
  341. listeners.splice(i, 1);
  342. }
  343. });
  344. // Removes an empty event collection.
  345. if (listeners.length === 0) {
  346. delete this._eventCollection[event];
  347. }
  348. return this;
  349. };
  350. /**
  351. * Execute each item in the listener collection in order with the specified data.
  352. * @memberof! Emitter.prototype
  353. * @function
  354. * @param {String} event - The name of the event you want to emit.
  355. * @param {...Object} data - Data to pass to the listeners.
  356. * @returns {Object} Returns an instance of Emitter.
  357. * @example
  358. * // Emits the "foo" event with 'param1' and 'param2' as arguments.
  359. * emitter.emit('foo', 'param1', 'param2');
  360. */
  361. Emitter.prototype.emit = function emit(event) {
  362. var _this = this;
  363. for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  364. args[_key - 1] = arguments[_key];
  365. }
  366. var listeners = undefined;
  367. // Defines listeners value.
  368. if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
  369. return this;
  370. }
  371. // Clone listeners
  372. listeners = listeners.slice(0);
  373. listeners.forEach(function (fn) {
  374. return fn.apply(_this, args);
  375. });
  376. return this;
  377. };
  378. return Emitter;
  379. })();
  380. /**
  381. * Exports Emitter
  382. */
  383. exports["default"] = Emitter;
  384. module.exports = exports["default"];
  385. },{}]},{},[1])(1)
  386. });