Repository: Mango/slideout
Branch: master
Commit: 6eb8440ddc7e
Files: 17
Total size: 82.2 KB
Directory structure:
gitextract_keq77s3o/
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bower.json
├── browserify.js
├── component.json
├── dist/
│ └── slideout.js
├── index.css
├── index.js
├── package.json
└── test/
├── index.html
├── index.right.html
├── test.css
└── test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
bower_components
coverage
================================================
FILE: .jshintrc
================================================
{
"bitwise": false,
"curly": true,
"eqeqeq": true,
"expr": true,
"forin": true,
"freeze": true,
"funcscope": true,
"globalstrict": true,
"nonbsp": true,
"unused": true,
"debug": true,
"evil": true,
"lastsemic": true,
"loopfunc": true,
"proto": true,
"scripturl": true,
"strict": true,
"browser": true,
"browserify": true,
"node": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- 6.9.1
# Send coverage data to Coveralls
after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## Code style
Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.**
## Modifying the code
Please don't edit `/dist/slideout.js` and `/dist/slideout.min.js` files. You'll find source code in the `index.js` file.
1. Fork and clone the repo.
2. Run `npm install` to install all dependencies.
3. Create a new branch, please don't work in your `master` branch directly.
4. Open the file `/test/index.html`.
5. Code!
### Running the tests
- Run `npm test` from your command line and check the console
- Open the `/test/index.html` file to run test in your browser.
## Pull requests
1. Create a new branch, **please don't work in your `master` branch directly**.
2. Code!
3. Update the tests and run `npm test` to see the tests.
4. Run `npm run hint`.
5. Run `npm run dist` to build a new version.
6. Update the documentation and package to reflect any changes.
7. Push to your fork and submit a pull request.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Mango
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Slideout.js
[![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]
> A touch slideout navigation menu for your mobile web apps.
## Features
- Dependency-free.
- Simple markup.
- Native scrolling.
- Easy customization.
- CSS transforms & transitions.
- Just 2 Kb! (min & gzip)
## Demo
[Check out the demo](https://mango.github.io/slideout/) to see it in action (on your mobile or emulate touches on your browser).
<img src="https://i.imgur.com/AWgwlVW.gif" alt="Slideout.js demo">
## Installation
Slideout is available on cdnjs
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/slideout/1.0.1/slideout.min.js"></script>
```
Also you can use one of many package managers
$ npm install slideout
$ spm install slideout
$ bower install slideout.js
$ component install mango/slideout
## Usage
Implementing Slideout.js into your project is easy.
First of all, you'll need to create your markup. You should have a menu (`#menu`) and a main content (`#panel`) into your body.
```html
<nav id="menu">
<header>
<h2>Menu</h2>
</header>
</nav>
<main id="panel">
<header>
<h2>Panel</h2>
</header>
</main>
```
Add the Slideout.js styles (index.css) in your web application.
```css
body {
width: 100%;
height: 100%;
}
.slideout-menu {
position: fixed;
top: 0;
bottom: 0;
width: 256px;
min-height: 100vh;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
z-index: 0;
display: none;
}
.slideout-menu-left {
left: 0;
}
.slideout-menu-right {
right: 0;
}
.slideout-panel {
position: relative;
z-index: 1;
will-change: transform;
background-color: #FFF; /* A background-color is required */
min-height: 100vh;
}
.slideout-open,
.slideout-open body,
.slideout-open .slideout-panel {
overflow: hidden;
}
.slideout-open .slideout-menu {
display: block;
}
```
Then you just include Slideout.js, create a new instance with some options and call the toggle method:
```html
<script src="dist/slideout.min.js"></script>
<script>
var slideout = new Slideout({
'panel': document.getElementById('panel'),
'menu': document.getElementById('menu'),
'padding': 256,
'tolerance': 70
});
// Toggle button
document.querySelector('.toggle-button').addEventListener('click', function() {
slideout.toggle();
});
</script>
```
#### Full example
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slideout Demo</title>
<meta http-equiv="cleartype" content="on">
<meta name="MobileOptimized" content="320">
<meta name="HandheldFriendly" content="True">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
body {
width: 100%;
height: 100%;
}
.slideout-menu {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 0;
width: 256px;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
display: none;
}
.slideout-panel {
position: relative;
z-index: 1;
will-change: transform;
}
.slideout-open,
.slideout-open body,
.slideout-open .slideout-panel {
overflow: hidden;
}
.slideout-open .slideout-menu {
display: block;
}
</style>
</head>
<body>
<nav id="menu">
<h2>Menu</h2>
</nav>
<main id="panel">
<header>
<button class="toggle-button">☰</button>
<h2>Panel</h2>
</header>
</main>
<script src="dist/slideout.min.js"></script>
<script>
var slideout = new Slideout({
'panel': document.getElementById('panel'),
'menu': document.getElementById('menu'),
'padding': 256,
'tolerance': 70
});
// Toggle button
document.querySelector('.toggle-button').addEventListener('click', function() {
slideout.toggle();
});
</script>
</body>
</html>
```
## Browser Support
- Chrome (IOS, Android, desktop)
- Firefox (Android, desktop)
- Safari (IOS, Android, desktop)
- Opera (desktop)
- IE 10+ (desktop and mobile)
## API
### Slideout(options)
Create a new instance of `Slideout`.
- `options` (Object) - Options to customize a new instance of Slideout.
- `options.panel` (HTMLElement) - The DOM element that contains all your application content (`.slideout-panel`).
- `options.menu` (HTMLElement) - The DOM element that contains your menu application (`.slideout-menu`).
- `[options.duration]` (Number) - The time (milliseconds) to open/close the slideout. Default: `300`.
- `[options.easing]` (String) - The CSS effect to use when animating the opening and closing of the slideout. Default: `ease`. Possible values:
- `ease`
- `linear`
- `ease-in`
- `ease-out`
- `ease-in-out`
- `step-start`
- `step-end`
- [`cubic-bezier`](http://cubic-bezier.com/)
- `[options.padding]` (Number) - Default: `256`.
- `[options.tolerance]` (Number) - The number of `px` needed for the menu can be opened completely, otherwise it closes. Default: `70`.
- `[options.touch]` (Boolean) - Set this option to false to disable Slideout touch events. Default: `true`.
- `[options.side]` (String) - The side to open the slideout (`left` or `right`). Default: `left`.
```js
var slideout = new Slideout({
'panel': document.getElementById('main'),
'menu': document.getElementById('menu'),
'padding': 256,
'tolerance': 70,
'easing': 'cubic-bezier(.32,2,.55,.27)'
});
```
### Slideout.open();
Opens the slideout menu. It emits `beforeopen` and `open` events.
```js
slideout.open();
```
### Slideout.close();
Closes the slideout menu. It emits `beforeclose` and `close` events.
```js
slideout.close();
```
### Slideout.toggle();
Toggles (open/close) the slideout menu.
```js
slideout.toggle();
```
### Slideout.isOpen();
Returns `true` if the slideout is currently open, and `false` if it is closed.
```js
slideout.isOpen(); // true or false
```
### Slideout.destroy();
Cleans up the instance so another slideout can be created on the same area.
```js
slideout.destroy();
```
### Slideout.enableTouch();
Enables opening the slideout via touch events.
```js
slideout.enableTouch();
```
### Slideout.disableTouch();
Disables opening the slideout via touch events.
```js
slideout.disableTouch();
```
### Slideout.on(event, listener);
```js
slideout.on('open', function() { ... });
```
### Slideout.once(event, listener);
```js
slideout.once('open', function() { ... });
```
### Slideout.off(event, listener);
```js
slideout.off('open', listener);
```
### Slideout.emit(event, ...data);
```js
slideout.emit('open');
```
## Events
An instance of Slideout emits the following events:
- `beforeclose`
- `close`
- `beforeopen`
- `open`
- `translatestart`
- `translate`
- `translateend`
The slideout emits `translatestart`, `translate` and `translateend` events only when it is opening/closing via touch events.
```js
slideout.on('translatestart', function() {
console.log('Start');
});
slideout.on('translate', function(translated) {
console.log('Translate: ' + translated); // 120 in px
});
slideout.on('translateend', function() {
console.log('End');
});
// 'Start'
// 'Translate 120'
// 'End'
```
## `data-slideout-ignore` attribute
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.
```html
<main id="panel">
<header>
<h2>Panel</h2>
</header>
<div id="carousel" data-slideout-ignore>
<h2>Carousel</h2>
...
</div>
</main>
```
## npm-scripts
```
$ npm run build
```
```
$ npm run dist
```
```
$ npm test
```
```
$ npm run hint
```
## FAQ
### How to add a toggle button.
```js
// vanilla js
document.querySelector('.toggle-button').addEventListener('click', function() {
slideout.toggle();
});
// jQuery
$('.toggle-button').on('click', function() {
slideout.toggle();
});
```
### How to open slideout from right side.
You should use the `side` option with the value `right`.
```js
var slideout = new Slideout({
'panel': document.getElementById('content'),
'menu': document.getElementById('menu'),
'side': 'right'
});
```
### How to enable slideout only on mobile devices.
You should use `mediaqueries`:
```css
@media screen and (min-width: 780px) {
.slideout-panel {
margin-left: 256px;
}
.slideout-menu {
display: block;
}
.btn-hamburger {
display: none;
}
}
```
Demo: http://codepen.io/pazguille/pen/mEdQvX
### How to use slideout with a fixed header.
First, you should define the styles for your fixed header:
```css
.fixed-header {
position: fixed;
width: 100%;
height: 50px;
backface-visibility: hidden;
z-index: 2;
background-color: red;
}
```
Then, using slideout's events you should translate the fixed header:
```js
var fixed = document.querySelector('.fixed-header');
slideout.on('translate', function(translated) {
fixed.style.transform = 'translateX(' + translated + 'px)';
});
slideout.on('beforeopen', function () {
fixed.style.transition = 'transform 300ms ease';
fixed.style.transform = 'translateX(256px)';
});
slideout.on('beforeclose', function () {
fixed.style.transition = 'transform 300ms ease';
fixed.style.transform = 'translateX(0px)';
});
slideout.on('open', function () {
fixed.style.transition = '';
});
slideout.on('close', function () {
fixed.style.transition = '';
});
```
Demo: http://codepen.io/pazguille/pen/ZBxdgw
### How to disable dragging on some elements.
You can use the attribute `data-slideout-ignore` to disable dragging on some elements:
```html
<nav id="menu">
<header>
<h2>Menu</h2>
</header>
</nav>
<main id="panel">
<header>
<h2>Panel</h2>
</header>
<div id="carousel" data-slideout-ignore>
<h2>Carousel</h2>
...
</div>
</main>
```
### How to add an overlay to close the menu on click.
You can do that using the powerful `slideout` API and a little extra CSS:
```css
.panel:before {
content: '';
display: block;
background-color: rgba(0,0,0,0);
transition: background-color 0.5s ease-in-out;
}
.panel-open:before {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
background-color: rgba(0,0,0,.5);
z-index: 99;
}
```
```js
function close(eve) {
eve.preventDefault();
slideout.close();
}
slideout
.on('beforeopen', function() {
this.panel.classList.add('panel-open');
})
.on('open', function() {
this.panel.addEventListener('click', close);
})
.on('beforeclose', function() {
this.panel.classList.remove('panel-open');
this.panel.removeEventListener('click', close);
});
```
Demo: http://codepen.io/pazguille/pen/BQYRYK
## With :heart: by
- Guille Paz (Front-end developer | Web standards lover)
- E-mail: [guille87paz@gmail.com](mailto:guille87paz@gmail.com)
- Twitter: [@pazguille](http://twitter.com/pazguille)
- Web: [http://pazguille.me](http://pazguille.me)
## License
MIT license. Copyright © 2015 [Mango](http://getmango.com).
[npm-image]: https://img.shields.io/npm/v/slideout.svg
[lic-image]: https://img.shields.io/npm/l/slideout.svg
[npm-link]: https://npmjs.org/package/slideout
[travis-image]: https://img.shields.io/travis/Mango/slideout.svg
[travis-link]: https://travis-ci.org/Mango/slideout
[deps-image]: https://img.shields.io/david/mango/slideout.svg
[deps-link]: https://david-dm.org/mango/slideout
[devdeps-image]: https://img.shields.io/david/dev/mango/slideout.svg
[devdeps-link]: https://david-dm.org/mango/slideout#info=devDependencies
[dt-image]: https://img.shields.io/npm/dt/slideout.svg
[coverage-image]: https://img.shields.io/coveralls/Mango/slideout.svg
[coverage-link]: https://coveralls.io/github/Mango/slideout
================================================
FILE: bower.json
================================================
{
"name": "slideout.js",
"repository": "https://github.com/Mango/slideout",
"description": "A touch slideout navigation menu for your mobile web apps.",
"author": "Guille Paz <guille87paz@gmail.com>",
"dependencies": {
"decouple": "0.0.2",
"emitter-es6": "0.0.7"
},
"ignore": [
".*",
"package.json",
"component.json",
"node_modules",
"browserify.js",
"**/.*",
"bower_components",
"test",
"demo"
],
"homepage": "https://github.com/mango/slideout",
"moduleType": [
"adecouplemd",
"globals",
"node"
],
"keywords": [
"slideout",
"offcanvas",
"menu",
"touch"
],
"main": "dist/slideout.js",
"license": "MIT"
}
================================================
FILE: browserify.js
================================================
'use strict';
var fs = require('fs');
var browserify = require('browserify');
if (!fs.existsSync('./dist')) {
fs.mkdirSync('./dist');
}
browserify({'debug': true, 'standalone': 'Slideout'})
.require('./index.js', {'entry': true})
.bundle()
.on('error', function(err) { console.log('Error : ' + err.message); })
.pipe(fs.createWriteStream('dist/slideout.js'));
================================================
FILE: component.json
================================================
{
"name": "slideout",
"repo": "mango/slideout",
"description": "A touch slideout navigation menu for your mobile web apps.",
"author": "Guille Paz <guille87paz@gmail.com>",
"version": "1.0.1",
"twitter": "@mango",
"keywords": [
"slideout",
"offcanvas",
"menu",
"touch"
],
"dependencies": {
"pazguille/decouple": "0.0.2",
"Mango/emitter": "0.0.7"
},
"development": {},
"license": "MIT",
"scripts": [
"index.js"
]
}
================================================
FILE: dist/slideout.js
================================================
!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){
'use strict';
/**
* Module dependencies
*/
var decouple = require('decouple');
var Emitter = require('emitter');
/**
* Privates
*/
var scrollTimeout;
var scrolling = false;
var doc = window.document;
var html = doc.documentElement;
var msPointerSupported = window.navigator.msPointerEnabled;
var touch = {
'start': msPointerSupported ? 'MSPointerDown' : 'touchstart',
'move': msPointerSupported ? 'MSPointerMove' : 'touchmove',
'end': msPointerSupported ? 'MSPointerUp' : 'touchend'
};
var prefix = (function prefix() {
var regex = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/;
var styleDeclaration = doc.getElementsByTagName('script')[0].style;
for (var prop in styleDeclaration) {
if (regex.test(prop)) {
return '-' + prop.match(regex)[0].toLowerCase() + '-';
}
}
// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
// However (prop in style) returns the correct value, so we'll have to test for
// the precence of a specific property
if ('WebkitOpacity' in styleDeclaration) { return '-webkit-'; }
if ('KhtmlOpacity' in styleDeclaration) { return '-khtml-'; }
return '';
}());
function extend(destination, from) {
for (var prop in from) {
if (from[prop]) {
destination[prop] = from[prop];
}
}
return destination;
}
function inherits(child, uber) {
child.prototype = extend(child.prototype || {}, uber.prototype);
}
function hasIgnoredElements(el) {
while (el.parentNode) {
if (el.getAttribute('data-slideout-ignore') !== null) {
return el;
}
el = el.parentNode;
}
return null;
}
/**
* Slideout constructor
*/
function Slideout(options) {
options = options || {};
// Sets default values
this._startOffsetX = 0;
this._currentOffsetX = 0;
this._opening = false;
this._moved = false;
this._opened = false;
this._preventOpen = false;
// Sets panel
this.panel = options.panel;
this.menu = options.menu;
// Sets options
this._touch = options.touch === undefined ? true : options.touch && true;
this._side = options.side || 'left';
this._easing = options.fx || options.easing || 'ease';
this._duration = parseInt(options.duration, 10) || 300;
this._tolerance = parseInt(options.tolerance, 10) || 70;
this._padding = this._translateTo = parseInt(options.padding, 10) || 256;
this._orientation = this._side === 'right' ? -1 : 1;
this._translateTo *= this._orientation;
// Sets classnames
if (!this.panel.classList.contains('slideout-panel')) {
this.panel.classList.add('slideout-panel');
}
if (!this.panel.classList.contains('slideout-panel-' + this._side)) {
this.panel.classList.add('slideout-panel-' + this._side);
}
if (!this.menu.classList.contains('slideout-menu')) {
this.menu.classList.add('slideout-menu');
}
if (!this.menu.classList.contains('slideout-menu-' + this._side)) {
this.menu.classList.add('slideout-menu-' + this._side);
}
// Init touch events
if (this._touch) {
this._initTouchEvents();
}
}
/**
* Inherits from Emitter
*/
inherits(Slideout, Emitter);
/**
* Opens the slideout menu.
*/
Slideout.prototype.open = function() {
var self = this;
this.emit('beforeopen');
if (!html.classList.contains('slideout-open')) {
html.classList.add('slideout-open');
}
this._setTransition();
this._translateXTo(this._translateTo);
this._opened = true;
setTimeout(function() {
self.panel.style.transition = self.panel.style['-webkit-transition'] = '';
self.emit('open');
}, this._duration + 50);
return this;
};
/**
* Closes slideout menu.
*/
Slideout.prototype.close = function() {
var self = this;
if (!this.isOpen() && !this._opening) {
return this;
}
this.emit('beforeclose');
this._setTransition();
this._translateXTo(0);
this._opened = false;
setTimeout(function() {
html.classList.remove('slideout-open');
self.panel.style.transition = self.panel.style['-webkit-transition'] = self.panel.style[prefix + 'transform'] = self.panel.style.transform = '';
self.emit('close');
}, this._duration + 50);
return this;
};
/**
* Toggles (open/close) slideout menu.
*/
Slideout.prototype.toggle = function() {
return this.isOpen() ? this.close() : this.open();
};
/**
* Returns true if the slideout is currently open, and false if it is closed.
*/
Slideout.prototype.isOpen = function() {
return this._opened;
};
/**
* Translates panel and updates currentOffset with a given X point
*/
Slideout.prototype._translateXTo = function(translateX) {
this._currentOffsetX = translateX;
this.panel.style[prefix + 'transform'] = this.panel.style.transform = 'translateX(' + translateX + 'px)';
return this;
};
/**
* Set transition properties
*/
Slideout.prototype._setTransition = function() {
this.panel.style[prefix + 'transition'] = this.panel.style.transition = prefix + 'transform ' + this._duration + 'ms ' + this._easing;
return this;
};
/**
* Initializes touch event
*/
Slideout.prototype._initTouchEvents = function() {
var self = this;
/**
* Decouple scroll event
*/
this._onScrollFn = decouple(doc, 'scroll', function() {
if (!self._moved) {
clearTimeout(scrollTimeout);
scrolling = true;
scrollTimeout = setTimeout(function() {
scrolling = false;
}, 250);
}
});
/**
* Prevents touchmove event if slideout is moving
*/
this._preventMove = function(eve) {
if (self._moved) {
eve.preventDefault();
}
};
doc.addEventListener(touch.move, this._preventMove);
/**
* Resets values on touchstart
*/
this._resetTouchFn = function(eve) {
if (typeof eve.touches === 'undefined') {
return;
}
self._moved = false;
self._opening = false;
self._startOffsetX = eve.touches[0].pageX;
self._preventOpen = (!self._touch || (!self.isOpen() && self.menu.clientWidth !== 0));
};
this.panel.addEventListener(touch.start, this._resetTouchFn);
/**
* Resets values on touchcancel
*/
this._onTouchCancelFn = function() {
self._moved = false;
self._opening = false;
};
this.panel.addEventListener('touchcancel', this._onTouchCancelFn);
/**
* Toggles slideout on touchend
*/
this._onTouchEndFn = function() {
if (self._moved) {
self.emit('translateend');
(self._opening && Math.abs(self._currentOffsetX) > self._tolerance) ? self.open() : self.close();
}
self._moved = false;
};
this.panel.addEventListener(touch.end, this._onTouchEndFn);
/**
* Translates panel on touchmove
*/
this._onTouchMoveFn = function(eve) {
if (
scrolling ||
self._preventOpen ||
typeof eve.touches === 'undefined' ||
hasIgnoredElements(eve.target)
) {
return;
}
var dif_x = eve.touches[0].clientX - self._startOffsetX;
var translateX = self._currentOffsetX = dif_x;
if (Math.abs(translateX) > self._padding) {
return;
}
if (Math.abs(dif_x) > 20) {
self._opening = true;
var oriented_dif_x = dif_x * self._orientation;
if (self._opened && oriented_dif_x > 0 || !self._opened && oriented_dif_x < 0) {
return;
}
if (!self._moved) {
self.emit('translatestart');
}
if (oriented_dif_x <= 0) {
translateX = dif_x + self._padding * self._orientation;
self._opening = false;
}
if (!(self._moved && html.classList.contains('slideout-open'))) {
html.classList.add('slideout-open');
}
self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translateX(' + translateX + 'px)';
self.emit('translate', translateX);
self._moved = true;
}
};
this.panel.addEventListener(touch.move, this._onTouchMoveFn);
return this;
};
/**
* Enable opening the slideout via touch events.
*/
Slideout.prototype.enableTouch = function() {
this._touch = true;
return this;
};
/**
* Disable opening the slideout via touch events.
*/
Slideout.prototype.disableTouch = function() {
this._touch = false;
return this;
};
/**
* Destroy an instance of slideout.
*/
Slideout.prototype.destroy = function() {
// Close before clean
this.close();
// Remove event listeners
doc.removeEventListener(touch.move, this._preventMove);
this.panel.removeEventListener(touch.start, this._resetTouchFn);
this.panel.removeEventListener('touchcancel', this._onTouchCancelFn);
this.panel.removeEventListener(touch.end, this._onTouchEndFn);
this.panel.removeEventListener(touch.move, this._onTouchMoveFn);
doc.removeEventListener('scroll', this._onScrollFn);
// Remove methods
this.open = this.close = function() {};
// Return the instance so it can be easily dereferenced
return this;
};
/**
* Expose Slideout
*/
module.exports = Slideout;
},{"decouple":2,"emitter":3}],2:[function(require,module,exports){
'use strict';
var requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
}());
function decouple(node, event, fn) {
var eve,
tracking = false;
function captureEvent(e) {
eve = e;
track();
}
function track() {
if (!tracking) {
requestAnimFrame(update);
tracking = true;
}
}
function update() {
fn.call(node, eve);
tracking = false;
}
node.addEventListener(event, captureEvent, false);
return captureEvent;
}
/**
* Expose decouple
*/
module.exports = decouple;
},{}],3:[function(require,module,exports){
"use strict";
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
exports.__esModule = true;
/**
* Creates a new instance of Emitter.
* @class
* @returns {Object} Returns a new instance of Emitter.
* @example
* // Creates a new instance of Emitter.
* var Emitter = require('emitter');
*
* var emitter = new Emitter();
*/
var Emitter = (function () {
function Emitter() {
_classCallCheck(this, Emitter);
}
/**
* Adds a listener to the collection for the specified event.
* @memberof! Emitter.prototype
* @function
* @param {String} event - The event name.
* @param {Function} listener - A listener function to add.
* @returns {Object} Returns an instance of Emitter.
* @example
* // Add an event listener to "foo" event.
* emitter.on('foo', listener);
*/
Emitter.prototype.on = function on(event, listener) {
// Use the current collection or create it.
this._eventCollection = this._eventCollection || {};
// Use the current collection of an event or create it.
this._eventCollection[event] = this._eventCollection[event] || [];
// Appends the listener into the collection of the given event
this._eventCollection[event].push(listener);
return this;
};
/**
* Adds a listener to the collection for the specified event that will be called only once.
* @memberof! Emitter.prototype
* @function
* @param {String} event - The event name.
* @param {Function} listener - A listener function to add.
* @returns {Object} Returns an instance of Emitter.
* @example
* // Will add an event handler to "foo" event once.
* emitter.once('foo', listener);
*/
Emitter.prototype.once = function once(event, listener) {
var self = this;
function fn() {
self.off(event, fn);
listener.apply(this, arguments);
}
fn.listener = listener;
this.on(event, fn);
return this;
};
/**
* Removes a listener from the collection for the specified event.
* @memberof! Emitter.prototype
* @function
* @param {String} event - The event name.
* @param {Function} listener - A listener function to remove.
* @returns {Object} Returns an instance of Emitter.
* @example
* // Remove a given listener.
* emitter.off('foo', listener);
*/
Emitter.prototype.off = function off(event, listener) {
var listeners = undefined;
// Defines listeners value.
if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
return this;
}
listeners.forEach(function (fn, i) {
if (fn === listener || fn.listener === listener) {
// Removes the given listener.
listeners.splice(i, 1);
}
});
// Removes an empty event collection.
if (listeners.length === 0) {
delete this._eventCollection[event];
}
return this;
};
/**
* Execute each item in the listener collection in order with the specified data.
* @memberof! Emitter.prototype
* @function
* @param {String} event - The name of the event you want to emit.
* @param {...Object} data - Data to pass to the listeners.
* @returns {Object} Returns an instance of Emitter.
* @example
* // Emits the "foo" event with 'param1' and 'param2' as arguments.
* emitter.emit('foo', 'param1', 'param2');
*/
Emitter.prototype.emit = function emit(event) {
var _this = this;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var listeners = undefined;
// Defines listeners value.
if (!this._eventCollection || !(listeners = this._eventCollection[event])) {
return this;
}
// Clone listeners
listeners = listeners.slice(0);
listeners.forEach(function (fn) {
return fn.apply(_this, args);
});
return this;
};
return Emitter;
})();
/**
* Exports Emitter
*/
exports["default"] = Emitter;
module.exports = exports["default"];
},{}]},{},[1])(1)
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9kZWNvdXBsZS9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9lbWl0dGVyL2Rpc3QvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiJ3VzZSBzdHJpY3QnO1xuXG4vKipcbiAqIE1vZHVsZSBkZXBlbmRlbmNpZXNcbiAqL1xudmFyIGRlY291cGxlID0gcmVxdWlyZSgnZGVjb3VwbGUnKTtcbnZhciBFbWl0dGVyID0gcmVxdWlyZSgnZW1pdHRlcicpO1xuXG4vKipcbiAqIFByaXZhdGVzXG4gKi9cbnZhciBzY3JvbGxUaW1lb3V0O1xudmFyIHNjcm9sbGluZyA9IGZhbHNlO1xudmFyIGRvYyA9IHdpbmRvdy5kb2N1bWVudDtcbnZhciBodG1sID0gZG9jLmRvY3VtZW50RWxlbWVudDtcbnZhciBtc1BvaW50ZXJTdXBwb3J0ZWQgPSB3aW5kb3cubmF2aWdhdG9yLm1zUG9pbnRlckVuYWJsZWQ7XG52YXIgdG91Y2ggPSB7XG4gICdzdGFydCc6IG1zUG9pbnRlclN1cHBvcnRlZCA/ICdNU1BvaW50ZXJEb3duJyA6ICd0b3VjaHN0YXJ0JyxcbiAgJ21vdmUnOiBtc1BvaW50ZXJTdXBwb3J0ZWQgPyAnTVNQb2ludGVyTW92ZScgOiAndG91Y2htb3ZlJyxcbiAgJ2VuZCc6IG1zUG9pbnRlclN1cHBvcnRlZCA/ICdNU1BvaW50ZXJVcCcgOiAndG91Y2hlbmQnXG59O1xudmFyIHByZWZpeCA9IChmdW5jdGlvbiBwcmVmaXgoKSB7XG4gIHZhciByZWdleCA9IC9eKFdlYmtpdHxLaHRtbHxNb3p8bXN8TykoPz1bQS1aXSkvO1xuICB2YXIgc3R5bGVEZWNsYXJhdGlvbiA9IGRvYy5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0JylbMF0uc3R5bGU7XG4gIGZvciAodmFyIHByb3AgaW4gc3R5bGVEZWNsYXJhdGlvbikge1xuICAgIGlmIChyZWdleC50ZXN0KHByb3ApKSB7XG4gICAgICByZXR1cm4gJy0nICsgcHJvcC5tYXRjaChyZWdleClbMF0udG9Mb3dlckNhc2UoKSArICctJztcbiAgICB9XG4gIH1cbiAgLy8gTm90aGluZyBmb3VuZCBzbyBmYXI/IFdlYmtpdCBkb2VzIG5vdCBlbnVtZXJhdGUgb3ZlciB0aGUgQ1NTIHByb3BlcnRpZXMgb2YgdGhlIHN0eWxlIG9iamVjdC5cbiAgLy8gSG93ZXZlciAocHJvcCBpbiBzdHlsZSkgcmV0dXJucyB0aGUgY29ycmVjdCB2YWx1ZSwgc28gd2UnbGwgaGF2ZSB0byB0ZXN0IGZvclxuICAvLyB0aGUgcHJlY2VuY2Ugb2YgYSBzcGVjaWZpYyBwcm9wZXJ0eVxuICBpZiAoJ1dlYmtpdE9wYWNpdHknIGluIHN0eWxlRGVjbGFyYXRpb24pIHsgcmV0dXJuICctd2Via2l0LSc7IH1cbiAgaWYgKCdLaHRtbE9wYWNpdHknIGluIHN0eWxlRGVjbGFyYXRpb24pIHsgcmV0dXJuICcta2h0bWwtJzsgfVxuICByZXR1cm4gJyc7XG59KCkpO1xuZnVuY3Rpb24gZXh0ZW5kKGRlc3RpbmF0aW9uLCBmcm9tKSB7XG4gIGZvciAodmFyIHByb3AgaW4gZnJvbSkge1xuICAgIGlmIChmcm9tW3Byb3BdKSB7XG4gICAgICBkZXN0aW5hdGlvbltwcm9wXSA9IGZyb21bcHJvcF07XG4gICAgfVxuICB9XG4gIHJldHVybiBkZXN0aW5hdGlvbjtcbn1cbmZ1bmN0aW9uIGluaGVyaXRzKGNoaWxkLCB1YmVyKSB7XG4gIGNoaWxkLnByb3RvdHlwZSA9IGV4dGVuZChjaGlsZC5wcm90b3R5cGUgfHwge30sIHViZXIucHJvdG90eXBlKTtcbn1cbmZ1bmN0aW9uIGhhc0lnbm9yZWRFbGVtZW50cyhlbCkge1xuICB3aGlsZSAoZWwucGFyZW50Tm9kZSkge1xuICAgIGlmIChlbC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc2xpZGVvdXQtaWdub3JlJykgIT09IG51bGwpIHtcbiAgICAgIHJldHVybiBlbDtcbiAgICB9XG4gICAgZWwgPSBlbC5wYXJlbnROb2RlO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFNsaWRlb3V0IGNvbnN0cnVjdG9yXG4gKi9cbmZ1bmN0aW9uIFNsaWRlb3V0KG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cbiAgLy8gU2V0cyBkZWZhdWx0IHZhbHVlc1xuICB0aGlzLl9zdGFydE9mZnNldFggPSAwO1xuICB0aGlzLl9jdXJyZW50T2Zmc2V0WCA9IDA7XG4gIHRoaXMuX29wZW5pbmcgPSBmYWxzZTtcbiAgdGhpcy5fbW92ZWQgPSBmYWxzZTtcbiAgdGhpcy5fb3BlbmVkID0gZmFsc2U7XG4gIHRoaXMuX3ByZXZlbnRPcGVuID0gZmFsc2U7XG5cbiAgLy8gU2V0cyBwYW5lbFxuICB0aGlzLnBhbmVsID0gb3B0aW9ucy5wYW5lbDtcbiAgdGhpcy5tZW51ID0gb3B0aW9ucy5tZW51O1xuXG4gIC8vIFNldHMgb3B0aW9uc1xuICB0aGlzLl90b3VjaCA9IG9wdGlvbnMudG91Y2ggPT09IHVuZGVmaW5lZCA/IHRydWUgOiBvcHRpb25zLnRvdWNoICYmIHRydWU7XG4gIHRoaXMuX3NpZGUgPSBvcHRpb25zLnNpZGUgfHwgJ2xlZnQnO1xuICB0aGlzLl9lYXNpbmcgPSBvcHRpb25zLmZ4IHx8wqBvcHRpb25zLmVhc2luZyB8fCAnZWFzZSc7XG4gIHRoaXMuX2R1cmF0aW9uID0gcGFyc2VJbnQob3B0aW9ucy5kdXJhdGlvbiwgMTApIHx8IDMwMDtcbiAgdGhpcy5fdG9sZXJhbmNlID0gcGFyc2VJbnQob3B0aW9ucy50b2xlcmFuY2UsIDEwKSB8fCA3MDtcbiAgdGhpcy5fcGFkZGluZyA9IHRoaXMuX3RyYW5zbGF0ZVRvID0gcGFyc2VJbnQob3B0aW9ucy5wYWRkaW5nLCAxMCkgfHwgMjU2O1xuICB0aGlzLl9vcmllbnRhdGlvbiA9IHRoaXMuX3NpZGUgPT09ICdyaWdodCcgPyAtMSA6IDE7XG4gIHRoaXMuX3RyYW5zbGF0ZVRvICo9IHRoaXMuX29yaWVudGF0aW9uO1xuXG4gIC8vIFNldHMgIGNsYXNzbmFtZXNcbiAgaWYgKCF0aGlzLnBhbmVsLmNsYXNzTGlzdC5jb250YWlucygnc2xpZGVvdXQtcGFuZWwnKSkge1xuICAgIHRoaXMucGFuZWwuY2xhc3NMaXN0LmFkZCgnc2xpZGVvdXQtcGFuZWwnKTtcbiAgfVxuICBpZiAoIXRoaXMucGFuZWwuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1wYW5lbC0nICsgdGhpcy5fc2lkZSkpIHtcbiAgICB0aGlzLnBhbmVsLmNsYXNzTGlzdC5hZGQoJ3NsaWRlb3V0LXBhbmVsLScgKyB0aGlzLl9zaWRlKTtcbiAgfVxuICBpZiAoIXRoaXMubWVudS5jbGFzc0xpc3QuY29udGFpbnMoJ3NsaWRlb3V0LW1lbnUnKSkge1xuICAgIHRoaXMubWVudS5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1tZW51Jyk7XG4gIH1cbiAgaWYgKCF0aGlzLm1lbnUuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1tZW51LScgKyB0aGlzLl9zaWRlKSkge1xuICAgIHRoaXMubWVudS5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1tZW51LScgKyB0aGlzLl9zaWRlKTtcbiAgfVxuXG4gIC8vIEluaXQgdG91Y2ggZXZlbnRzXG4gIGlmICh0aGlzLl90b3VjaCkge1xuICAgIHRoaXMuX2luaXRUb3VjaEV2ZW50cygpO1xuICB9XG59XG5cbi8qKlxuICogSW5oZXJpdHMgZnJvbSBFbWl0dGVyXG4gKi9cbmluaGVyaXRzKFNsaWRlb3V0LCBFbWl0dGVyKTtcblxuLyoqXG4gKiBPcGVucyB0aGUgc2xpZGVvdXQgbWVudS5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLm9wZW4gPSBmdW5jdGlvbigpIHtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuICB0aGlzLmVtaXQoJ2JlZm9yZW9wZW4nKTtcbiAgaWYgKCFodG1sLmNsYXNzTGlzdC5jb250YWlucygnc2xpZGVvdXQtb3BlbicpKSB7XG4gICAgaHRtbC5jbGFzc0xpc3QuYWRkKCdzbGlkZW91dC1vcGVuJyk7XG4gIH1cbiAgdGhpcy5fc2V0VHJhbnNpdGlvbigpO1xuICB0aGlzLl90cmFuc2xhdGVYVG8odGhpcy5fdHJhbnNsYXRlVG8pO1xuICB0aGlzLl9vcGVuZWQgPSB0cnVlO1xuICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgIHNlbGYucGFuZWwuc3R5bGUudHJhbnNpdGlvbiA9IHNlbGYucGFuZWwuc3R5bGVbJy13ZWJraXQtdHJhbnNpdGlvbiddID0gJyc7XG4gICAgc2VsZi5lbWl0KCdvcGVuJyk7XG4gIH0sIHRoaXMuX2R1cmF0aW9uICsgNTApO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogQ2xvc2VzIHNsaWRlb3V0IG1lbnUuXG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5jbG9zZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIGlmICghdGhpcy5pc09wZW4oKSAmJiAhdGhpcy5fb3BlbmluZykge1xuICAgIHJldHVybiB0aGlzO1xuICB9XG4gIHRoaXMuZW1pdCgnYmVmb3JlY2xvc2UnKTtcbiAgdGhpcy5fc2V0VHJhbnNpdGlvbigpO1xuICB0aGlzLl90cmFuc2xhdGVYVG8oMCk7XG4gIHRoaXMuX29wZW5lZCA9IGZhbHNlO1xuICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgIGh0bWwuY2xhc3NMaXN0LnJlbW92ZSgnc2xpZGVvdXQtb3BlbicpO1xuICAgIHNlbGYucGFuZWwuc3R5bGUudHJhbnNpdGlvbiA9IHNlbGYucGFuZWwuc3R5bGVbJy13ZWJraXQtdHJhbnNpdGlvbiddID0gc2VsZi5wYW5lbC5zdHlsZVtwcmVmaXggKyAndHJhbnNmb3JtJ10gPSBzZWxmLnBhbmVsLnN0eWxlLnRyYW5zZm9ybSA9ICcnO1xuICAgIHNlbGYuZW1pdCgnY2xvc2UnKTtcbiAgfSwgdGhpcy5fZHVyYXRpb24gKyA1MCk7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBUb2dnbGVzIChvcGVuL2Nsb3NlKSBzbGlkZW91dCBtZW51LlxuICovXG5TbGlkZW91dC5wcm90b3R5cGUudG9nZ2xlID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmlzT3BlbigpID8gdGhpcy5jbG9zZSgpIDogdGhpcy5vcGVuKCk7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgc2xpZGVvdXQgaXMgY3VycmVudGx5IG9wZW4sIGFuZCBmYWxzZSBpZiBpdCBpcyBjbG9zZWQuXG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5pc09wZW4gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuX29wZW5lZDtcbn07XG5cbi8qKlxuICogVHJhbnNsYXRlcyBwYW5lbCBhbmQgdXBkYXRlcyBjdXJyZW50T2Zmc2V0IHdpdGggYSBnaXZlbiBYIHBvaW50XG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5fdHJhbnNsYXRlWFRvID0gZnVuY3Rpb24odHJhbnNsYXRlWCkge1xuICB0aGlzLl9jdXJyZW50T2Zmc2V0WCA9IHRyYW5zbGF0ZVg7XG4gIHRoaXMucGFuZWwuc3R5bGVbcHJlZml4ICsgJ3RyYW5zZm9ybSddID0gdGhpcy5wYW5lbC5zdHlsZS50cmFuc2Zvcm0gPSAndHJhbnNsYXRlWCgnICsgdHJhbnNsYXRlWCArICdweCknO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogU2V0IHRyYW5zaXRpb24gcHJvcGVydGllc1xuICovXG5TbGlkZW91dC5wcm90b3R5cGUuX3NldFRyYW5zaXRpb24gPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wYW5lbC5zdHlsZVtwcmVmaXggKyAndHJhbnNpdGlvbiddID0gdGhpcy5wYW5lbC5zdHlsZS50cmFuc2l0aW9uID0gcHJlZml4ICsgJ3RyYW5zZm9ybSAnICsgdGhpcy5fZHVyYXRpb24gKyAnbXMgJyArIHRoaXMuX2Vhc2luZztcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIHRvdWNoIGV2ZW50XG4gKi9cblNsaWRlb3V0LnByb3RvdHlwZS5faW5pdFRvdWNoRXZlbnRzID0gZnVuY3Rpb24oKSB7XG4gIHZhciBzZWxmID0gdGhpcztcblxuICAvKipcbiAgICogRGVjb3VwbGUgc2Nyb2xsIGV2ZW50XG4gICAqL1xuICB0aGlzLl9vblNjcm9sbEZuID0gZGVjb3VwbGUoZG9jLCAnc2Nyb2xsJywgZnVuY3Rpb24oKSB7XG4gICAgaWYgKCFzZWxmLl9tb3ZlZCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHNjcm9sbFRpbWVvdXQpO1xuICAgICAgc2Nyb2xsaW5nID0gdHJ1ZTtcbiAgICAgIHNjcm9sbFRpbWVvdXQgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgICBzY3JvbGxpbmcgPSBmYWxzZTtcbiAgICAgIH0sIDI1MCk7XG4gICAgfVxuICB9KTtcblxuICAvKipcbiAgICogUHJldmVudHMgdG91Y2htb3ZlIGV2ZW50IGlmIHNsaWRlb3V0IGlzIG1vdmluZ1xuICAgKi9cbiAgdGhpcy5fcHJldmVudE1vdmUgPSBmdW5jdGlvbihldmUpIHtcbiAgICBpZiAoc2VsZi5fbW92ZWQpIHtcbiAgICAgIGV2ZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIH1cbiAgfTtcblxuICBkb2MuYWRkRXZlbnRMaXN0ZW5lcih0b3VjaC5tb3ZlLCB0aGlzLl9wcmV2ZW50TW92ZSk7XG5cbiAgLyoqXG4gICAqIFJlc2V0cyB2YWx1ZXMgb24gdG91Y2hzdGFydFxuICAgKi9cbiAgdGhpcy5fcmVzZXRUb3VjaEZuID0gZnVuY3Rpb24oZXZlKSB7XG4gICAgaWYgKHR5cGVvZiBldmUudG91Y2hlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBzZWxmLl9tb3ZlZCA9IGZhbHNlO1xuICAgIHNlbGYuX29wZW5pbmcgPSBmYWxzZTtcbiAgICBzZWxmLl9zdGFydE9mZnNldFggPSBldmUudG91Y2hlc1swXS5wYWdlWDtcbiAgICBzZWxmLl9wcmV2ZW50T3BlbiA9ICghc2VsZi5fdG91Y2ggfHwgKCFzZWxmLmlzT3BlbigpICYmIHNlbGYubWVudS5jbGllbnRXaWR0aCAhPT0gMCkpO1xuICB9O1xuXG4gIHRoaXMucGFuZWwuYWRkRXZlbnRMaXN0ZW5lcih0b3VjaC5zdGFydCwgdGhpcy5fcmVzZXRUb3VjaEZuKTtcblxuICAvKipcbiAgICogUmVzZXRzIHZhbHVlcyBvbiB0b3VjaGNhbmNlbFxuICAgKi9cbiAgdGhpcy5fb25Ub3VjaENhbmNlbEZuID0gZnVuY3Rpb24oKSB7XG4gICAgc2VsZi5fbW92ZWQgPSBmYWxzZTtcbiAgICBzZWxmLl9vcGVuaW5nID0gZmFsc2U7XG4gIH07XG5cbiAgdGhpcy5wYW5lbC5hZGRFdmVudExpc3RlbmVyKCd0b3VjaGNhbmNlbCcsIHRoaXMuX29uVG91Y2hDYW5jZWxGbik7XG5cbiAgLyoqXG4gICAqIFRvZ2dsZXMgc2xpZGVvdXQgb24gdG91Y2hlbmRcbiAgICovXG4gIHRoaXMuX29uVG91Y2hFbmRGbiA9IGZ1bmN0aW9uKCkge1xuICAgIGlmIChzZWxmLl9tb3ZlZCkge1xuICAgICAgc2VsZi5lbWl0KCd0cmFuc2xhdGVlbmQnKTtcbiAgICAgIChzZWxmLl9vcGVuaW5nICYmIE1hdGguYWJzKHNlbGYuX2N1cnJlbnRPZmZzZXRYKSA+IHNlbGYuX3RvbGVyYW5jZSkgPyBzZWxmLm9wZW4oKSA6IHNlbGYuY2xvc2UoKTtcbiAgICB9XG4gICAgc2VsZi5fbW92ZWQgPSBmYWxzZTtcbiAgfTtcblxuICB0aGlzLnBhbmVsLmFkZEV2ZW50TGlzdGVuZXIodG91Y2guZW5kLCB0aGlzLl9vblRvdWNoRW5kRm4pO1xuXG4gIC8qKlxuICAgKiBUcmFuc2xhdGVzIHBhbmVsIG9uIHRvdWNobW92ZVxuICAgKi9cbiAgdGhpcy5fb25Ub3VjaE1vdmVGbiA9IGZ1bmN0aW9uKGV2ZSkge1xuICAgIGlmIChcbiAgICAgIHNjcm9sbGluZyB8fFxuICAgICAgc2VsZi5fcHJldmVudE9wZW4gfHxcbiAgICAgIHR5cGVvZiBldmUudG91Y2hlcyA9PT0gJ3VuZGVmaW5lZCcgfHxcbiAgICAgIGhhc0lnbm9yZWRFbGVtZW50cyhldmUudGFyZ2V0KVxuICAgICkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBkaWZfeCA9IGV2ZS50b3VjaGVzWzBdLmNsaWVudFggLSBzZWxmLl9zdGFydE9mZnNldFg7XG4gICAgdmFyIHRyYW5zbGF0ZVggPSBzZWxmLl9jdXJyZW50T2Zmc2V0WCA9IGRpZl94O1xuXG4gICAgaWYgKE1hdGguYWJzKHRyYW5zbGF0ZVgpID4gc2VsZi5fcGFkZGluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChNYXRoLmFicyhkaWZfeCkgPiAyMCkge1xuXG4gICAgICBzZWxmLl9vcGVuaW5nID0gdHJ1ZTtcblxuICAgICAgdmFyIG9yaWVudGVkX2RpZl94ID0gZGlmX3ggKiBzZWxmLl9vcmllbnRhdGlvbjtcblxuICAgICAgaWYgKHNlbGYuX29wZW5lZCAmJiBvcmllbnRlZF9kaWZfeCA+IDAgfHwgIXNlbGYuX29wZW5lZCAmJiBvcmllbnRlZF9kaWZfeCA8IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXNlbGYuX21vdmVkKSB7XG4gICAgICAgIHNlbGYuZW1pdCgndHJhbnNsYXRlc3RhcnQnKTtcbiAgICAgIH1cblxuICAgICAgaWYgKG9yaWVudGVkX2RpZl94IDw9IDApIHtcbiAgICAgICAgdHJhbnNsYXRlWCA9IGRpZl94ICsgc2VsZi5fcGFkZGluZyAqIHNlbGYuX29yaWVudGF0aW9uO1xuICAgICAgICBzZWxmLl9vcGVuaW5nID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGlmICghKHNlbGYuX21vdmVkICYmIGh0bWwuY2xhc3NMaXN0LmNvbnRhaW5zKCdzbGlkZW91dC1vcGVuJykpKSB7XG4gICAgICAgIGh0bWwuY2xhc3NMaXN0LmFkZCgnc2xpZGVvdXQtb3BlbicpO1xuICAgICAgfVxuXG4gICAgICBzZWxmLnBhbmVsLnN0eWxlW3ByZWZpeCArICd0cmFuc2Zvcm0nXSA9IHNlbGYucGFuZWwuc3R5bGUudHJhbnNmb3JtID0gJ3RyYW5zbGF0ZVgoJyArIHRyYW5zbGF0ZVggKyAncHgpJztcbiAgICAgIHNlbGYuZW1pdCgndHJhbnNsYXRlJywgdHJhbnNsYXRlWCk7XG4gICAgICBzZWxmLl9tb3ZlZCA9IHRydWU7XG4gICAgfVxuXG4gIH07XG5cbiAgdGhpcy5wYW5lbC5hZGRFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX29uVG91Y2hNb3ZlRm4pO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBFbmFibGUgb3BlbmluZyB0aGUgc2xpZGVvdXQgdmlhIHRvdWNoIGV2ZW50cy5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmVuYWJsZVRvdWNoID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX3RvdWNoID0gdHJ1ZTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIERpc2FibGUgb3BlbmluZyB0aGUgc2xpZGVvdXQgdmlhIHRvdWNoIGV2ZW50cy5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmRpc2FibGVUb3VjaCA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLl90b3VjaCA9IGZhbHNlO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogRGVzdHJveSBhbiBpbnN0YW5jZSBvZiBzbGlkZW91dC5cbiAqL1xuU2xpZGVvdXQucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgLy8gQ2xvc2UgYmVmb3JlIGNsZWFuXG4gIHRoaXMuY2xvc2UoKTtcblxuICAvLyBSZW1vdmUgZXZlbnQgbGlzdGVuZXJzXG4gIGRvYy5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX3ByZXZlbnRNb3ZlKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLnN0YXJ0LCB0aGlzLl9yZXNldFRvdWNoRm4pO1xuICB0aGlzLnBhbmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3RvdWNoY2FuY2VsJywgdGhpcy5fb25Ub3VjaENhbmNlbEZuKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLmVuZCwgdGhpcy5fb25Ub3VjaEVuZEZuKTtcbiAgdGhpcy5wYW5lbC5yZW1vdmVFdmVudExpc3RlbmVyKHRvdWNoLm1vdmUsIHRoaXMuX29uVG91Y2hNb3ZlRm4pO1xuICBkb2MucmVtb3ZlRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhpcy5fb25TY3JvbGxGbik7XG5cbiAgLy8gUmVtb3ZlIG1ldGhvZHNcbiAgdGhpcy5vcGVuID0gdGhpcy5jbG9zZSA9IGZ1bmN0aW9uKCkge307XG5cbiAgLy8gUmV0dXJuIHRoZSBpbnN0YW5jZSBzbyBpdCBjYW4gYmUgZWFzaWx5IGRlcmVmZXJlbmNlZFxuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogRXhwb3NlIFNsaWRlb3V0XG4gKi9cbm1vZHVsZS5leHBvcnRzID0gU2xpZGVvdXQ7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciByZXF1ZXN0QW5pbUZyYW1lID0gKGZ1bmN0aW9uKCkge1xuICByZXR1cm4gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fFxuICAgIHdpbmRvdy53ZWJraXRSZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHxcbiAgICBmdW5jdGlvbiAoY2FsbGJhY2spIHtcbiAgICAgIHdpbmRvdy5zZXRUaW1lb3V0KGNhbGxiYWNrLCAxMDAwIC8gNjApO1xuICAgIH07XG59KCkpO1xuXG5mdW5jdGlvbiBkZWNvdXBsZShub2RlLCBldmVudCwgZm4pIHtcbiAgdmFyIGV2ZSxcbiAgICAgIHRyYWNraW5nID0gZmFsc2U7XG5cbiAgZnVuY3Rpb24gY2FwdHVyZUV2ZW50KGUpIHtcbiAgICBldmUgPSBlO1xuICAgIHRyYWNrKCk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFjaygpIHtcbiAgICBpZiAoIXRyYWNraW5nKSB7XG4gICAgICByZXF1ZXN0QW5pbUZyYW1lKHVwZGF0ZSk7XG4gICAgICB0cmFja2luZyA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gdXBkYXRlKCkge1xuICAgIGZuLmNhbGwobm9kZSwgZXZlKTtcbiAgICB0cmFja2luZyA9IGZhbHNlO1xuICB9XG5cbiAgbm9kZS5hZGRFdmVudExpc3RlbmVyKGV2ZW50LCBjYXB0dXJlRXZlbnQsIGZhbHNlKTtcblxuICByZXR1cm4gY2FwdHVyZUV2ZW50O1xufVxuXG4vKipcbiAqIEV4cG9zZSBkZWNvdXBsZVxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGRlY291cGxlO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5cbnZhciBfY2xhc3NDYWxsQ2hlY2sgPSBmdW5jdGlvbiAoaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIik7IH0gfTtcblxuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZTtcbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICogQGNsYXNzXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIGEgbmV3IGluc3RhbmNlIG9mIEVtaXR0ZXIuXG4gKiBAZXhhbXBsZVxuICogLy8gQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICogdmFyIEVtaXR0ZXIgPSByZXF1aXJlKCdlbWl0dGVyJyk7XG4gKlxuICogdmFyIGVtaXR0ZXIgPSBuZXcgRW1pdHRlcigpO1xuICovXG5cbnZhciBFbWl0dGVyID0gKGZ1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRW1pdHRlcigpIHtcbiAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgRW1pdHRlcik7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIGxpc3RlbmVyIHRvIHRoZSBjb2xsZWN0aW9uIGZvciB0aGUgc3BlY2lmaWVkIGV2ZW50LlxuICAgKiBAbWVtYmVyb2YhIEVtaXR0ZXIucHJvdG90eXBlXG4gICAqIEBmdW5jdGlvblxuICAgKiBAcGFyYW0ge1N0cmluZ30gZXZlbnQgLSBUaGUgZXZlbnQgbmFtZS5cbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgLSBBIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIGFkZC5cbiAgICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyBBZGQgYW4gZXZlbnQgbGlzdGVuZXIgdG8gXCJmb29cIiBldmVudC5cbiAgICogZW1pdHRlci5vbignZm9vJywgbGlzdGVuZXIpO1xuICAgKi9cblxuICBFbWl0dGVyLnByb3RvdHlwZS5vbiA9IGZ1bmN0aW9uIG9uKGV2ZW50LCBsaXN0ZW5lcikge1xuICAgIC8vIFVzZSB0aGUgY3VycmVudCBjb2xsZWN0aW9uIG9yIGNyZWF0ZSBpdC5cbiAgICB0aGlzLl9ldmVudENvbGxlY3Rpb24gPSB0aGlzLl9ldmVudENvbGxlY3Rpb24gfHwge307XG5cbiAgICAvLyBVc2UgdGhlIGN1cnJlbnQgY29sbGVjdGlvbiBvZiBhbiBldmVudCBvciBjcmVhdGUgaXQuXG4gICAgdGhpcy5fZXZlbnRDb2xsZWN0aW9uW2V2ZW50XSA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0gfHwgW107XG5cbiAgICAvLyBBcHBlbmRzIHRoZSBsaXN0ZW5lciBpbnRvIHRoZSBjb2xsZWN0aW9uIG9mIHRoZSBnaXZlbiBldmVudFxuICAgIHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0ucHVzaChsaXN0ZW5lcik7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICAvKipcbiAgICogQWRkcyBhIGxpc3RlbmVyIHRvIHRoZSBjb2xsZWN0aW9uIGZvciB0aGUgc3BlY2lmaWVkIGV2ZW50IHRoYXQgd2lsbCBiZSBjYWxsZWQgb25seSBvbmNlLlxuICAgKiBAbWVtYmVyb2YhIEVtaXR0ZXIucHJvdG90eXBlXG4gICAqIEBmdW5jdGlvblxuICAgKiBAcGFyYW0ge1N0cmluZ30gZXZlbnQgLSBUaGUgZXZlbnQgbmFtZS5cbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgLSBBIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIGFkZC5cbiAgICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBhbiBpbnN0YW5jZSBvZiBFbWl0dGVyLlxuICAgKiBAZXhhbXBsZVxuICAgKiAvLyBXaWxsIGFkZCBhbiBldmVudCBoYW5kbGVyIHRvIFwiZm9vXCIgZXZlbnQgb25jZS5cbiAgICogZW1pdHRlci5vbmNlKCdmb28nLCBsaXN0ZW5lcik7XG4gICAqL1xuXG4gIEVtaXR0ZXIucHJvdG90eXBlLm9uY2UgPSBmdW5jdGlvbiBvbmNlKGV2ZW50LCBsaXN0ZW5lcikge1xuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIGZ1bmN0aW9uIGZuKCkge1xuICAgICAgc2VsZi5vZmYoZXZlbnQsIGZuKTtcbiAgICAgIGxpc3RlbmVyLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgZm4ubGlzdGVuZXIgPSBsaXN0ZW5lcjtcblxuICAgIHRoaXMub24oZXZlbnQsIGZuKTtcblxuICAgIHJldHVybiB0aGlzO1xuICB9O1xuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGEgbGlzdGVuZXIgZnJvbSB0aGUgY29sbGVjdGlvbiBmb3IgdGhlIHNwZWNpZmllZCBldmVudC5cbiAgICogQG1lbWJlcm9mISBFbWl0dGVyLnByb3RvdHlwZVxuICAgKiBAZnVuY3Rpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IGV2ZW50IC0gVGhlIGV2ZW50IG5hbWUuXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IGxpc3RlbmVyIC0gQSBsaXN0ZW5lciBmdW5jdGlvbiB0byByZW1vdmUuXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IFJldHVybnMgYW4gaW5zdGFuY2Ugb2YgRW1pdHRlci5cbiAgICogQGV4YW1wbGVcbiAgICogLy8gUmVtb3ZlIGEgZ2l2ZW4gbGlzdGVuZXIuXG4gICAqIGVtaXR0ZXIub2ZmKCdmb28nLCBsaXN0ZW5lcik7XG4gICAqL1xuXG4gIEVtaXR0ZXIucHJvdG90eXBlLm9mZiA9IGZ1bmN0aW9uIG9mZihldmVudCwgbGlzdGVuZXIpIHtcblxuICAgIHZhciBsaXN0ZW5lcnMgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBEZWZpbmVzIGxpc3RlbmVycyB2YWx1ZS5cbiAgICBpZiAoIXRoaXMuX2V2ZW50Q29sbGVjdGlvbiB8fCAhKGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0pKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICBsaXN0ZW5lcnMuZm9yRWFjaChmdW5jdGlvbiAoZm4sIGkpIHtcbiAgICAgIGlmIChmbiA9PT0gbGlzdGVuZXIgfHwgZm4ubGlzdGVuZXIgPT09IGxpc3RlbmVyKSB7XG4gICAgICAgIC8vIFJlbW92ZXMgdGhlIGdpdmVuIGxpc3RlbmVyLlxuICAgICAgICBsaXN0ZW5lcnMuc3BsaWNlKGksIDEpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gUmVtb3ZlcyBhbiBlbXB0eSBldmVudCBjb2xsZWN0aW9uLlxuICAgIGlmIChsaXN0ZW5lcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICBkZWxldGUgdGhpcy5fZXZlbnRDb2xsZWN0aW9uW2V2ZW50XTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICAvKipcbiAgICogRXhlY3V0ZSBlYWNoIGl0ZW0gaW4gdGhlIGxpc3RlbmVyIGNvbGxlY3Rpb24gaW4gb3JkZXIgd2l0aCB0aGUgc3BlY2lmaWVkIGRhdGEuXG4gICAqIEBtZW1iZXJvZiEgRW1pdHRlci5wcm90b3R5cGVcbiAgICogQGZ1bmN0aW9uXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBldmVudCAtIFRoZSBuYW1lIG9mIHRoZSBldmVudCB5b3Ugd2FudCB0byBlbWl0LlxuICAgKiBAcGFyYW0gey4uLk9iamVjdH0gZGF0YSAtIERhdGEgdG8gcGFzcyB0byB0aGUgbGlzdGVuZXJzLlxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIGFuIGluc3RhbmNlIG9mIEVtaXR0ZXIuXG4gICAqIEBleGFtcGxlXG4gICAqIC8vIEVtaXRzIHRoZSBcImZvb1wiIGV2ZW50IHdpdGggJ3BhcmFtMScgYW5kICdwYXJhbTInIGFzIGFyZ3VtZW50cy5cbiAgICogZW1pdHRlci5lbWl0KCdmb28nLCAncGFyYW0xJywgJ3BhcmFtMicpO1xuICAgKi9cblxuICBFbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24gZW1pdChldmVudCkge1xuICAgIHZhciBfdGhpcyA9IHRoaXM7XG5cbiAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgYXJncyA9IEFycmF5KF9sZW4gPiAxID8gX2xlbiAtIDEgOiAwKSwgX2tleSA9IDE7IF9rZXkgPCBfbGVuOyBfa2V5KyspIHtcbiAgICAgIGFyZ3NbX2tleSAtIDFdID0gYXJndW1lbnRzW19rZXldO1xuICAgIH1cblxuICAgIHZhciBsaXN0ZW5lcnMgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBEZWZpbmVzIGxpc3RlbmVycyB2YWx1ZS5cbiAgICBpZiAoIXRoaXMuX2V2ZW50Q29sbGVjdGlvbiB8fCAhKGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50Q29sbGVjdGlvbltldmVudF0pKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvLyBDbG9uZSBsaXN0ZW5lcnNcbiAgICBsaXN0ZW5lcnMgPSBsaXN0ZW5lcnMuc2xpY2UoMCk7XG5cbiAgICBsaXN0ZW5lcnMuZm9yRWFjaChmdW5jdGlvbiAoZm4pIHtcbiAgICAgIHJldHVybiBmbi5hcHBseShfdGhpcywgYXJncyk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICByZXR1cm4gRW1pdHRlcjtcbn0pKCk7XG5cbi8qKlxuICogRXhwb3J0cyBFbWl0dGVyXG4gKi9cbmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gRW1pdHRlcjtcbm1vZHVsZS5leHBvcnRzID0gZXhwb3J0c1tcImRlZmF1bHRcIl07Il19
================================================
FILE: index.css
================================================
body {
width: 100%;
height: 100%;
}
.slideout-menu {
position: fixed;
top: 0;
bottom: 0;
width: 256px;
min-height: 100vh;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
z-index: 0;
display: none;
}
.slideout-menu-left {
left: 0;
}
.slideout-menu-right {
right: 0;
}
.slideout-panel {
position: relative;
z-index: 1;
will-change: transform;
background-color: #FFF; /* A background-color is required */
min-height: 100vh;
}
.slideout-open,
.slideout-open body,
.slideout-open .slideout-panel {
overflow: hidden;
}
.slideout-open .slideout-menu {
display: block;
}
================================================
FILE: index.js
================================================
'use strict';
/**
* Module dependencies
*/
var decouple = require('decouple');
var Emitter = require('emitter');
/**
* Privates
*/
var scrollTimeout;
var scrolling = false;
var doc = window.document;
var html = doc.documentElement;
var msPointerSupported = window.navigator.msPointerEnabled;
var touch = {
'start': msPointerSupported ? 'MSPointerDown' : 'touchstart',
'move': msPointerSupported ? 'MSPointerMove' : 'touchmove',
'end': msPointerSupported ? 'MSPointerUp' : 'touchend'
};
var prefix = (function prefix() {
var regex = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/;
var styleDeclaration = doc.getElementsByTagName('script')[0].style;
for (var prop in styleDeclaration) {
if (regex.test(prop)) {
return '-' + prop.match(regex)[0].toLowerCase() + '-';
}
}
// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
// However (prop in style) returns the correct value, so we'll have to test for
// the precence of a specific property
if ('WebkitOpacity' in styleDeclaration) { return '-webkit-'; }
if ('KhtmlOpacity' in styleDeclaration) { return '-khtml-'; }
return '';
}());
function extend(destination, from) {
for (var prop in from) {
if (from[prop]) {
destination[prop] = from[prop];
}
}
return destination;
}
function inherits(child, uber) {
child.prototype = extend(child.prototype || {}, uber.prototype);
}
function hasIgnoredElements(el) {
while (el.parentNode) {
if (el.getAttribute('data-slideout-ignore') !== null) {
return el;
}
el = el.parentNode;
}
return null;
}
/**
* Slideout constructor
*/
function Slideout(options) {
options = options || {};
// Sets default values
this._startOffsetX = 0;
this._currentOffsetX = 0;
this._opening = false;
this._moved = false;
this._opened = false;
this._preventOpen = false;
// Sets panel
this.panel = options.panel;
this.menu = options.menu;
// Sets options
this._touch = options.touch === undefined ? true : options.touch && true;
this._side = options.side || 'left';
this._easing = options.fx || options.easing || 'ease';
this._duration = parseInt(options.duration, 10) || 300;
this._tolerance = parseInt(options.tolerance, 10) || 70;
this._padding = this._translateTo = parseInt(options.padding, 10) || 256;
this._orientation = this._side === 'right' ? -1 : 1;
this._translateTo *= this._orientation;
// Sets classnames
if (!this.panel.classList.contains('slideout-panel')) {
this.panel.classList.add('slideout-panel');
}
if (!this.panel.classList.contains('slideout-panel-' + this._side)) {
this.panel.classList.add('slideout-panel-' + this._side);
}
if (!this.menu.classList.contains('slideout-menu')) {
this.menu.classList.add('slideout-menu');
}
if (!this.menu.classList.contains('slideout-menu-' + this._side)) {
this.menu.classList.add('slideout-menu-' + this._side);
}
// Init touch events
if (this._touch) {
this._initTouchEvents();
}
}
/**
* Inherits from Emitter
*/
inherits(Slideout, Emitter);
/**
* Opens the slideout menu.
*/
Slideout.prototype.open = function() {
var self = this;
this.emit('beforeopen');
if (!html.classList.contains('slideout-open')) {
html.classList.add('slideout-open');
}
this._setTransition();
this._translateXTo(this._translateTo);
this._opened = true;
setTimeout(function() {
self.panel.style.transition = self.panel.style['-webkit-transition'] = '';
self.emit('open');
}, this._duration + 50);
return this;
};
/**
* Closes slideout menu.
*/
Slideout.prototype.close = function() {
var self = this;
if (!this.isOpen() && !this._opening) {
return this;
}
this.emit('beforeclose');
this._setTransition();
this._translateXTo(0);
this._opened = false;
setTimeout(function() {
html.classList.remove('slideout-open');
self.panel.style.transition = self.panel.style['-webkit-transition'] = self.panel.style[prefix + 'transform'] = self.panel.style.transform = '';
self.emit('close');
}, this._duration + 50);
return this;
};
/**
* Toggles (open/close) slideout menu.
*/
Slideout.prototype.toggle = function() {
return this.isOpen() ? this.close() : this.open();
};
/**
* Returns true if the slideout is currently open, and false if it is closed.
*/
Slideout.prototype.isOpen = function() {
return this._opened;
};
/**
* Translates panel and updates currentOffset with a given X point
*/
Slideout.prototype._translateXTo = function(translateX) {
this._currentOffsetX = translateX;
this.panel.style[prefix + 'transform'] = this.panel.style.transform = 'translateX(' + translateX + 'px)';
return this;
};
/**
* Set transition properties
*/
Slideout.prototype._setTransition = function() {
this.panel.style[prefix + 'transition'] = this.panel.style.transition = prefix + 'transform ' + this._duration + 'ms ' + this._easing;
return this;
};
/**
* Initializes touch event
*/
Slideout.prototype._initTouchEvents = function() {
var self = this;
/**
* Decouple scroll event
*/
this._onScrollFn = decouple(doc, 'scroll', function() {
if (!self._moved) {
clearTimeout(scrollTimeout);
scrolling = true;
scrollTimeout = setTimeout(function() {
scrolling = false;
}, 250);
}
});
/**
* Prevents touchmove event if slideout is moving
*/
this._preventMove = function(eve) {
if (self._moved) {
eve.preventDefault();
}
};
doc.addEventListener(touch.move, this._preventMove);
/**
* Resets values on touchstart
*/
this._resetTouchFn = function(eve) {
if (typeof eve.touches === 'undefined') {
return;
}
self._moved = false;
self._opening = false;
self._startOffsetX = eve.touches[0].pageX;
self._preventOpen = (!self._touch || (!self.isOpen() && self.menu.clientWidth !== 0));
};
this.panel.addEventListener(touch.start, this._resetTouchFn);
/**
* Resets values on touchcancel
*/
this._onTouchCancelFn = function() {
self._moved = false;
self._opening = false;
};
this.panel.addEventListener('touchcancel', this._onTouchCancelFn);
/**
* Toggles slideout on touchend
*/
this._onTouchEndFn = function() {
if (self._moved) {
self.emit('translateend');
(self._opening && Math.abs(self._currentOffsetX) > self._tolerance) ? self.open() : self.close();
}
self._moved = false;
};
this.panel.addEventListener(touch.end, this._onTouchEndFn);
/**
* Translates panel on touchmove
*/
this._onTouchMoveFn = function(eve) {
if (
scrolling ||
self._preventOpen ||
typeof eve.touches === 'undefined' ||
hasIgnoredElements(eve.target)
) {
return;
}
var dif_x = eve.touches[0].clientX - self._startOffsetX;
var translateX = self._currentOffsetX = dif_x;
if (Math.abs(translateX) > self._padding) {
return;
}
if (Math.abs(dif_x) > 20) {
self._opening = true;
var oriented_dif_x = dif_x * self._orientation;
if (self._opened && oriented_dif_x > 0 || !self._opened && oriented_dif_x < 0) {
return;
}
if (!self._moved) {
self.emit('translatestart');
}
if (oriented_dif_x <= 0) {
translateX = dif_x + self._padding * self._orientation;
self._opening = false;
}
if (!(self._moved && html.classList.contains('slideout-open'))) {
html.classList.add('slideout-open');
}
self.panel.style[prefix + 'transform'] = self.panel.style.transform = 'translateX(' + translateX + 'px)';
self.emit('translate', translateX);
self._moved = true;
}
};
this.panel.addEventListener(touch.move, this._onTouchMoveFn);
return this;
};
/**
* Enable opening the slideout via touch events.
*/
Slideout.prototype.enableTouch = function() {
this._touch = true;
return this;
};
/**
* Disable opening the slideout via touch events.
*/
Slideout.prototype.disableTouch = function() {
this._touch = false;
return this;
};
/**
* Destroy an instance of slideout.
*/
Slideout.prototype.destroy = function() {
// Close before clean
this.close();
// Remove event listeners
doc.removeEventListener(touch.move, this._preventMove);
this.panel.removeEventListener(touch.start, this._resetTouchFn);
this.panel.removeEventListener('touchcancel', this._onTouchCancelFn);
this.panel.removeEventListener(touch.end, this._onTouchEndFn);
this.panel.removeEventListener(touch.move, this._onTouchMoveFn);
doc.removeEventListener('scroll', this._onScrollFn);
// Remove methods
this.open = this.close = function() {};
// Return the instance so it can be easily dereferenced
return this;
};
/**
* Expose Slideout
*/
module.exports = Slideout;
================================================
FILE: package.json
================================================
{
"name": "slideout",
"repository": "git@github.com:mango/slideout.git",
"description": "A touch slideout navigation menu for your mobile web apps.",
"author": "Guille Paz <guille87paz@gmail.com>",
"version": "1.0.1",
"scripts": {
"build": "node browserify.js",
"test": "npm run build && istanbul cover _mocha",
"dist": "node browserify.js && uglifyjs ./dist/slideout.js -m -o ./dist/slideout.min.js",
"hint": "jshint index.js"
},
"dependencies": {
"decouple": "0.0.2",
"emitter": "git+https://github.com/Mango/emitter.git#0.0.7"
},
"devDependencies": {
"better-assert": "1.0.2",
"browserify": "13.1.1",
"coveralls": "2.11.15",
"istanbul": "0.4.5",
"jsdom": "9.8.3",
"jshint": "2.9.4",
"mocha": "3.1.2",
"uglify-js": "2.7.4"
},
"main": "index.js",
"keywords": [
"slideout",
"offcanvas",
"menu",
"touch"
],
"license": "MIT",
"spm": {
"dependencies": {
"decouple": "0.0.2",
"emitter": "git+https://github.com/Mango/emitter.git#0.0.7"
},
"main": "index.js"
},
"filename": "slideout.min.js",
"autoupdate": {
"source": "git",
"target": "git://github.com/Mango/slideout.git",
"basePath": "dist",
"files": [
"*.js"
]
}
}
================================================
FILE: test/index.html
================================================
<!doctype html>
<!--[if IE 8]><html class="ie8 lt-ie10"><![endif]-->
<!--[if IE 9]><html class="ie9 lt-ie10"><![endif]-->
<!--[if gt IE 9]><!--><html lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="cleartype" content="on">
<meta name="MobileOptimized" content="320">
<meta name="HandheldFriendly" content="True">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/1.13.0/mocha.min.css">
<link rel="stylesheet" href="test.css">
<link rel="stylesheet" href="../index.css">
<title>Slideout tests</title>
</head>
<body>
<nav id="menu" class="menu">
<a href="https://github.com/mango/slideout" target="_blank">
<header class="menu-header">
<span class="menu-header-title">Tests</span>
</header>
</a>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
</nav>
<main id="panel" class="panel">
<header class="panel-header">
<button class="btn-hamburger js-slideout-toggle"></button>
<h1 class="title">Slideout.js</h1>
</header>
<section class="box">
<h2 class="box-title">Test</h2>
<div id="mocha" class="box-content"></div>
</section>
<footer class="panel-footer">
<p>with <span class="heart">❤</span> by <a href="https://getmango.com/en" target="_blank">Mango</a></p>
</footer>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/1.13.0/mocha.min.js"></script>
<script>
mocha.setup('bdd');
var exports = null;
function assert(expr, msg) {
if (!expr) throw new Error(msg || 'failed');
}
</script>
<script src="../dist/slideout.js"></script>
<script src="test.js"></script>
<script>
window.onload = function() {
document.querySelector('.js-slideout-toggle').addEventListener('click', function() {
slideout.toggle();
});
document.querySelector('.menu').addEventListener('click', function(eve) {
if (eve.target.nodeName === 'A') { slideout.close(); }
});
var runner = mocha.run();
};
</script>
</body>
</html>
================================================
FILE: test/index.right.html
================================================
<!doctype html>
<!--[if IE 8]><html class="ie8 lt-ie10"><![endif]-->
<!--[if IE 9]><html class="ie9 lt-ie10"><![endif]-->
<!--[if gt IE 9]><!--><html lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="cleartype" content="on">
<meta name="MobileOptimized" content="320">
<meta name="HandheldFriendly" content="True">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="test.css">
<link rel="stylesheet" href="../index.css">
<style>
.slideout-menu { text-align: right; }
.btn-hamburger { left: auto; right: 12px;}
.box { height: 1500px; }
</style>
<title>Slideout right tests</title>
</head>
<body>
<nav id="menu" class="menu">
<a href="https://github.com/mango/slideout" target="_blank">
<header class="menu-header">
<span class="menu-header-title">Tests</span>
</header>
</a>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
<section class="menu-section">
<h3 class="menu-section-title">Docs</h3>
<ul class="menu-section-list">
<li><a href="https://github.com/mango/slideout#installation" target="_blank">Installation</a></li>
<li><a href="https://github.com/mango/slideout#usage" target="_blank">Usage</a></li>
<li><a href="https://github.com/mango/slideout#api" target="_blank">API</a></li>
<li><a href="https://github.com/mango/slideout#npm-scripts" target="_blank">npm-scripts</a></li>
</ul>
</section>
</nav>
<main id="panel" class="panel">
<header class="panel-header">
<button class="btn-hamburger js-slideout-toggle"></button>
<h1 class="title">Slideout.js</h1>
</header>
<section class="box">
<h2 class="box-title">Test</h2>
<div class="box-content"></div>
</section>
<footer class="panel-footer">
<p>with <span class="heart">❤</span> by <a href="https://getmango.com/en" target="_blank">Mango</a></p>
</footer>
</main>
<script src="../dist/slideout.js"></script>
<script>
window.onload = function() {
var slideout = new Slideout({
'panel': document.getElementById('panel'),
'menu': document.getElementById('menu'),
'side': 'right'
});
document.querySelector('.js-slideout-toggle').addEventListener('click', function() {
slideout.toggle();
});
document.querySelector('.menu').addEventListener('click', function(eve) {
if (eve.target.nodeName === 'A') { slideout.close(); }
});
};
</script>
</body>
</html>
================================================
FILE: test/test.css
================================================
/*! 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}
html,
body {
font: 100%/1.4em 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0 auto;
color: #222;
-webkit-text-size-adjust: none;
-webkit-font-smoothing: antialiased;
}
body,
.panel {
background-color: #fff;
}
.menu {
background-color: #1D1F20;
background-image: linear-gradient(145deg, #1D1F20, #404348);
}
a {
color: #4B5;
text-decoration: none;
}
.menu a {
color: #fff;
}
.menu a:hover {
text-decoration: underline;
}
.menu-header {
border-bottom: 1px solid #2a2d2f;
padding: 20px;
background-size: 32px;
}
.menu-header-title {
font-weight: 400;
letter-spacing: 0.5px;
margin: 0;
}
.menu-section {
margin: 25px 0;
}
.menu-section-title {
text-transform: uppercase;
color: #85888d;
font-weight: 200;
font-size: 13px;
letter-spacing: 1px;
padding: 0 20px;
margin:0;
}
.menu-section-list {
padding:0;
margin: 10px 0;
list-style:none;
}
.menu-section-list a {
display: block;
padding: 10px 20px;
}
.panel {
text-align: center;
padding-top: 5px;
min-height: 100%;
}
#mocha {
text-align: left;
margin:0 !important;
}
#mocha-stats {
top: 70px;
}
/**
* Header
*/
.panel-header {
margin: 120px auto 55px;
}
.title {
font-size: 3.2em;
line-height: 1em;
margin: 0 0 15px;
color: #4B5;
font-weight: 400;
}
/**
* hamburger
*/
.btn-hamburger {
border: none;
position: absolute;
top: 12px;
left: 12px;
outline:none;
background: url('assets/menu.png') no-repeat center;
width: 50px;
height: 50px;
}
/**
* Boxes
*/
.box {
border: 1px solid #4b5;
border-radius: 4px;
text-align: left;
margin: 50px 10px;
position: relative;
}
.box:before,
.box:after {
content: ' ';
display: inline-block;
width: 1px;
height: 50px;
border-left: 1px solid #4b5;
position: absolute;
left: 50%;
}
.box:before {
top: -50px;
}
.box:after {
bottom: -50px;
}
.box-title {
margin: 0;
padding:10px 20px;
border-bottom: 1px solid #4b5;
color: #4b5;
font-size: 1.2em;
font-weight: 400;
}
.box-content {
padding:20px;
background-color: #f8f8f8;
}
/**
* Medium Screens
*/
@media all and (min-width:40em) {
.btn-hamburger {
top: 20px;
left: 30px;
}
.panel-header {
margin-top: 40px;
width: 455px;
}
.title {
font-size: 4.2em;
}
}
/**
* Large Screens
*/
@media all and (min-width: 54em) {
.box {
width: 70%;
max-width: 1200px;
margin: 50px auto;
}
}
/**
* Footer
*/
.panel-footer {
margin: 10px auto 20px;
}
.panel-footer p {
padding-bottom: 20px;
}
.heart {
font-style: normal;
font-weight: 500;
color: #c0392b;
text-decoration: none;
}
================================================
FILE: test/test.js
================================================
if (exports) {
var fs = require('fs');
var jsdom = require('jsdom').jsdom;
var html = fs.readFileSync('./test/index.html', 'utf-8');
var document = jsdom(html);
window = document.defaultView;
var Slideout = require('../');
var assert = require('better-assert');
}
var doc = window.document;
var beforeopenEvent = false;
var openEvent = false;
var beforecloseEvent = false;
var closeEvent = false;
var slideout = new Slideout({
'panel': doc.getElementById('panel'),
'menu': doc.getElementById('menu'),
});
slideout
.on('beforeopen', function() {
beforeopenEvent = true;
})
.on('open', function() {
openEvent = true;
})
.on('beforeclose', function() {
beforecloseEvent = true;
})
.on('close', function() {
closeEvent = true;
});
describe('Slideout', function () {
it('should be defined.', function () {
assert(Slideout !== undefined);
});
it('should be a function.', function () {
assert(typeof Slideout === 'function');
});
it('should return a new instance.', function () {
assert(slideout instanceof Slideout);
});
describe('should have the following methods:', function () {
var methods = [
'open',
'close',
'toggle',
'isOpen',
'_initTouchEvents',
'_translateXTo',
'_setTransition',
'on',
'once',
'off',
'emit'
];
var i = 0;
var len = methods.length;
for (i; i < len; i += 1) {
(function (i) {
it('.' + methods[i] + '()', function (done) {
assert(typeof slideout[methods[i]] === 'function');
done()
});
}(i));
}
});
describe('should define the following properties:', function () {
var properties = [
'panel',
'menu',
'_startOffsetX',
'_currentOffsetX',
'_opening',
'_moved',
'_opened',
'_easing',
'_duration',
'_tolerance',
'_padding',
'_touch',
'_side'
];
var i = 0;
var len = properties.length;
for (i; i < len; i += 1) {
(function (i) {
it('.' + properties[i] + '()', function (done) {
assert(slideout[properties[i]] !== undefined);
done()
});
}(i));
}
});
it('should add classnames to panel and menu DOM elements.', function () {
assert(slideout.panel.className.search('slideout-panel') !== -1);
assert(slideout.panel.className.search('slideout-panel-left') !== -1);
assert(slideout.menu.className.search('slideout-menu') !== -1);
assert(slideout.menu.className.search('slideout-menu-left') !== -1);
});
describe('.open()', function () {
it('should add "slideout-open" classname to HTML.', function () {
assert(doc.documentElement.className.search('slideout-open') === -1);
slideout.open();
assert(doc.documentElement.className.search('slideout-open') !== -1);
});
it('should translateX the panel to the given padding.', function () {
assert(slideout.panel.style.transform === 'translateX(256px)');
assert(slideout.panel.style.transition.search(/transform 300ms ease/) !== -1);
});
it('should set _opened to true.', function () {
assert(slideout._opened === true);
});
it('should emit "beforeopen" event.', function () {
assert(beforeopenEvent === true);
});
it('should emit "open" event.', function (done) {
setTimeout(function(){
assert(openEvent === true);
done();
}, 400);
});
});
describe('.isOpen()', function () {
it('should return true if the slideout is opened.', function () {
assert(slideout.isOpen());
});
});
describe('.close()', function () {
it('should remove "slideout-open" classname to HTML.', function (done) {
assert(doc.documentElement.className.search('slideout-open') !== -1);
slideout.close();
setTimeout(function(){
assert(doc.documentElement.className.search('slideout-open') === -1);
done();
}, 350);
});
it('should translateX the panel to 0.', function () {
assert(slideout.panel.style.transform === '');
assert(slideout.panel.style.transition === '');
});
it('should set _opened to false.', function () {
assert(slideout._opened === false);
});
it('should emit "beforeclose" event.', function () {
assert(beforecloseEvent === true);
});
it('should emit "close" event.', function () {
assert(closeEvent === true);
});
});
describe('.toggle()', function () {
it('should show the slideout if it is not opened.', function (done) {
assert(doc.documentElement.className.search('slideout-open') === -1);
slideout.toggle();
assert(doc.documentElement.className.search('slideout-open') !== -1);
slideout.toggle();
setTimeout(function(){
assert(doc.documentElement.className.search('slideout-open') === -1);
done();
}, 350);
});
});
describe('.destroy()', function() {
it('should destroy the instance internals allowing a new one to be created in it\'s place.', function(){
slideout.destroy();
slideout = new Slideout({
'panel': doc.getElementById('panel'),
'menu': doc.getElementById('menu')
});
slideout.open();
setTimeout(function(){ slideout.close(); }, 750);
});
});
});
gitextract_keq77s3o/
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bower.json
├── browserify.js
├── component.json
├── dist/
│ └── slideout.js
├── index.css
├── index.js
├── package.json
└── test/
├── index.html
├── index.right.html
├── test.css
└── test.js
SYMBOL INDEX (12 symbols across 2 files)
FILE: dist/slideout.js
function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function extend (line 38) | function extend(destination, from) {
function inherits (line 46) | function inherits(child, uber) {
function hasIgnoredElements (line 49) | function hasIgnoredElements(el) {
function Slideout (line 62) | function Slideout(options) {
function decouple (line 360) | function decouple(node, event, fn) {
function Emitter (line 409) | function Emitter() {
function fn (line 453) | function fn() {
FILE: index.js
function extend (line 37) | function extend(destination, from) {
function inherits (line 45) | function inherits(child, uber) {
function hasIgnoredElements (line 48) | function hasIgnoredElements(el) {
function Slideout (line 61) | function Slideout(options) {
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (88K chars).
[
{
"path": ".gitignore",
"chars": 49,
"preview": ".DS_Store\nnode_modules\nbower_components\ncoverage\n"
},
{
"path": ".jshintrc",
"chars": 377,
"preview": "{\n \"bitwise\": false,\n \"curly\": true,\n \"eqeqeq\": true,\n \"expr\": true,\n \"forin\": true,\n \"freeze\": true,\n \"funcscope"
},
{
"path": ".travis.yml",
"chars": 156,
"preview": "language: node_js\nnode_js:\n - 6.9.1\n# Send coverage data to Coveralls\nafter_script: \"cat ./coverage/lcov.info | ./node_"
},
{
"path": "CONTRIBUTING.md",
"chars": 988,
"preview": "# Contributing\n\n## Code style\nRegarding code style like indentation and whitespace, **follow the conventions you see use"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Mango\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 12160,
"preview": "# Slideout.js\n\n[![NPM version][npm-image]][npm-link] [![License][lic-image]][npm-link] [![Build status][travis-image]][t"
},
{
"path": "bower.json",
"chars": 706,
"preview": "{\n \"name\": \"slideout.js\",\n \"repository\": \"https://github.com/Mango/slideout\",\n \"description\": \"A touch slideout navig"
},
{
"path": "browserify.js",
"chars": 372,
"preview": "'use strict';\n\nvar fs = require('fs');\nvar browserify = require('browserify');\n\nif (!fs.existsSync('./dist')) {\n fs.mkd"
},
{
"path": "component.json",
"chars": 470,
"preview": "{\n \"name\": \"slideout\",\n \"repo\": \"mango/slideout\",\n \"description\": \"A touch slideout navigation menu for your mobile w"
},
{
"path": "dist/slideout.js",
"chars": 38041,
"preview": "!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();else if(\"function\"==typeof defin"
},
{
"path": "index.css",
"chars": 616,
"preview": "body {\n width: 100%;\n height: 100%;\n}\n\n.slideout-menu {\n position: fixed;\n top: 0;\n bottom: 0;\n width: 256px;\n mi"
},
{
"path": "index.js",
"chars": 8829,
"preview": "'use strict';\n\n/**\n * Module dependencies\n */\nvar decouple = require('decouple');\nvar Emitter = require('emitter');\n\n/**"
},
{
"path": "package.json",
"chars": 1276,
"preview": "{\n \"name\": \"slideout\",\n \"repository\": \"git@github.com:mango/slideout.git\",\n \"description\": \"A touch slideout navigati"
},
{
"path": "test/index.html",
"chars": 4568,
"preview": "<!doctype html>\n<!--[if IE 8]><html class=\"ie8 lt-ie10\"><![endif]-->\n<!--[if IE 9]><html class=\"ie9 lt-ie10\"><![endif]--"
},
{
"path": "test/index.right.html",
"chars": 4453,
"preview": "<!doctype html>\n<!--[if IE 8]><html class=\"ie8 lt-ie10\"><![endif]-->\n<!--[if IE 9]><html class=\"ie9 lt-ie10\"><![endif]--"
},
{
"path": "test/test.css",
"chars": 4631,
"preview": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webki"
},
{
"path": "test/test.js",
"chars": 5383,
"preview": "if (exports) {\n var fs = require('fs');\n var jsdom = require('jsdom').jsdom;\n var html = fs.readFileSync('./test/inde"
}
]
About this extraction
This page contains the full source code of the Mango/slideout GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (82.2 KB), approximately 32.2k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.