├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bower.json
├── browserify.js
├── component.json
├── dist
├── slideout.js
└── slideout.min.js
├── index.css
├── index.js
├── package.json
└── test
├── assets
└── menu.png
├── index.html
├── index.right.html
├── test.css
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | bower_components
4 | coverage
5 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": false,
3 | "curly": true,
4 | "eqeqeq": true,
5 | "expr": true,
6 | "forin": true,
7 | "freeze": true,
8 | "funcscope": true,
9 | "globalstrict": true,
10 | "nonbsp": true,
11 | "unused": true,
12 | "debug": true,
13 | "evil": true,
14 | "lastsemic": true,
15 | "loopfunc": true,
16 | "proto": true,
17 | "scripturl": true,
18 | "strict": true,
19 | "browser": true,
20 | "browserify": true,
21 | "node": true
22 | }
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 6.9.1
4 | # Send coverage data to Coveralls
5 | after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Code style
4 | Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.**
5 |
6 | ## Modifying the code
7 |
8 | Please don't edit `/dist/slideout.js` and `/dist/slideout.min.js` files. You'll find source code in the `index.js` file.
9 |
10 | 1. Fork and clone the repo.
11 | 2. Run `npm install` to install all dependencies.
12 | 3. Create a new branch, please don't work in your `master` branch directly.
13 | 4. Open the file `/test/index.html`.
14 | 5. Code!
15 |
16 | ### Running the tests
17 |
18 | - Run `npm test` from your command line and check the console
19 | - Open the `/test/index.html` file to run test in your browser.
20 |
21 | ## Pull requests
22 |
23 | 1. Create a new branch, **please don't work in your `master` branch directly**.
24 | 2. Code!
25 | 3. Update the tests and run `npm test` to see the tests.
26 | 4. Run `npm run hint`.
27 | 5. Run `npm run dist` to build a new version.
28 | 6. Update the documentation and package to reflect any changes.
29 | 7. Push to your fork and submit a pull request.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Mango
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Slideout.js
2 |
3 | [![NPM version][npm-image]][npm-link] [![License][lic-image]][npm-link] [![Build status][travis-image]][travis-link] [![Coverage Status][coverage-image]][coverage-link] [![Dependency status][deps-image]][deps-link] [![devDependency status][devdeps-image]][devdeps-link] [![downloads][dt-image]][npm-link]
4 |
5 | > A touch slideout navigation menu for your mobile web apps.
6 |
7 | ## Features
8 |
9 | - Dependency-free.
10 | - Simple markup.
11 | - Native scrolling.
12 | - Easy customization.
13 | - CSS transforms & transitions.
14 | - Just 2 Kb! (min & gzip)
15 |
16 | ## Demo
17 |
18 | [Check out the demo](https://mango.github.io/slideout/) to see it in action (on your mobile or emulate touches on your browser).
19 |
20 |
21 |
22 | ## Installation
23 |
24 | Slideout is available on cdnjs
25 |
26 | ```html
27 |
28 | ```
29 |
30 | Also you can use one of many package managers
31 |
32 | $ npm install slideout
33 |
34 | $ spm install slideout
35 |
36 | $ bower install slideout.js
37 |
38 | $ component install mango/slideout
39 |
40 | ## Usage
41 |
42 | Implementing Slideout.js into your project is easy.
43 |
44 | First of all, you'll need to create your markup. You should have a menu (`#menu`) and a main content (`#panel`) into your body.
45 |
46 | ```html
47 |
52 |
53 |
54 |
57 |
58 | ```
59 |
60 | Add the Slideout.js styles (index.css) in your web application.
61 |
62 | ```css
63 | body {
64 | width: 100%;
65 | height: 100%;
66 | }
67 |
68 | .slideout-menu {
69 | position: fixed;
70 | top: 0;
71 | bottom: 0;
72 | width: 256px;
73 | min-height: 100vh;
74 | overflow-y: scroll;
75 | -webkit-overflow-scrolling: touch;
76 | z-index: 0;
77 | display: none;
78 | }
79 |
80 | .slideout-menu-left {
81 | left: 0;
82 | }
83 |
84 | .slideout-menu-right {
85 | right: 0;
86 | }
87 |
88 | .slideout-panel {
89 | position: relative;
90 | z-index: 1;
91 | will-change: transform;
92 | background-color: #FFF; /* A background-color is required */
93 | min-height: 100vh;
94 | }
95 |
96 | .slideout-open,
97 | .slideout-open body,
98 | .slideout-open .slideout-panel {
99 | overflow: hidden;
100 | }
101 |
102 | .slideout-open .slideout-menu {
103 | display: block;
104 | }
105 | ```
106 |
107 | Then you just include Slideout.js, create a new instance with some options and call the toggle method:
108 |
109 | ```html
110 |
111 |
124 | ```
125 |
126 | #### Full example
127 |
128 | ```html
129 |
130 |
131 |
132 |
133 | Slideout Demo
134 |
135 |
136 |
137 |
138 |
139 |
174 |
175 |
176 |
177 |
180 |
181 |
182 |
183 | ☰
184 | Panel
185 |
186 |
187 |
188 |
189 |
202 |
203 |
204 |
205 | ```
206 |
207 | ## Browser Support
208 |
209 | - Chrome (IOS, Android, desktop)
210 | - Firefox (Android, desktop)
211 | - Safari (IOS, Android, desktop)
212 | - Opera (desktop)
213 | - IE 10+ (desktop and mobile)
214 |
215 | ## API
216 |
217 | ### Slideout(options)
218 | Create a new instance of `Slideout`.
219 |
220 | - `options` (Object) - Options to customize a new instance of Slideout.
221 | - `options.panel` (HTMLElement) - The DOM element that contains all your application content (`.slideout-panel`).
222 | - `options.menu` (HTMLElement) - The DOM element that contains your menu application (`.slideout-menu`).
223 | - `[options.duration]` (Number) - The time (milliseconds) to open/close the slideout. Default: `300`.
224 | - `[options.easing]` (String) - The CSS effect to use when animating the opening and closing of the slideout. Default: `ease`. Possible values:
225 | - `ease`
226 | - `linear`
227 | - `ease-in`
228 | - `ease-out`
229 | - `ease-in-out`
230 | - `step-start`
231 | - `step-end`
232 | - [`cubic-bezier`](http://cubic-bezier.com/)
233 | - `[options.padding]` (Number) - Default: `256`.
234 | - `[options.tolerance]` (Number) - The number of `px` needed for the menu can be opened completely, otherwise it closes. Default: `70`.
235 | - `[options.touch]` (Boolean) - Set this option to false to disable Slideout touch events. Default: `true`.
236 | - `[options.side]` (String) - The side to open the slideout (`left` or `right`). Default: `left`.
237 |
238 | ```js
239 | var slideout = new Slideout({
240 | 'panel': document.getElementById('main'),
241 | 'menu': document.getElementById('menu'),
242 | 'padding': 256,
243 | 'tolerance': 70,
244 | 'easing': 'cubic-bezier(.32,2,.55,.27)'
245 | });
246 | ```
247 |
248 | ### Slideout.open();
249 | Opens the slideout menu. It emits `beforeopen` and `open` events.
250 |
251 | ```js
252 | slideout.open();
253 | ```
254 |
255 | ### Slideout.close();
256 | Closes the slideout menu. It emits `beforeclose` and `close` events.
257 |
258 | ```js
259 | slideout.close();
260 | ```
261 |
262 | ### Slideout.toggle();
263 | Toggles (open/close) the slideout menu.
264 |
265 | ```js
266 | slideout.toggle();
267 | ```
268 |
269 | ### Slideout.isOpen();
270 | Returns `true` if the slideout is currently open, and `false` if it is closed.
271 |
272 | ```js
273 | slideout.isOpen(); // true or false
274 | ```
275 |
276 | ### Slideout.destroy();
277 | Cleans up the instance so another slideout can be created on the same area.
278 |
279 | ```js
280 | slideout.destroy();
281 | ```
282 |
283 | ### Slideout.enableTouch();
284 | Enables opening the slideout via touch events.
285 |
286 | ```js
287 | slideout.enableTouch();
288 | ```
289 |
290 | ### Slideout.disableTouch();
291 | Disables opening the slideout via touch events.
292 |
293 | ```js
294 | slideout.disableTouch();
295 | ```
296 |
297 | ### Slideout.on(event, listener);
298 | ```js
299 | slideout.on('open', function() { ... });
300 | ```
301 |
302 | ### Slideout.once(event, listener);
303 | ```js
304 | slideout.once('open', function() { ... });
305 | ```
306 |
307 | ### Slideout.off(event, listener);
308 | ```js
309 | slideout.off('open', listener);
310 | ```
311 |
312 | ### Slideout.emit(event, ...data);
313 | ```js
314 | slideout.emit('open');
315 | ```
316 |
317 | ## Events
318 |
319 | An instance of Slideout emits the following events:
320 |
321 | - `beforeclose`
322 | - `close`
323 | - `beforeopen`
324 | - `open`
325 | - `translatestart`
326 | - `translate`
327 | - `translateend`
328 |
329 | The slideout emits `translatestart`, `translate` and `translateend` events only when it is opening/closing via touch events.
330 |
331 | ```js
332 | slideout.on('translatestart', function() {
333 | console.log('Start');
334 | });
335 |
336 | slideout.on('translate', function(translated) {
337 | console.log('Translate: ' + translated); // 120 in px
338 | });
339 |
340 | slideout.on('translateend', function() {
341 | console.log('End');
342 | });
343 |
344 | // 'Start'
345 | // 'Translate 120'
346 | // 'End'
347 | ```
348 |
349 | ## `data-slideout-ignore` attribute
350 | You can use the special HTML attribute `data-slideout-ignore` to disable dragging on some elements. For example, if you have to prevent `slideout` will open when touch on carousels, maps, iframes, etc.
351 |
352 | ```html
353 |
354 |
357 |
358 |
Carousel
359 | ...
360 |
361 |
362 | ```
363 |
364 | ## npm-scripts
365 | ```
366 | $ npm run build
367 | ```
368 |
369 | ```
370 | $ npm run dist
371 | ```
372 |
373 | ```
374 | $ npm test
375 | ```
376 |
377 | ```
378 | $ npm run hint
379 | ```
380 |
381 | ## FAQ
382 |
383 | ### How to add a toggle button.
384 |
385 | ```js
386 | // vanilla js
387 | document.querySelector('.toggle-button').addEventListener('click', function() {
388 | slideout.toggle();
389 | });
390 |
391 | // jQuery
392 | $('.toggle-button').on('click', function() {
393 | slideout.toggle();
394 | });
395 | ```
396 |
397 | ### How to open slideout from right side.
398 |
399 | You should use the `side` option with the value `right`.
400 | ```js
401 | var slideout = new Slideout({
402 | 'panel': document.getElementById('content'),
403 | 'menu': document.getElementById('menu'),
404 | 'side': 'right'
405 | });
406 | ```
407 |
408 | ### How to enable slideout only on mobile devices.
409 |
410 | You should use `mediaqueries`:
411 | ```css
412 | @media screen and (min-width: 780px) {
413 | .slideout-panel {
414 | margin-left: 256px;
415 | }
416 |
417 | .slideout-menu {
418 | display: block;
419 | }
420 |
421 | .btn-hamburger {
422 | display: none;
423 | }
424 | }
425 | ```
426 | Demo: http://codepen.io/pazguille/pen/mEdQvX
427 |
428 | ### How to use slideout with a fixed header.
429 |
430 | First, you should define the styles for your fixed header:
431 | ```css
432 | .fixed-header {
433 | position: fixed;
434 | width: 100%;
435 | height: 50px;
436 | backface-visibility: hidden;
437 | z-index: 2;
438 | background-color: red;
439 | }
440 | ```
441 |
442 | Then, using slideout's events you should translate the fixed header:
443 | ```js
444 | var fixed = document.querySelector('.fixed-header');
445 |
446 | slideout.on('translate', function(translated) {
447 | fixed.style.transform = 'translateX(' + translated + 'px)';
448 | });
449 |
450 | slideout.on('beforeopen', function () {
451 | fixed.style.transition = 'transform 300ms ease';
452 | fixed.style.transform = 'translateX(256px)';
453 | });
454 |
455 | slideout.on('beforeclose', function () {
456 | fixed.style.transition = 'transform 300ms ease';
457 | fixed.style.transform = 'translateX(0px)';
458 | });
459 |
460 | slideout.on('open', function () {
461 | fixed.style.transition = '';
462 | });
463 |
464 | slideout.on('close', function () {
465 | fixed.style.transition = '';
466 | });
467 | ```
468 |
469 | Demo: http://codepen.io/pazguille/pen/ZBxdgw
470 |
471 |
472 | ### How to disable dragging on some elements.
473 | You can use the attribute `data-slideout-ignore` to disable dragging on some elements:
474 |
475 | ```html
476 |
481 |
482 |
483 |
486 |
487 |
Carousel
488 | ...
489 |
490 |
491 | ```
492 |
493 | ### How to add an overlay to close the menu on click.
494 | You can do that using the powerful `slideout` API and a little extra CSS:
495 |
496 | ```css
497 | .panel:before {
498 | content: '';
499 | display: block;
500 | background-color: rgba(0,0,0,0);
501 | transition: background-color 0.5s ease-in-out;
502 | }
503 |
504 | .panel-open:before {
505 | position: absolute;
506 | top: 0;
507 | bottom: 0;
508 | width: 100%;
509 | background-color: rgba(0,0,0,.5);
510 | z-index: 99;
511 | }
512 | ```
513 |
514 | ```js
515 | function close(eve) {
516 | eve.preventDefault();
517 | slideout.close();
518 | }
519 |
520 | slideout
521 | .on('beforeopen', function() {
522 | this.panel.classList.add('panel-open');
523 | })
524 | .on('open', function() {
525 | this.panel.addEventListener('click', close);
526 | })
527 | .on('beforeclose', function() {
528 | this.panel.classList.remove('panel-open');
529 | this.panel.removeEventListener('click', close);
530 | });
531 | ```
532 |
533 | Demo: http://codepen.io/pazguille/pen/BQYRYK
534 |
535 | ## With :heart: by
536 | - Guille Paz (Front-end developer | Web standards lover)
537 | - E-mail: [guille87paz@gmail.com](mailto:guille87paz@gmail.com)
538 | - Twitter: [@pazguille](http://twitter.com/pazguille)
539 | - Web: [http://pazguille.me](http://pazguille.me)
540 |
541 | ## License
542 | MIT license. Copyright © 2015 [Mango](http://getmango.com).
543 |
544 | [npm-image]: https://img.shields.io/npm/v/slideout.svg
545 | [lic-image]: https://img.shields.io/npm/l/slideout.svg
546 | [npm-link]: https://npmjs.org/package/slideout
547 | [travis-image]: https://img.shields.io/travis/Mango/slideout.svg
548 | [travis-link]: https://travis-ci.org/Mango/slideout
549 | [deps-image]: https://img.shields.io/david/mango/slideout.svg
550 | [deps-link]: https://david-dm.org/mango/slideout
551 | [devdeps-image]: https://img.shields.io/david/dev/mango/slideout.svg
552 | [devdeps-link]: https://david-dm.org/mango/slideout#info=devDependencies
553 | [dt-image]: https://img.shields.io/npm/dt/slideout.svg
554 | [coverage-image]: https://img.shields.io/coveralls/Mango/slideout.svg
555 | [coverage-link]: https://coveralls.io/github/Mango/slideout
556 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slideout.js",
3 | "repository": "https://github.com/Mango/slideout",
4 | "description": "A touch slideout navigation menu for your mobile web apps.",
5 | "author": "Guille Paz ",
6 | "dependencies": {
7 | "decouple": "0.0.2",
8 | "emitter-es6": "0.0.7"
9 | },
10 | "ignore": [
11 | ".*",
12 | "package.json",
13 | "component.json",
14 | "node_modules",
15 | "browserify.js",
16 | "**/.*",
17 | "bower_components",
18 | "test",
19 | "demo"
20 | ],
21 | "homepage": "https://github.com/mango/slideout",
22 | "moduleType": [
23 | "adecouplemd",
24 | "globals",
25 | "node"
26 | ],
27 | "keywords": [
28 | "slideout",
29 | "offcanvas",
30 | "menu",
31 | "touch"
32 | ],
33 | "main": "dist/slideout.js",
34 | "license": "MIT"
35 | }
36 |
--------------------------------------------------------------------------------
/browserify.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var browserify = require('browserify');
5 |
6 | if (!fs.existsSync('./dist')) {
7 | fs.mkdirSync('./dist');
8 | }
9 |
10 | browserify({'debug': true, 'standalone': 'Slideout'})
11 | .require('./index.js', {'entry': true})
12 | .bundle()
13 | .on('error', function(err) { console.log('Error : ' + err.message); })
14 | .pipe(fs.createWriteStream('dist/slideout.js'));
--------------------------------------------------------------------------------
/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slideout",
3 | "repo": "mango/slideout",
4 | "description": "A touch slideout navigation menu for your mobile web apps.",
5 | "author": "Guille Paz ",
6 | "version": "1.0.1",
7 | "twitter": "@mango",
8 | "keywords": [
9 | "slideout",
10 | "offcanvas",
11 | "menu",
12 | "touch"
13 | ],
14 | "dependencies": {
15 | "pazguille/decouple": "0.0.2",
16 | "Mango/emitter": "0.0.7"
17 | },
18 | "development": {},
19 | "license": "MIT",
20 | "scripts": [
21 | "index.js"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/dist/slideout.js:
--------------------------------------------------------------------------------
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 self._tolerance) ? self.open() : self.close();
245 | }
246 | self._moved = false;
247 | };
248 |
249 | this.panel.addEventListener(touch.end, this._onTouchEndFn);
250 |
251 | /**
252 | * Translates panel on touchmove
253 | */
254 | this._onTouchMoveFn = function(eve) {
255 | if (
256 | scrolling ||
257 | self._preventOpen ||
258 | typeof eve.touches === 'undefined' ||
259 | hasIgnoredElements(eve.target)
260 | ) {
261 | return;
262 | }
263 |
264 | var dif_x = eve.touches[0].clientX - self._startOffsetX;
265 | var translateX = self._currentOffsetX = dif_x;
266 |
267 | if (Math.abs(translateX) > self._padding) {
268 | return;
269 | }
270 |
271 | if (Math.abs(dif_x) > 20) {
272 |
273 | self._opening = true;
274 |
275 | var oriented_dif_x = dif_x * self._orientation;
276 |
277 | if (self._opened && oriented_dif_x > 0 || !self._opened && oriented_dif_x < 0) {
278 | return;
279 | }
280 |
281 | if (!self._moved) {
282 | self.emit('translatestart');
283 | }
284 |
285 | if (oriented_dif_x <= 0) {
286 | translateX = dif_x + self._padding * self._orientation;
287 | self._opening = false;
288 | }
289 |
290 | if (!(self._moved && html.classList.contains('slideout-open'))) {
291 | html.classList.add('slideout-open');
292 | }
293 |
294 | self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translateX(' + translateX + 'px)';
295 | self.emit('translate', translateX);
296 | self._moved = true;
297 | }
298 |
299 | };
300 |
301 | this.panel.addEventListener(touch.move, this._onTouchMoveFn);
302 |
303 | return this;
304 | };
305 |
306 | /**
307 | * Enable opening the slideout via touch events.
308 | */
309 | Slideout.prototype.enableTouch = function() {
310 | this._touch = true;
311 | return this;
312 | };
313 |
314 | /**
315 | * Disable opening the slideout via touch events.
316 | */
317 | Slideout.prototype.disableTouch = function() {
318 | this._touch = false;
319 | return this;
320 | };
321 |
322 | /**
323 | * Destroy an instance of slideout.
324 | */
325 | Slideout.prototype.destroy = function() {
326 | // Close before clean
327 | this.close();
328 |
329 | // Remove event listeners
330 | doc.removeEventListener(touch.move, this._preventMove);
331 | this.panel.removeEventListener(touch.start, this._resetTouchFn);
332 | this.panel.removeEventListener('touchcancel', this._onTouchCancelFn);
333 | this.panel.removeEventListener(touch.end, this._onTouchEndFn);
334 | this.panel.removeEventListener(touch.move, this._onTouchMoveFn);
335 | doc.removeEventListener('scroll', this._onScrollFn);
336 |
337 | // Remove methods
338 | this.open = this.close = function() {};
339 |
340 | // Return the instance so it can be easily dereferenced
341 | return this;
342 | };
343 |
344 | /**
345 | * Expose Slideout
346 | */
347 | module.exports = Slideout;
348 |
349 | },{"decouple":2,"emitter":3}],2:[function(require,module,exports){
350 | 'use strict';
351 |
352 | var requestAnimFrame = (function() {
353 | return window.requestAnimationFrame ||
354 | window.webkitRequestAnimationFrame ||
355 | function (callback) {
356 | window.setTimeout(callback, 1000 / 60);
357 | };
358 | }());
359 |
360 | function decouple(node, event, fn) {
361 | var eve,
362 | tracking = false;
363 |
364 | function captureEvent(e) {
365 | eve = e;
366 | track();
367 | }
368 |
369 | function track() {
370 | if (!tracking) {
371 | requestAnimFrame(update);
372 | tracking = true;
373 | }
374 | }
375 |
376 | function update() {
377 | fn.call(node, eve);
378 | tracking = false;
379 | }
380 |
381 | node.addEventListener(event, captureEvent, false);
382 |
383 | return captureEvent;
384 | }
385 |
386 | /**
387 | * Expose decouple
388 | */
389 | module.exports = decouple;
390 |
391 | },{}],3:[function(require,module,exports){
392 | "use strict";
393 |
394 | var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
395 |
396 | exports.__esModule = true;
397 | /**
398 | * Creates a new instance of Emitter.
399 | * @class
400 | * @returns {Object} Returns a new instance of Emitter.
401 | * @example
402 | * // Creates a new instance of Emitter.
403 | * var Emitter = require('emitter');
404 | *
405 | * var emitter = new Emitter();
406 | */
407 |
408 | var Emitter = (function () {
409 | function Emitter() {
410 | _classCallCheck(this, Emitter);
411 | }
412 |
413 | /**
414 | * Adds a listener to the collection for the specified event.
415 | * @memberof! Emitter.prototype
416 | * @function
417 | * @param {String} event - The event name.
418 | * @param {Function} listener - A listener function to add.
419 | * @returns {Object} Returns an instance of Emitter.
420 | * @example
421 | * // Add an event listener to "foo" event.
422 | * emitter.on('foo', listener);
423 | */
424 |
425 | Emitter.prototype.on = function on(event, listener) {
426 | // Use the current collection or create it.
427 | this._eventCollection = this._eventCollection || {};
428 |
429 | // Use the current collection of an event or create it.
430 | this._eventCollection[event] = this._eventCollection[event] || [];
431 |
432 | // Appends the listener into the collection of the given event
433 | this._eventCollection[event].push(listener);
434 |
435 | return this;
436 | };
437 |
438 | /**
439 | * Adds a listener to the collection for the specified event that will be called only once.
440 | * @memberof! Emitter.prototype
441 | * @function
442 | * @param {String} event - The event name.
443 | * @param {Function} listener - A listener function to add.
444 | * @returns {Object} Returns an instance of Emitter.
445 | * @example
446 | * // Will add an event handler to "foo" event once.
447 | * emitter.once('foo', listener);
448 | */
449 |
450 | Emitter.prototype.once = function once(event, listener) {
451 | var self = this;
452 |
453 | function fn() {
454 | self.off(event, fn);
455 | listener.apply(this, arguments);
456 | }
457 |
458 | fn.listener = listener;
459 |
460 | this.on(event, fn);
461 |
462 | return this;
463 | };
464 |
465 | /**
466 | * Removes a listener from the collection for the specified event.
467 | * @memberof! Emitter.prototype
468 | * @function
469 | * @param {String} event - The event name.
470 | * @param {Function} listener - A listener function to remove.
471 | * @returns {Object} Returns an instance of Emitter.
472 | * @example
473 | * // Remove a given listener.
474 | * emitter.off('foo', listener);
475 | */
476 |
477 | Emitter.prototype.off = function off(event, listener) {
478 |
479 | var listeners = undefined;
480 |
481 | // Defines listeners value.
482 | if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
483 | return this;
484 | }
485 |
486 | listeners.forEach(function (fn, i) {
487 | if (fn === listener || fn.listener === listener) {
488 | // Removes the given listener.
489 | listeners.splice(i, 1);
490 | }
491 | });
492 |
493 | // Removes an empty event collection.
494 | if (listeners.length === 0) {
495 | delete this._eventCollection[event];
496 | }
497 |
498 | return this;
499 | };
500 |
501 | /**
502 | * Execute each item in the listener collection in order with the specified data.
503 | * @memberof! Emitter.prototype
504 | * @function
505 | * @param {String} event - The name of the event you want to emit.
506 | * @param {...Object} data - Data to pass to the listeners.
507 | * @returns {Object} Returns an instance of Emitter.
508 | * @example
509 | * // Emits the "foo" event with 'param1' and 'param2' as arguments.
510 | * emitter.emit('foo', 'param1', 'param2');
511 | */
512 |
513 | Emitter.prototype.emit = function emit(event) {
514 | var _this = this;
515 |
516 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
517 | args[_key - 1] = arguments[_key];
518 | }
519 |
520 | var listeners = undefined;
521 |
522 | // Defines listeners value.
523 | if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
524 | return this;
525 | }
526 |
527 | // Clone listeners
528 | listeners = listeners.slice(0);
529 |
530 | listeners.forEach(function (fn) {
531 | return fn.apply(_this, args);
532 | });
533 |
534 | return this;
535 | };
536 |
537 | return Emitter;
538 | })();
539 |
540 | /**
541 | * Exports Emitter
542 | */
543 | exports["default"] = Emitter;
544 | module.exports = exports["default"];
545 | },{}]},{},[1])(1)
546 | });
547 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9kZWNvdXBsZS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9lbWl0dGVyL2Rpc3QvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiJ3VzZSBzdHJpY3QnO1xuXG4vKipcbiAqIE1vZHVsZSBkZXBlbmRlbmNpZXNcbiAqL1xudmFyIGRlY291cGxlID0gcmVxdWlyZSgnZGVjb3VwbGUnKTtcbnZhciBFbWl0dGVyID0gcmVxdWlyZSgnZW1pdHRlcicpO1xuXG4vKipcbiAqIFByaXZhdGVzXG4gKi9cbnZhciBzY3JvbGxUaW1lb3V0O1xudmFyIHNjcm9sbGluZyA9IGZhbHNlO1xudmFyIGRvYyA9IHdpbmRvdy5kb2N1bWVudDtcbnZhciBodG1sID0gZG9jLmRvY3VtZW50RWxlbWVudDtcbnZhciBtc1BvaW50ZXJTdXBwb3J0ZWQgPSB3aW5kb3cubmF2aWdhdG9yLm1zUG9pbnRlckVuYWJsZWQ7XG52YXIgdG91Y2ggPSB7XG4gICdzdGFydCc6IG1zUG9pbnRlclN1cHBvcnRlZCA/ICdNU1BvaW50ZXJEb3duJyA6ICd0b3VjaHN0YXJ0JyxcbiAgJ21vdmUnOiBtc1BvaW50ZXJTdXBwb3J0ZWQgPyAnTVNQb2ludGVyTW92ZScgOiAndG91Y2htb3ZlJyxcbiAgJ2VuZCc6IG1zUG9pbnRlclN1cHBvcnRlZCA/ICdNU1BvaW50ZXJVcCcgOiAndG91Y2hlbmQnXG59O1xudmFyIHByZWZpeCA9IChmdW5jdGlvbiBwcmVmaXgoKSB7XG4gIHZhciByZWdleCA9IC9eKFdlYmtpdHxLaHRtbHxNb3p8bXN8TykoPz1bQS1aXSkvO1xuICB2YXIgc3R5bGVEZWNsYXJhdGlvbiA9IGRvYy5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0JylbMF0uc3R5bGU7XG4gIGZvciAodmFyIHByb3AgaW4gc3R5bGVEZWNsYXJhdGlvbikge1xuICAgIGlmIChyZWdleC50ZXN0KHByb3ApKSB7XG4gICAgICByZXR1cm4gJy0nICsgcHJvcC5tYXRjaChyZWdleClbMF0udG9Mb3dlckNhc2UoKSArICctJztcbiAgICB9XG4gIH1cbiAgLy8gTm90aGluZyBmb3VuZCBzbyBmYXI/IFdlYmtpdCBkb2VzIG5vdCBlbnVtZXJhdGUgb3ZlciB0aGUgQ1NTIHByb3BlcnRpZXMgb2YgdGhlIHN0eWxlIG9iamVjdC5cbiAgLy8gSG93ZXZlciAocHJvcCBpbiBzdHlsZSkgcmV0dXJucyB0aGUgY29ycmVjdCB2YWx1ZSwgc28gd2UnbGwgaGF2ZSB0byB0ZXN0IGZvclxuICAvLyB0aGUgcHJlY2VuY2Ugb2YgYSBzcGVjaWZpYyBwcm9wZXJ0eVxuICBpZiAoJ1dlYmtpdE9wYWNpdHknIGluIHN0eWxlRGVjbGFyYXRpb24pIHsgcmV0dXJuICctd2Via2l0LSc7IH1cbiAgaWYgKCdLaHRtbE9wYWNpdHknIGluIHN0eWxlRGVjbGFyYXRpb24pIHsgcmV0dXJuICcta2h0bWwtJzsgfVxuICByZXR1cm4gJyc7XG59KCkpO1xuZnVuY3Rpb24gZXh0ZW5kKGRlc3RpbmF0aW9uLCBmcm9tKSB7XG4gIGZvciAodmFyIHByb3AgaW4gZnJvbSkge1xuICAgIGlmIChmcm9tW3Byb3BdKSB7XG4gICAgICBkZXN0aW5hdGlvbltwcm9wXSA9IGZyb21bcHJvcF07XG4gICAgfVxuICB9XG4gIHJldHVybiBkZXN0aW5hdGlvbjtcbn1cbmZ1bmN0aW9uIGluaGVyaXRzKGNoaWxkLCB1YmVyKSB7XG4gIGNoaWxkLnByb3RvdHlwZSA9IGV4dGVuZChjaGlsZC5wcm90b3R5cGUgfHwge30sIHViZXIucHJvdG90eXBlKTtcbn1cbmZ1bmN0aW9uIGhhc0lnbm9yZWRFbGVtZW50cyhlbCkge1xuICB3aGlsZSAoZWwucGFyZW50Tm9kZSkge1xuICAgIGlmIChlbC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc2xpZGVvdXQtaWdub3JlJykgIT09IG51bGwpIHtcbiAgICAgIHJldHVybiBlbDtcbiAgICB9XG4gICAgZWwgPSBlbC5wYXJlbnROb2RlO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFNsaWRlb3V0IGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNsaWRlb3V0KG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgLy8gU2V0cyBkZWZhdWx0IHZhbHVlc1xuICB0aGlzLl9zdGFydE9mZnNldFggPSAwO1xuICB0aGlzLl9jdXJyZW50T2Zmc2V0WCA9IDA7XG4gIHRoaXMuX29wZW5pbmcgPSBmYWxzZTtcbiAgdGhpcy5fbW92ZWQgPSBmYWxzZTtcbiAgdGhpcy5fb3BlbmVkID0gZmFsc2U7XG4gIHRoaXMuX3ByZXZlbnRPcGVuID0gZmFsc2U7XG5cbiAgLy8gU2V0cyBwYW5lbFxuICB0aGlzLnBhbmVsID0gb3B0aW9ucy5wYW5lbDtcbiAgdGhpcy5tZW51ID0gb3B0aW9ucy5tZW51O1xuXG4gIC8vIFNldHMgb3B0aW9uc1xuICB0aGlzLl90b3VjaCA9IG9wdGlvbnMudG91Y2ggPT09IHVuZGVmaW5lZCA/IHRydWUgOiBvcHRpb25zLnRvdWNoICYmIHRydWU7XG4gIHRoaXMuX3NpZGUgPSBvcHRpb25zLnNpZGUgfHwgJ2xlZnQnO1xuICB0aGlzLl9lYXNpbmcgPSBvcHRpb25zLmZ4IHx8wqBvcHRpb25zLmVhc2luZyB8fCAnZWFzZSc7XG4gIHRoaXMuX2R1cmF0aW9uID0gcGFyc2VJbnQob3B0aW9ucy5kdXJhdGlvbiwgMTApIHx8IDMwMDtcbiAgdGhpcy5fdG9sZXJhbmNlID0gcGFyc2VJbnQob3B0aW9ucy50b2xlcmFuY2UsIDEwKSB8fCA3MDtcbiAgdGhpcy5fcGFkZGluZyA9IHRoaXMuX3RyYW5zbGF0ZVRvID0gcGFyc2VJbnQob3B0aW9ucy5wYWRkaW5nLCAxMCkgfHwgMjU2O1xuICB0aGlzLl9vcmllbnRhdGlvbiA9IHRoaXMuX3NpZGUgPT09ICdyaWdodCcgPyAtMSA6IDE7XG4gIHRoaXMuX3RyYW5zbGF0ZVRvICo9IHRoaXMuX29yaWVudGF0aW9uO1xuXG4gIC8vIFNldHMgIGNsYXNzbmFtZXNcbiAgaWYgKCF0aGlzLnBhbmVsLmNsYXNzTGlzdC5jb250YWlucygnc2xpZGVvdXQtcGFuZWwnKSkge1xuICAgIHRoaXMucGFuZWwuY2xhc3NMaXN0LmFkZCgnc2xpZGVvdXQtcGFuZWwnKTtcbiAgfVxuICBpZiAoIXRoaXMucGFuZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1wYW5lbC0nICsgdGhpcy5fc2lkZSkpIHtcbiAgICB0aGlzLnBhbmVsLmNsYXNzTGlzdC5hZGQoJ3NsaWRlb3V0LXBhbmVsLScgKyB0aGlzLl9zaWRlKTtcbiAgfVxuICBpZiAoIXRoaXMubWVudS5jbGFzc0xpc3QuY29udGFpbnMoJ3NsaWRlb3V0LW1lbnUnKSkge1xuICAgIHRoaXMubWVudS5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1tZW51Jyk7XG4gIH1cbiAgaWYgKCF0aGlzLm1lbnUuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1tZW51LScgKyB0aGlzLl9zaWRlKSkge1xuICAgIHRoaXMubWVudS5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1tZW51LScgKyB0aGlzLl9zaWRlKTtcbiAgfVxuXG4gIC8vIEluaXQgdG91Y2ggZXZlbnRzXG4gIGlmICh0aGlzLl90b3VjaCkge1xuICAgIHRoaXMuX2luaXRUb3VjaEV2ZW50cygpO1xuICB9XG59XG5cbi8qKlxuICogSW5oZXJpdHMgZnJvbSBFbWl0dGVyXG4gKi9cbmluaGVyaXRzKFNsaWRlb3V0LCBFbWl0dGVyKTtcblxuLyoqXG4gKiBPcGVucyB0aGUgc2xpZGVvdXQgbWVudS5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLm9wZW4gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuICB0aGlzLmVtaXQoJ2JlZm9yZW9wZW4nKTtcbiAgaWYgKCFodG1sLmNsYXNzTGlzdC5jb250YWlucygnc2xpZGVvdXQtb3BlbicpKSB7XG4gICAgaHRtbC5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1vcGVuJyk7XG4gIH1cbiAgdGhpcy5fc2V0VHJhbnNpdGlvbigpO1xuICB0aGlzLl90cmFuc2xhdGVYVG8odGhpcy5fdHJhbnNsYXRlVG8pO1xuICB0aGlzLl9vcGVuZWQgPSB0cnVlO1xuICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgIHNlbGYucGFuZWwuc3R5bGUudHJhbnNpdGlvbiA9IHNlbGYucGFuZWwuc3R5bGVbJy13ZWJraXQtdHJhbnNpdGlvbiddID0gJyc7XG4gICAgc2VsZi5lbWl0KCdvcGVuJyk7XG4gIH0sIHRoaXMuX2R1cmF0aW9uICsgNTApO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogQ2xvc2VzIHNsaWRlb3V0IG1lbnUuXG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5jbG9zZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIGlmICghdGhpcy5pc09wZW4oKSAmJiAhdGhpcy5fb3BlbmluZykge1xuICAgIHJldHVybiB0aGlzO1xuICB9XG4gIHRoaXMuZW1pdCgnYmVmb3JlY2xvc2UnKTtcbiAgdGhpcy5fc2V0VHJhbnNpdGlvbigpO1xuICB0aGlzLl90cmFuc2xhdGVYVG8oMCk7XG4gIHRoaXMuX29wZW5lZCA9IGZhbHNlO1xuICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgIGh0bWwuY2xhc3NMaXN0LnJlbW92ZSgnc2xpZGVvdXQtb3BlbicpO1xuICAgIHNlbGYucGFuZWwuc3R5bGUudHJhbnNpdGlvbiA9IHNlbGYucGFuZWwuc3R5bGVbJy13ZWJraXQtdHJhbnNpdGlvbiddID0gc2VsZi5wYW5lbC5zdHlsZVtwcmVmaXggKyAndHJhbnNmb3JtJ10gPSBzZWxmLnBhbmVsLnN0eWxlLnRyYW5zZm9ybSA9ICcnO1xuICAgIHNlbGYuZW1pdCgnY2xvc2UnKTtcbiAgfSwgdGhpcy5fZHVyYXRpb24gKyA1MCk7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBUb2dnbGVzIChvcGVuL2Nsb3NlKSBzbGlkZW91dCBtZW51LlxuICovXG5TbGlkZW91dC5wcm90b3R5cGUudG9nZ2xlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmlzT3BlbigpID8gdGhpcy5jbG9zZSgpIDogdGhpcy5vcGVuKCk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgc2xpZGVvdXQgaXMgY3VycmVudGx5IG9wZW4sIGFuZCBmYWxzZSBpZiBpdCBpcyBjbG9zZWQuXG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5pc09wZW4gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuX29wZW5lZDtcbn07XG5cbi8qKlxuICogVHJhbnNsYXRlcyBwYW5lbCBhbmQgdXBkYXRlcyBjdXJyZW50T2Zmc2V0IHdpdGggYSBnaXZlbiBYIHBvaW50XG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5fdHJhbnNsYXRlWFRvID0gZnVuY3Rpb24odHJhbnNsYXRlWCkge1xuICB0aGlzLl9jdXJyZW50T2Zmc2V0WCA9IHRyYW5zbGF0ZVg7XG4gIHRoaXMucGFuZWwuc3R5bGVbcHJlZml4ICsgJ3RyYW5zZm9ybSddID0gdGhpcy5wYW5lbC5zdHlsZS50cmFuc2Zvcm0gPSAndHJhbnNsYXRlWCgnICsgdHJhbnNsYXRlWCArICdweCknO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogU2V0IHRyYW5zaXRpb24gcHJvcGVydGllc1xuICovXG5TbGlkZW91dC5wcm90b3R5cGUuX3NldFRyYW5zaXRpb24gPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wYW5lbC5zdHlsZVtwcmVmaXggKyAndHJhbnNpdGlvbiddID0gdGhpcy5wYW5lbC5zdHlsZS50cmFuc2l0aW9uID0gcHJlZml4ICsgJ3RyYW5zZm9ybSAnICsgdGhpcy5fZHVyYXRpb24gKyAnbXMgJyArIHRoaXMuX2Vhc2luZztcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIHRvdWNoIGV2ZW50XG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5faW5pdFRvdWNoRXZlbnRzID0gZnVuY3Rpb24oKSB7XG4gIHZhciBzZWxmID0gdGhpcztcblxuICAvKipcbiAgICogRGVjb3VwbGUgc2Nyb2xsIGV2ZW50XG4gICAqL1xuICB0aGlzLl9vblNjcm9sbEZuID0gZGVjb3VwbGUoZG9jLCAnc2Nyb2xsJywgZnVuY3Rpb24oKSB7XG4gICAgaWYgKCFzZWxmLl9tb3ZlZCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHNjcm9sbFRpbWVvdXQpO1xuICAgICAgc2Nyb2xsaW5nID0gdHJ1ZTtcbiAgICAgIHNjcm9sbFRpbWVvdXQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICBzY3JvbGxpbmcgPSBmYWxzZTtcbiAgICAgIH0sIDI1MCk7XG4gICAgfVxuICB9KTtcblxuICAvKipcbiAgICogUHJldmVudHMgdG91Y2htb3ZlIGV2ZW50IGlmIHNsaWRlb3V0IGlzIG1vdmluZ1xuICAgKi9cbiAgdGhpcy5fcHJldmVudE1vdmUgPSBmdW5jdGlvbihldmUpIHtcbiAgICBpZiAoc2VsZi5fbW92ZWQpIHtcbiAgICAgIGV2ZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbiAgfTtcblxuICBkb2MuYWRkRXZlbnRMaXN0ZW5lcih0b3VjaC5tb3ZlLCB0aGlzLl9wcmV2ZW50TW92ZSk7XG5cbiAgLyoqXG4gICAqIFJlc2V0cyB2YWx1ZXMgb24gdG91Y2hzdGFydFxuICAgKi9cbiAgdGhpcy5fcmVzZXRUb3VjaEZuID0gZnVuY3Rpb24oZXZlKSB7XG4gICAgaWYgKHR5cGVvZiBldmUudG91Y2hlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBzZWxmLl9tb3ZlZCA9IGZhbHNlO1xuICAgIHNlbGYuX29wZW5pbmcgPSBmYWxzZTtcbiAgICBzZWxmLl9zdGFydE9mZnNldFggPSBldmUudG91Y2hlc1swXS5wYWdlWDtcbiAgICBzZWxmLl9wcmV2ZW50T3BlbiA9ICghc2VsZi5fdG91Y2ggfHwgKCFzZWxmLmlzT3BlbigpICYmIHNlbGYubWVudS5jbGllbnRXaWR0aCAhPT0gMCkpO1xuICB9O1xuXG4gIHRoaXMucGFuZWwuYWRkRXZlbnRMaXN0ZW5lcih0b3VjaC5zdGFydCwgdGhpcy5fcmVzZXRUb3VjaEZuKTtcblxuICAvKipcbiAgICogUmVzZXRzIHZhbHVlcyBvbiB0b3VjaGNhbmNlbFxuICAgKi9cbiAgdGhpcy5fb25Ub3VjaENhbmNlbEZuID0gZnVuY3Rpb24oKSB7XG4gICAgc2VsZi5fbW92ZWQgPSBmYWxzZTtcbiAgICBzZWxmLl9vcGVuaW5nID0gZmFsc2U7XG4gIH07XG5cbiAgdGhpcy5wYW5lbC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaGNhbmNlbCcsIHRoaXMuX29uVG91Y2hDYW5jZWxGbik7XG5cbiAgLyoqXG4gICAqIFRvZ2dsZXMgc2xpZGVvdXQgb24gdG91Y2hlbmRcbiAgICovXG4gIHRoaXMuX29uVG91Y2hFbmRGbiA9IGZ1bmN0aW9uKCkge1xuICAgIGlmIChzZWxmLl9tb3ZlZCkge1xuICAgICAgc2VsZi5lbWl0KCd0cmFuc2xhdGVlbmQnKTtcbiAgICAgIChzZWxmLl9vcGVuaW5nICYmIE1hdGguYWJzKHNlbGYuX2N1cnJlbnRPZmZzZXRYKSA+IHNlbGYuX3RvbGVyYW5jZSkgPyBzZWxmLm9wZW4oKSA6IHNlbGYuY2xvc2UoKTtcbiAgICB9XG4gICAgc2VsZi5fbW92ZWQgPSBmYWxzZTtcbiAgfTtcblxuICB0aGlzLnBhbmVsLmFkZEV2ZW50TGlzdGVuZXIodG91Y2guZW5kLCB0aGlzLl9vblRvdWNoRW5kRm4pO1xuXG4gIC8qKlxuICAgKiBUcmFuc2xhdGVzIHBhbmVsIG9uIHRvdWNobW92ZVxuICAgKi9cbiAgdGhpcy5fb25Ub3VjaE1vdmVGbiA9IGZ1bmN0aW9uKGV2ZSkge1xuICAgIGlmIChcbiAgICAgIHNjcm9sbGluZyB8fFxuICAgICAgc2VsZi5fcHJldmVudE9wZW4gfHxcbiAgICAgIHR5cGVvZiBldmUudG91Y2hlcyA9PT0gJ3VuZGVmaW5lZCcgfHxcbiAgICAgIGhhc0lnbm9yZWRFbGVtZW50cyhldmUudGFyZ2V0KVxuICAgICkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBkaWZfeCA9IGV2ZS50b3VjaGVzWzBdLmNsaWVudFggLSBzZWxmLl9zdGFydE9mZnNldFg7XG4gICAgdmFyIHRyYW5zbGF0ZVggPSBzZWxmLl9jdXJyZW50T2Zmc2V0WCA9IGRpZl94O1xuXG4gICAgaWYgKE1hdGguYWJzKHRyYW5zbGF0ZVgpID4gc2VsZi5fcGFkZGluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChNYXRoLmFicyhkaWZfeCkgPiAyMCkge1xuXG4gICAgICBzZWxmLl9vcGVuaW5nID0gdHJ1ZTtcblxuICAgICAgdmFyIG9yaWVudGVkX2RpZl94ID0gZGlmX3ggKiBzZWxmLl9vcmllbnRhdGlvbjtcblxuICAgICAgaWYgKHNlbGYuX29wZW5lZCAmJiBvcmllbnRlZF9kaWZfeCA+IDAgfHwgIXNlbGYuX29wZW5lZCAmJiBvcmllbnRlZF9kaWZfeCA8IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXNlbGYuX21vdmVkKSB7XG4gICAgICAgIHNlbGYuZW1pdCgndHJhbnNsYXRlc3RhcnQnKTtcbiAgICAgIH1cblxuICAgICAgaWYgKG9yaWVudGVkX2RpZl94IDw9IDApIHtcbiAgICAgICAgdHJhbnNsYXRlWCA9IGRpZl94ICsgc2VsZi5fcGFkZGluZyAqIHNlbGYuX29yaWVudGF0aW9uO1xuICAgICAgICBzZWxmLl9vcGVuaW5nID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGlmICghKHNlbGYuX21vdmVkICYmIGh0bWwuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1vcGVuJykpKSB7XG4gICAgICAgIGh0bWwuY2xhc3NMaXN0LmFkZCgnc2xpZGVvdXQtb3BlbicpO1xuICAgICAgfVxuXG4gICAgICBzZWxmLnBhbmVsLnN0eWxlW3ByZWZpeCArICd0cmFuc2Zvcm0nXSA9IHNlbGYucGFuZWwuc3R5bGUudHJhbnNmb3JtID0gJ3RyYW5zbGF0ZVgoJyArIHRyYW5zbGF0ZVggKyAncHgpJztcbiAgICAgIHNlbGYuZW1pdCgndHJhbnNsYXRlJywgdHJhbnNsYXRlWCk7XG4gICAgICBzZWxmLl9tb3ZlZCA9IHRydWU7XG4gICAgfVxuXG4gIH07XG5cbiAgdGhpcy5wYW5lbC5hZGRFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX29uVG91Y2hNb3ZlRm4pO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBFbmFibGUgb3BlbmluZyB0aGUgc2xpZGVvdXQgdmlhIHRvdWNoIGV2ZW50cy5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmVuYWJsZVRvdWNoID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX3RvdWNoID0gdHJ1ZTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIERpc2FibGUgb3BlbmluZyB0aGUgc2xpZGVvdXQgdmlhIHRvdWNoIGV2ZW50cy5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmRpc2FibGVUb3VjaCA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLl90b3VjaCA9IGZhbHNlO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogRGVzdHJveSBhbiBpbnN0YW5jZSBvZiBzbGlkZW91dC5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgLy8gQ2xvc2UgYmVmb3JlIGNsZWFuXG4gIHRoaXMuY2xvc2UoKTtcblxuICAvLyBSZW1vdmUgZXZlbnQgbGlzdGVuZXJzXG4gIGRvYy5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX3ByZXZlbnRNb3ZlKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLnN0YXJ0LCB0aGlzLl9yZXNldFRvdWNoRm4pO1xuICB0aGlzLnBhbmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3RvdWNoY2FuY2VsJywgdGhpcy5fb25Ub3VjaENhbmNlbEZuKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLmVuZCwgdGhpcy5fb25Ub3VjaEVuZEZuKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX29uVG91Y2hNb3ZlRm4pO1xuICBkb2MucmVtb3ZlRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhpcy5fb25TY3JvbGxGbik7XG5cbiAgLy8gUmVtb3ZlIG1ldGhvZHNcbiAgdGhpcy5vcGVuID0gdGhpcy5jbG9zZSA9IGZ1bmN0aW9uKCkge307XG5cbiAgLy8gUmV0dXJuIHRoZSBpbnN0YW5jZSBzbyBpdCBjYW4gYmUgZWFzaWx5IGRlcmVmZXJlbmNlZFxuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogRXhwb3NlIFNsaWRlb3V0XG4gKi9cbm1vZHVsZS5leHBvcnRzID0gU2xpZGVvdXQ7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciByZXF1ZXN0QW5pbUZyYW1lID0gKGZ1bmN0aW9uKCkge1xuICByZXR1cm4gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fFxuICAgIHdpbmRvdy53ZWJraXRSZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHxcbiAgICBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGNhbGxiYWNrLCAxMDAwIC8gNjApO1xuICAgIH07XG59KCkpO1xuXG5mdW5jdGlvbiBkZWNvdXBsZShub2RlLCBldmVudCwgZm4pIHtcbiAgdmFyIGV2ZSxcbiAgICAgIHRyYWNraW5nID0gZmFsc2U7XG5cbiAgZnVuY3Rpb24gY2FwdHVyZUV2ZW50KGUpIHtcbiAgICBldmUgPSBlO1xuICAgIHRyYWNrKCk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFjaygpIHtcbiAgICBpZiAoIXRyYWNraW5nKSB7XG4gICAgICByZXF1ZXN0QW5pbUZyYW1lKHVwZGF0ZSk7XG4gICAgICB0cmFja2luZyA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gdXBkYXRlKCkge1xuICAgIGZuLmNhbGwobm9kZSwgZXZlKTtcbiAgICB0cmFja2luZyA9IGZhbHNlO1xuICB9XG5cbiAgbm9kZS5hZGRFdmVudExpc3RlbmVyKGV2ZW50LCBjYXB0dXJlRXZlbnQsIGZhbHNlKTtcblxuICByZXR1cm4gY2FwdHVyZUV2ZW50O1xufVxuXG4vKipcbiAqIEV4cG9zZSBkZWNvdXBsZVxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGRlY291cGxlO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBfY2xhc3NDYWxsQ2hlY2sgPSBmdW5jdGlvbiAoaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIik7IH0gfTtcblxuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZTtcbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICogQGNsYXNzXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIGEgbmV3IGluc3RhbmNlIG9mIEVtaXR0ZXIuXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICogdmFyIEVtaXR0ZXIgPSByZXF1aXJlKCdlbWl0dGVyJyk7XG4gKlxuICogdmFyIGVtaXR0ZXIgPSBuZXcgRW1pdHRlcigpO1xuICovXG5cbnZhciBFbWl0dGVyID0gKGZ1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRW1pdHRlcigpIHtcbiAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgRW1pdHRlcik7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIGxpc3RlbmVyIHRvIHRoZSBjb2xsZWN0aW9uIGZvciB0aGUgc3BlY2lmaWVkIGV2ZW50LlxuICAgKiBAbWVtYmVyb2YhIEVtaXR0ZXIucHJvdG90eXBlXG4gICAqIEBmdW5jdGlvblxuICAgKiBAcGFyYW0ge1N0cmluZ30gZXZlbnQgLSBUaGUgZXZlbnQgbmFtZS5cbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgLSBBIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIGFkZC5cbiAgICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyBBZGQgYW4gZXZlbnQgbGlzdGVuZXIgdG8gXCJmb29cIiBldmVudC5cbiAgICogZW1pdHRlci5vbignZm9vJywgbGlzdGVuZXIpO1xuICAgKi9cblxuICBFbWl0dGVyLnByb3RvdHlwZS5vbiA9IGZ1bmN0aW9uIG9uKGV2ZW50LCBsaXN0ZW5lcikge1xuICAgIC8vIFVzZSB0aGUgY3VycmVudCBjb2xsZWN0aW9uIG9yIGNyZWF0ZSBpdC5cbiAgICB0aGlzLl9ldmVudENvbGxlY3Rpb24gPSB0aGlzLl9ldmVudENvbGxlY3Rpb24gfHwge307XG5cbiAgICAvLyBVc2UgdGhlIGN1cnJlbnQgY29sbGVjdGlvbiBvZiBhbiBldmVudCBvciBjcmVhdGUgaXQuXG4gICAgdGhpcy5fZXZlbnRDb2xsZWN0aW9uW2V2ZW50XSA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0gfHwgW107XG5cbiAgICAvLyBBcHBlbmRzIHRoZSBsaXN0ZW5lciBpbnRvIHRoZSBjb2xsZWN0aW9uIG9mIHRoZSBnaXZlbiBldmVudFxuICAgIHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0ucHVzaChsaXN0ZW5lcik7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICAvKipcbiAgICogQWRkcyBhIGxpc3RlbmVyIHRvIHRoZSBjb2xsZWN0aW9uIGZvciB0aGUgc3BlY2lmaWVkIGV2ZW50IHRoYXQgd2lsbCBiZSBjYWxsZWQgb25seSBvbmNlLlxuICAgKiBAbWVtYmVyb2YhIEVtaXR0ZXIucHJvdG90eXBlXG4gICAqIEBmdW5jdGlvblxuICAgKiBAcGFyYW0ge1N0cmluZ30gZXZlbnQgLSBUaGUgZXZlbnQgbmFtZS5cbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgLSBBIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIGFkZC5cbiAgICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyBXaWxsIGFkZCBhbiBldmVudCBoYW5kbGVyIHRvIFwiZm9vXCIgZXZlbnQgb25jZS5cbiAgICogZW1pdHRlci5vbmNlKCdmb28nLCBsaXN0ZW5lcik7XG4gICAqL1xuXG4gIEVtaXR0ZXIucHJvdG90eXBlLm9uY2UgPSBmdW5jdGlvbiBvbmNlKGV2ZW50LCBsaXN0ZW5lcikge1xuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIGZ1bmN0aW9uIGZuKCkge1xuICAgICAgc2VsZi5vZmYoZXZlbnQsIGZuKTtcbiAgICAgIGxpc3RlbmVyLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgZm4ubGlzdGVuZXIgPSBsaXN0ZW5lcjtcblxuICAgIHRoaXMub24oZXZlbnQsIGZuKTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9O1xuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGEgbGlzdGVuZXIgZnJvbSB0aGUgY29sbGVjdGlvbiBmb3IgdGhlIHNwZWNpZmllZCBldmVudC5cbiAgICogQG1lbWJlcm9mISBFbWl0dGVyLnByb3RvdHlwZVxuICAgKiBAZnVuY3Rpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IGV2ZW50IC0gVGhlIGV2ZW50IG5hbWUuXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGxpc3RlbmVyIC0gQSBsaXN0ZW5lciBmdW5jdGlvbiB0byByZW1vdmUuXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IFJldHVybnMgYW4gaW5zdGFuY2Ugb2YgRW1pdHRlci5cbiAgICogQGV4YW1wbGVcbiAgICogLy8gUmVtb3ZlIGEgZ2l2ZW4gbGlzdGVuZXIuXG4gICAqIGVtaXR0ZXIub2ZmKCdmb28nLCBsaXN0ZW5lcik7XG4gICAqL1xuXG4gIEVtaXR0ZXIucHJvdG90eXBlLm9mZiA9IGZ1bmN0aW9uIG9mZihldmVudCwgbGlzdGVuZXIpIHtcblxuICAgIHZhciBsaXN0ZW5lcnMgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBEZWZpbmVzIGxpc3RlbmVycyB2YWx1ZS5cbiAgICBpZiAoIXRoaXMuX2V2ZW50Q29sbGVjdGlvbiB8fCAhKGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0pKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICBsaXN0ZW5lcnMuZm9yRWFjaChmdW5jdGlvbiAoZm4sIGkpIHtcbiAgICAgIGlmIChmbiA9PT0gbGlzdGVuZXIgfHwgZm4ubGlzdGVuZXIgPT09IGxpc3RlbmVyKSB7XG4gICAgICAgIC8vIFJlbW92ZXMgdGhlIGdpdmVuIGxpc3RlbmVyLlxuICAgICAgICBsaXN0ZW5lcnMuc3BsaWNlKGksIDEpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gUmVtb3ZlcyBhbiBlbXB0eSBldmVudCBjb2xsZWN0aW9uLlxuICAgIGlmIChsaXN0ZW5lcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICBkZWxldGUgdGhpcy5fZXZlbnRDb2xsZWN0aW9uW2V2ZW50XTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICAvKipcbiAgICogRXhlY3V0ZSBlYWNoIGl0ZW0gaW4gdGhlIGxpc3RlbmVyIGNvbGxlY3Rpb24gaW4gb3JkZXIgd2l0aCB0aGUgc3BlY2lmaWVkIGRhdGEuXG4gICAqIEBtZW1iZXJvZiEgRW1pdHRlci5wcm90b3R5cGVcbiAgICogQGZ1bmN0aW9uXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBldmVudCAtIFRoZSBuYW1lIG9mIHRoZSBldmVudCB5b3Ugd2FudCB0byBlbWl0LlxuICAgKiBAcGFyYW0gey4uLk9iamVjdH0gZGF0YSAtIERhdGEgdG8gcGFzcyB0byB0aGUgbGlzdGVuZXJzLlxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIGFuIGluc3RhbmNlIG9mIEVtaXR0ZXIuXG4gICAqIEBleGFtcGxlXG4gICAqIC8vIEVtaXRzIHRoZSBcImZvb1wiIGV2ZW50IHdpdGggJ3BhcmFtMScgYW5kICdwYXJhbTInIGFzIGFyZ3VtZW50cy5cbiAgICogZW1pdHRlci5lbWl0KCdmb28nLCAncGFyYW0xJywgJ3BhcmFtMicpO1xuICAgKi9cblxuICBFbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24gZW1pdChldmVudCkge1xuICAgIHZhciBfdGhpcyA9IHRoaXM7XG5cbiAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgYXJncyA9IEFycmF5KF9sZW4gPiAxID8gX2xlbiAtIDEgOiAwKSwgX2tleSA9IDE7IF9rZXkgPCBfbGVuOyBfa2V5KyspIHtcbiAgICAgIGFyZ3NbX2tleSAtIDFdID0gYXJndW1lbnRzW19rZXldO1xuICAgIH1cblxuICAgIHZhciBsaXN0ZW5lcnMgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBEZWZpbmVzIGxpc3RlbmVycyB2YWx1ZS5cbiAgICBpZiAoIXRoaXMuX2V2ZW50Q29sbGVjdGlvbiB8fCAhKGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0pKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvLyBDbG9uZSBsaXN0ZW5lcnNcbiAgICBsaXN0ZW5lcnMgPSBsaXN0ZW5lcnMuc2xpY2UoMCk7XG5cbiAgICBsaXN0ZW5lcnMuZm9yRWFjaChmdW5jdGlvbiAoZm4pIHtcbiAgICAgIHJldHVybiBmbi5hcHBseShfdGhpcywgYXJncyk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICByZXR1cm4gRW1pdHRlcjtcbn0pKCk7XG5cbi8qKlxuICogRXhwb3J0cyBFbWl0dGVyXG4gKi9cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gRW1pdHRlcjtcbm1vZHVsZS5leHBvcnRzID0gZXhwb3J0c1tcImRlZmF1bHRcIl07Il19
548 |
--------------------------------------------------------------------------------
/dist/slideout.min.js:
--------------------------------------------------------------------------------
1 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.Slideout=t()}}(function(){var t,e,n;return function i(t,e,n){function o(r,a){if(!e[r]){if(!t[r]){var u=typeof require=="function"&&require;if(!a&&u)return u(r,!0);if(s)return s(r,!0);var l=new Error("Cannot find module '"+r+"'");throw l.code="MODULE_NOT_FOUND",l}var h=e[r]={exports:{}};t[r][0].call(h.exports,function(e){var n=t[r][1][e];return o(n?n:e)},h,h.exports,i,t,e,n)}return e[r].exports}var s=typeof require=="function"&&require;for(var r=0;rt._tolerance?t.open():t.close()}t._moved=false};this.panel.addEventListener(h.end,this._onTouchEndFn);this._onTouchMoveFn=function(e){if(r||t._preventOpen||typeof e.touches==="undefined"||d(e.target)){return}var n=e.touches[0].clientX-t._startOffsetX;var i=t._currentOffsetX=n;if(Math.abs(i)>t._padding){return}if(Math.abs(n)>20){t._opening=true;var o=n*t._orientation;if(t._opened&&o>0||!t._opened&&o<0){return}if(!t._moved){t.emit("translatestart")}if(o<=0){i=n+t._padding*t._orientation;t._opening=false}if(!(t._moved&&u.classList.contains("slideout-open"))){u.classList.add("slideout-open")}t.panel.style[f+"transform"]=t.panel.style.transform="translateX("+i+"px)";t.emit("translate",i);t._moved=true}};this.panel.addEventListener(h.move,this._onTouchMoveFn);return this};_.prototype.enableTouch=function(){this._touch=true;return this};_.prototype.disableTouch=function(){this._touch=false;return this};_.prototype.destroy=function(){this.close();a.removeEventListener(h.move,this._preventMove);this.panel.removeEventListener(h.start,this._resetTouchFn);this.panel.removeEventListener("touchcancel",this._onTouchCancelFn);this.panel.removeEventListener(h.end,this._onTouchEndFn);this.panel.removeEventListener(h.move,this._onTouchMoveFn);a.removeEventListener("scroll",this._onScrollFn);this.open=this.close=function(){};return this};e.exports=_},{decouple:2,emitter:3}],2:[function(t,e,n){"use strict";var i=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}();function o(t,e,n){var o,s=false;function r(t){o=t;a()}function a(){if(!s){i(u);s=true}}function u(){n.call(t,o);s=false}t.addEventListener(e,r,false);return r}e.exports=o},{}],3:[function(t,e,n){"use strict";var i=function(t,e){if(!(t instanceof e)){throw new TypeError("Cannot call a class as a function")}};n.__esModule=true;var o=function(){function t(){i(this,t)}t.prototype.on=function e(t,n){this._eventCollection=this._eventCollection||{};this._eventCollection[t]=this._eventCollection[t]||[];this._eventCollection[t].push(n);return this};t.prototype.once=function n(t,e){var n=this;function i(){n.off(t,i);e.apply(this,arguments)}i.listener=e;this.on(t,i);return this};t.prototype.off=function o(t,e){var n=undefined;if(!this._eventCollection||!(n=this._eventCollection[t])){return this}n.forEach(function(t,i){if(t===e||t.listener===e){n.splice(i,1)}});if(n.length===0){delete this._eventCollection[t]}return this};t.prototype.emit=function s(t){var e=this;for(var n=arguments.length,i=Array(n>1?n-1:0),o=1;o self._tolerance) ? self.open() : self.close();
244 | }
245 | self._moved = false;
246 | };
247 |
248 | this.panel.addEventListener(touch.end, this._onTouchEndFn);
249 |
250 | /**
251 | * Translates panel on touchmove
252 | */
253 | this._onTouchMoveFn = function(eve) {
254 | if (
255 | scrolling ||
256 | self._preventOpen ||
257 | typeof eve.touches === 'undefined' ||
258 | hasIgnoredElements(eve.target)
259 | ) {
260 | return;
261 | }
262 |
263 | var dif_x = eve.touches[0].clientX - self._startOffsetX;
264 | var translateX = self._currentOffsetX = dif_x;
265 |
266 | if (Math.abs(translateX) > self._padding) {
267 | return;
268 | }
269 |
270 | if (Math.abs(dif_x) > 20) {
271 |
272 | self._opening = true;
273 |
274 | var oriented_dif_x = dif_x * self._orientation;
275 |
276 | if (self._opened && oriented_dif_x > 0 || !self._opened && oriented_dif_x < 0) {
277 | return;
278 | }
279 |
280 | if (!self._moved) {
281 | self.emit('translatestart');
282 | }
283 |
284 | if (oriented_dif_x <= 0) {
285 | translateX = dif_x + self._padding * self._orientation;
286 | self._opening = false;
287 | }
288 |
289 | if (!(self._moved && html.classList.contains('slideout-open'))) {
290 | html.classList.add('slideout-open');
291 | }
292 |
293 | self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translateX(' + translateX + 'px)';
294 | self.emit('translate', translateX);
295 | self._moved = true;
296 | }
297 |
298 | };
299 |
300 | this.panel.addEventListener(touch.move, this._onTouchMoveFn);
301 |
302 | return this;
303 | };
304 |
305 | /**
306 | * Enable opening the slideout via touch events.
307 | */
308 | Slideout.prototype.enableTouch = function() {
309 | this._touch = true;
310 | return this;
311 | };
312 |
313 | /**
314 | * Disable opening the slideout via touch events.
315 | */
316 | Slideout.prototype.disableTouch = function() {
317 | this._touch = false;
318 | return this;
319 | };
320 |
321 | /**
322 | * Destroy an instance of slideout.
323 | */
324 | Slideout.prototype.destroy = function() {
325 | // Close before clean
326 | this.close();
327 |
328 | // Remove event listeners
329 | doc.removeEventListener(touch.move, this._preventMove);
330 | this.panel.removeEventListener(touch.start, this._resetTouchFn);
331 | this.panel.removeEventListener('touchcancel', this._onTouchCancelFn);
332 | this.panel.removeEventListener(touch.end, this._onTouchEndFn);
333 | this.panel.removeEventListener(touch.move, this._onTouchMoveFn);
334 | doc.removeEventListener('scroll', this._onScrollFn);
335 |
336 | // Remove methods
337 | this.open = this.close = function() {};
338 |
339 | // Return the instance so it can be easily dereferenced
340 | return this;
341 | };
342 |
343 | /**
344 | * Expose Slideout
345 | */
346 | module.exports = Slideout;
347 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slideout",
3 | "repository": "git@github.com:mango/slideout.git",
4 | "description": "A touch slideout navigation menu for your mobile web apps.",
5 | "author": "Guille Paz ",
6 | "version": "1.0.1",
7 | "scripts": {
8 | "build": "node browserify.js",
9 | "test": "npm run build && istanbul cover _mocha",
10 | "dist": "node browserify.js && uglifyjs ./dist/slideout.js -m -o ./dist/slideout.min.js",
11 | "hint": "jshint index.js"
12 | },
13 | "dependencies": {
14 | "decouple": "0.0.2",
15 | "emitter": "git+https://github.com/Mango/emitter.git#0.0.7"
16 | },
17 | "devDependencies": {
18 | "better-assert": "1.0.2",
19 | "browserify": "13.1.1",
20 | "coveralls": "2.11.15",
21 | "istanbul": "0.4.5",
22 | "jsdom": "9.8.3",
23 | "jshint": "2.9.4",
24 | "mocha": "3.1.2",
25 | "uglify-js": "2.7.4"
26 | },
27 | "main": "index.js",
28 | "keywords": [
29 | "slideout",
30 | "offcanvas",
31 | "menu",
32 | "touch"
33 | ],
34 | "license": "MIT",
35 | "spm": {
36 | "dependencies": {
37 | "decouple": "0.0.2",
38 | "emitter": "git+https://github.com/Mango/emitter.git#0.0.7"
39 | },
40 | "main": "index.js"
41 | },
42 | "filename": "slideout.min.js",
43 | "autoupdate": {
44 | "source": "git",
45 | "target": "git://github.com/Mango/slideout.git",
46 | "basePath": "dist",
47 | "files": [
48 | "*.js"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/assets/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mango/slideout/6eb8440ddc7e94194723f86f6580de4d101762a3/test/assets/menu.png
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Slideout tests
17 |
18 |
19 |
20 |
67 |
68 |
69 |
73 |
74 |
78 |
79 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
107 |
108 |
--------------------------------------------------------------------------------
/test/index.right.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
20 | Slideout right tests
21 |
22 |
23 |
24 |
71 |
72 |
73 |
77 |
78 |
82 |
83 |
86 |
87 |
88 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/test/test.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
2 | html,
3 | body {
4 | font: 100%/1.4em 'Helvetica Neue', Helvetica, Arial, sans-serif;
5 | margin: 0 auto;
6 | color: #222;
7 | -webkit-text-size-adjust: none;
8 | -webkit-font-smoothing: antialiased;
9 | }
10 |
11 | body,
12 | .panel {
13 | background-color: #fff;
14 | }
15 |
16 | .menu {
17 | background-color: #1D1F20;
18 | background-image: linear-gradient(145deg, #1D1F20, #404348);
19 | }
20 |
21 | a {
22 | color: #4B5;
23 | text-decoration: none;
24 | }
25 |
26 | .menu a {
27 | color: #fff;
28 | }
29 |
30 | .menu a:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .menu-header {
35 | border-bottom: 1px solid #2a2d2f;
36 | padding: 20px;
37 | background-size: 32px;
38 | }
39 |
40 | .menu-header-title {
41 | font-weight: 400;
42 | letter-spacing: 0.5px;
43 | margin: 0;
44 | }
45 |
46 | .menu-section {
47 | margin: 25px 0;
48 | }
49 |
50 | .menu-section-title {
51 | text-transform: uppercase;
52 | color: #85888d;
53 | font-weight: 200;
54 | font-size: 13px;
55 | letter-spacing: 1px;
56 | padding: 0 20px;
57 | margin:0;
58 | }
59 |
60 | .menu-section-list {
61 | padding:0;
62 | margin: 10px 0;
63 | list-style:none;
64 | }
65 |
66 | .menu-section-list a {
67 | display: block;
68 | padding: 10px 20px;
69 | }
70 |
71 | .panel {
72 | text-align: center;
73 | padding-top: 5px;
74 | min-height: 100%;
75 | }
76 |
77 | #mocha {
78 | text-align: left;
79 | margin:0 !important;
80 | }
81 |
82 | #mocha-stats {
83 | top: 70px;
84 | }
85 |
86 | /**
87 | * Header
88 | */
89 | .panel-header {
90 | margin: 120px auto 55px;
91 | }
92 |
93 | .title {
94 | font-size: 3.2em;
95 | line-height: 1em;
96 | margin: 0 0 15px;
97 | color: #4B5;
98 | font-weight: 400;
99 | }
100 |
101 | /**
102 | * hamburger
103 | */
104 | .btn-hamburger {
105 | border: none;
106 | position: absolute;
107 | top: 12px;
108 | left: 12px;
109 | outline:none;
110 | background: url('assets/menu.png') no-repeat center;
111 | width: 50px;
112 | height: 50px;
113 | }
114 |
115 | /**
116 | * Boxes
117 | */
118 | .box {
119 | border: 1px solid #4b5;
120 | border-radius: 4px;
121 | text-align: left;
122 | margin: 50px 10px;
123 | position: relative;
124 | }
125 |
126 | .box:before,
127 | .box:after {
128 | content: ' ';
129 | display: inline-block;
130 | width: 1px;
131 | height: 50px;
132 | border-left: 1px solid #4b5;
133 | position: absolute;
134 | left: 50%;
135 | }
136 |
137 | .box:before {
138 | top: -50px;
139 | }
140 |
141 | .box:after {
142 | bottom: -50px;
143 | }
144 |
145 | .box-title {
146 | margin: 0;
147 | padding:10px 20px;
148 | border-bottom: 1px solid #4b5;
149 | color: #4b5;
150 | font-size: 1.2em;
151 | font-weight: 400;
152 | }
153 |
154 | .box-content {
155 | padding:20px;
156 | background-color: #f8f8f8;
157 | }
158 |
159 | /**
160 | * Medium Screens
161 | */
162 | @media all and (min-width:40em) {
163 |
164 | .btn-hamburger {
165 | top: 20px;
166 | left: 30px;
167 | }
168 |
169 | .panel-header {
170 | margin-top: 40px;
171 | width: 455px;
172 | }
173 |
174 | .title {
175 | font-size: 4.2em;
176 | }
177 | }
178 |
179 | /**
180 | * Large Screens
181 | */
182 | @media all and (min-width: 54em) {
183 | .box {
184 | width: 70%;
185 | max-width: 1200px;
186 | margin: 50px auto;
187 | }
188 | }
189 |
190 | /**
191 | * Footer
192 | */
193 | .panel-footer {
194 | margin: 10px auto 20px;
195 | }
196 |
197 | .panel-footer p {
198 | padding-bottom: 20px;
199 | }
200 |
201 | .heart {
202 | font-style: normal;
203 | font-weight: 500;
204 | color: #c0392b;
205 | text-decoration: none;
206 | }
207 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | if (exports) {
2 | var fs = require('fs');
3 | var jsdom = require('jsdom').jsdom;
4 | var html = fs.readFileSync('./test/index.html', 'utf-8');
5 | var document = jsdom(html);
6 | window = document.defaultView;
7 | var Slideout = require('../');
8 | var assert = require('better-assert');
9 | }
10 |
11 | var doc = window.document;
12 | var beforeopenEvent = false;
13 | var openEvent = false;
14 | var beforecloseEvent = false;
15 | var closeEvent = false;
16 | var slideout = new Slideout({
17 | 'panel': doc.getElementById('panel'),
18 | 'menu': doc.getElementById('menu'),
19 | });
20 |
21 | slideout
22 | .on('beforeopen', function() {
23 | beforeopenEvent = true;
24 | })
25 | .on('open', function() {
26 | openEvent = true;
27 | })
28 | .on('beforeclose', function() {
29 | beforecloseEvent = true;
30 | })
31 | .on('close', function() {
32 | closeEvent = true;
33 | });
34 |
35 | describe('Slideout', function () {
36 |
37 | it('should be defined.', function () {
38 | assert(Slideout !== undefined);
39 | });
40 |
41 | it('should be a function.', function () {
42 | assert(typeof Slideout === 'function');
43 | });
44 |
45 | it('should return a new instance.', function () {
46 | assert(slideout instanceof Slideout);
47 | });
48 |
49 | describe('should have the following methods:', function () {
50 | var methods = [
51 | 'open',
52 | 'close',
53 | 'toggle',
54 | 'isOpen',
55 | '_initTouchEvents',
56 | '_translateXTo',
57 | '_setTransition',
58 | 'on',
59 | 'once',
60 | 'off',
61 | 'emit'
62 | ];
63 | var i = 0;
64 | var len = methods.length;
65 | for (i; i < len; i += 1) {
66 | (function (i) {
67 | it('.' + methods[i] + '()', function (done) {
68 | assert(typeof slideout[methods[i]] === 'function');
69 | done()
70 | });
71 | }(i));
72 | }
73 | });
74 |
75 | describe('should define the following properties:', function () {
76 | var properties = [
77 | 'panel',
78 | 'menu',
79 | '_startOffsetX',
80 | '_currentOffsetX',
81 | '_opening',
82 | '_moved',
83 | '_opened',
84 | '_easing',
85 | '_duration',
86 | '_tolerance',
87 | '_padding',
88 | '_touch',
89 | '_side'
90 | ];
91 | var i = 0;
92 | var len = properties.length;
93 | for (i; i < len; i += 1) {
94 | (function (i) {
95 | it('.' + properties[i] + '()', function (done) {
96 | assert(slideout[properties[i]] !== undefined);
97 | done()
98 | });
99 | }(i));
100 | }
101 | });
102 |
103 | it('should add classnames to panel and menu DOM elements.', function () {
104 | assert(slideout.panel.className.search('slideout-panel') !== -1);
105 | assert(slideout.panel.className.search('slideout-panel-left') !== -1);
106 | assert(slideout.menu.className.search('slideout-menu') !== -1);
107 | assert(slideout.menu.className.search('slideout-menu-left') !== -1);
108 | });
109 |
110 | describe('.open()', function () {
111 | it('should add "slideout-open" classname to HTML.', function () {
112 | assert(doc.documentElement.className.search('slideout-open') === -1);
113 | slideout.open();
114 | assert(doc.documentElement.className.search('slideout-open') !== -1);
115 | });
116 |
117 | it('should translateX the panel to the given padding.', function () {
118 | assert(slideout.panel.style.transform === 'translateX(256px)');
119 | assert(slideout.panel.style.transition.search(/transform 300ms ease/) !== -1);
120 | });
121 |
122 | it('should set _opened to true.', function () {
123 | assert(slideout._opened === true);
124 | });
125 |
126 | it('should emit "beforeopen" event.', function () {
127 | assert(beforeopenEvent === true);
128 | });
129 |
130 | it('should emit "open" event.', function (done) {
131 | setTimeout(function(){
132 | assert(openEvent === true);
133 | done();
134 | }, 400);
135 |
136 | });
137 | });
138 |
139 | describe('.isOpen()', function () {
140 | it('should return true if the slideout is opened.', function () {
141 | assert(slideout.isOpen());
142 | });
143 | });
144 |
145 | describe('.close()', function () {
146 | it('should remove "slideout-open" classname to HTML.', function (done) {
147 | assert(doc.documentElement.className.search('slideout-open') !== -1);
148 | slideout.close();
149 | setTimeout(function(){
150 | assert(doc.documentElement.className.search('slideout-open') === -1);
151 | done();
152 | }, 350);
153 |
154 | });
155 |
156 | it('should translateX the panel to 0.', function () {
157 | assert(slideout.panel.style.transform === '');
158 | assert(slideout.panel.style.transition === '');
159 | });
160 |
161 | it('should set _opened to false.', function () {
162 | assert(slideout._opened === false);
163 | });
164 |
165 | it('should emit "beforeclose" event.', function () {
166 | assert(beforecloseEvent === true);
167 | });
168 |
169 | it('should emit "close" event.', function () {
170 | assert(closeEvent === true);
171 | });
172 | });
173 |
174 | describe('.toggle()', function () {
175 | it('should show the slideout if it is not opened.', function (done) {
176 | assert(doc.documentElement.className.search('slideout-open') === -1);
177 | slideout.toggle();
178 | assert(doc.documentElement.className.search('slideout-open') !== -1);
179 | slideout.toggle();
180 | setTimeout(function(){
181 | assert(doc.documentElement.className.search('slideout-open') === -1);
182 | done();
183 | }, 350);
184 | });
185 | });
186 |
187 | describe('.destroy()', function() {
188 | it('should destroy the instance internals allowing a new one to be created in it\'s place.', function(){
189 | slideout.destroy();
190 | slideout = new Slideout({
191 | 'panel': doc.getElementById('panel'),
192 | 'menu': doc.getElementById('menu')
193 | });
194 | slideout.open();
195 | setTimeout(function(){ slideout.close(); }, 750);
196 | });
197 | });
198 | });
199 |
--------------------------------------------------------------------------------