module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(__webpack_require__(2));
__export(__webpack_require__(8));
__export(__webpack_require__(23));
__export(__webpack_require__(54));
__export(__webpack_require__(72));
__export(__webpack_require__(73));
__export(__webpack_require__(74));
__export(__webpack_require__(9));
__export(__webpack_require__(79));
__export(__webpack_require__(12));
__export(__webpack_require__(15));
__export(__webpack_require__(14));
__export(__webpack_require__(22));
__export(__webpack_require__(17));
__export(__webpack_require__(83));
// these modules don't export anything
__webpack_require__(84);
__webpack_require__(85);
__webpack_require__(86);
__webpack_require__(87);
__webpack_require__(88);
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
var core_1 = __webpack_require__(3);
var router_1 = __webpack_require__(4);
var http_1 = __webpack_require__(5);
var app_1 = __webpack_require__(6);
var config_1 = __webpack_require__(8);
var platform_1 = __webpack_require__(9);
var form_1 = __webpack_require__(13);
var keyboard_1 = __webpack_require__(14);
var events_1 = __webpack_require__(15);
var nav_registry_1 = __webpack_require__(16);
var translate_1 = __webpack_require__(17);
var click_block_1 = __webpack_require__(12);
var feature_detect_1 = __webpack_require__(18);
var tap_click_1 = __webpack_require__(19);
var dom_1 = __webpack_require__(11);
/**
* @private
*/
function ionicProviders(args) {
if (args === void 0) { args = {}; }
var platform = new platform_1.Platform();
var navRegistry = new nav_registry_1.NavRegistry(args.pages);
var config = args.config;
if (!(config instanceof config_1.Config)) {
config = new config_1.Config(config);
}
platform.url(window.location.href);
platform.userAgent(window.navigator.userAgent);
platform.navigatorPlatform(window.navigator.platform);
platform.load();
config.setPlatform(platform);
var clickBlock = new click_block_1.ClickBlock(config.get('clickBlock'));
var events = new events_1.Events();
var featureDetect = new feature_detect_1.FeatureDetect();
setupDom(window, document, config, platform, clickBlock, featureDetect);
bindEvents(window, document, platform, events);
// prepare the ready promise to fire....when ready
platform.prepareReady(config);
return [
app_1.IonicApp,
core_1.provide(click_block_1.ClickBlock, { useValue: clickBlock }),
core_1.provide(config_1.Config, { useValue: config }),
core_1.provide(platform_1.Platform, { useValue: platform }),
core_1.provide(feature_detect_1.FeatureDetect, { useValue: featureDetect }),
core_1.provide(events_1.Events, { useValue: events }),
core_1.provide(nav_registry_1.NavRegistry, { useValue: navRegistry }),
tap_click_1.TapClick,
form_1.Form,
keyboard_1.Keyboard,
translate_1.Translate,
router_1.ROUTER_PROVIDERS,
core_1.provide(router_1.LocationStrategy, { useClass: router_1.HashLocationStrategy }),
http_1.HTTP_PROVIDERS,
];
}
exports.ionicProviders = ionicProviders;
function setupDom(window, document, config, platform, clickBlock, featureDetect) {
var bodyEle = document.body;
var mode = config.get('mode');
// if dynamic mode links have been added the fire up the correct one
var modeLinkAttr = mode + '-href';
var linkEle = document.head.querySelector('link[' + modeLinkAttr + ']');
if (linkEle) {
var href = linkEle.getAttribute(modeLinkAttr);
linkEle.removeAttribute(modeLinkAttr);
linkEle.href = href;
}
// set the mode class name
// ios/md
bodyEle.classList.add(mode);
// language and direction
platform.setDir(document.documentElement.dir, false);
platform.setLang(document.documentElement.lang, false);
var versions = platform.versions();
platform.platforms().forEach(function (platformName) {
// platform-ios
var platformClass = 'platform-' + platformName;
bodyEle.classList.add(platformClass);
var platformVersion = versions[platformName];
if (platformVersion) {
// platform-ios9
platformClass += platformVersion.major;
bodyEle.classList.add(platformClass);
// platform-ios9_3
bodyEle.classList.add(platformClass + '_' + platformVersion.minor);
}
});
// touch devices should not use :hover CSS pseudo
// enable :hover CSS when the "hoverCSS" setting is not false
if (config.get('hoverCSS') !== false) {
bodyEle.classList.add('enable-hover');
}
if (config.get('clickBlock')) {
clickBlock.enable();
}
// run feature detection tests
featureDetect.run(window, document);
}
/**
* Bind some global events and publish on the 'app' channel
*/
function bindEvents(window, document, platform, events) {
window.addEventListener('online', function (ev) {
events.publish('app:online', ev);
}, false);
window.addEventListener('offline', function (ev) {
events.publish('app:offline', ev);
}, false);
window.addEventListener('orientationchange', function (ev) {
events.publish('app:rotated', ev);
});
// When that status taps, we respond
window.addEventListener('statusTap', function (ev) {
// TODO: Make this more better
var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2);
if (!el) {
return;
}
var content = dom_1.closest(el, 'scroll-content');
if (content) {
var scrollTo = new ScrollTo(content);
scrollTo.start(0, 0, 300, 0);
}
});
// start listening for resizes XXms after the app starts
setTimeout(function () {
window.addEventListener('resize', function () {
platform.windowResize();
});
}, 2000);
}
/***/ },
/* 3 */
/***/ function(module, exports) {
module.exports = require("angular2")["core"];
/***/ },
/* 4 */
/***/ function(module, exports) {
module.exports = require("angular2")["router"];
/***/ },
/* 5 */
/***/ function(module, exports) {
module.exports = require("angular2")["http"];
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
var browser_1 = __webpack_require__(7);
var config_1 = __webpack_require__(8);
var click_block_1 = __webpack_require__(12);
var dom_1 = __webpack_require__(11);
/**
* @private
* Component registry service. For more information on registering
* components see the [IdRef API reference](../id/IdRef/).
*/
var IonicApp = (function () {
function IonicApp(_config, _clickBlock, _zone) {
this._config = _config;
this._clickBlock = _clickBlock;
this._zone = _zone;
this._titleSrv = new browser_1.Title();
this._title = '';
this._disTime = 0;
this._scrollTime = 0;
// Our component registry map
this.components = {};
}
/**
* Sets the document title.
* @param {string} val Value to set the document title to.
*/
IonicApp.prototype.setTitle = function (val) {
var self = this;
if (val !== self._title) {
self._title = val;
this._zone.runOutsideAngular(function () {
function setAppTitle() {
self._titleSrv.setTitle(self._title);
}
dom_1.rafFrames(4, setAppTitle);
});
}
};
/**
* @private
* Sets if the app is currently enabled or not, meaning if it's
* available to accept new user commands. For example, this is set to `false`
* while views transition, a modal slides up, an action-sheet
* slides up, etc. After the transition completes it is set back to `true`.
* @param {bool} isEnabled
* @param {bool} fallback When `isEnabled` is set to `false`, this argument
* is used to set the maximum number of milliseconds that app will wait until
* it will automatically enable the app again. It's basically a fallback incase
* something goes wrong during a transition and the app wasn't re-enabled correctly.
*/
IonicApp.prototype.setEnabled = function (isEnabled, duration) {
if (duration === void 0) { duration = 700; }
this._disTime = (isEnabled ? 0 : Date.now() + duration);
if (duration > 32 || isEnabled) {
// only do a click block if the duration is longer than XXms
this._clickBlock.show(!isEnabled, duration + 64);
}
};
/**
* @private
* Boolean if the app is actively enabled or not.
* @return {bool}
*/
IonicApp.prototype.isEnabled = function () {
return (this._disTime < Date.now());
};
/**
* @private
*/
IonicApp.prototype.setScrolling = function () {
this._scrollTime = Date.now();
};
/**
* @private
* Boolean if the app is actively scrolling or not.
* @return {bool}
*/
IonicApp.prototype.isScrolling = function () {
return (this._scrollTime + 64 > Date.now());
};
/**
* @private
* Register a known component with a key, for easy lookups later.
* @param {TODO} id The id to use to register the component
* @param {TODO} component The component to register
*/
IonicApp.prototype.register = function (id, component) {
if (this.components[id] && this.components[id] !== component) {
}
this.components[id] = component;
};
/**
* @private
* Unregister a known component with a key.
* @param {TODO} id The id to use to unregister
*/
IonicApp.prototype.unregister = function (id) {
delete this.components[id];
};
/**
* @private
* Get a registered component with the given type (returns the first)
* @param {Object} cls the type to search for
* @return the matching component, or undefined if none was found
*/
IonicApp.prototype.getRegisteredComponent = function (cls) {
for (var _i = 0, _a = this.components; _i < _a.length; _i++) {
var component = _a[_i];
if (component instanceof cls) {
return component;
}
}
};
/**
* @private
* Get the component for the given key.
* @param {TODO} key TODO
* @return {TODO} TODO
*/
IonicApp.prototype.getComponent = function (id) {
return this.components[id];
};
IonicApp = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [(typeof (_a = typeof config_1.Config !== 'undefined' && config_1.Config) === 'function' && _a) || Object, (typeof (_b = typeof click_block_1.ClickBlock !== 'undefined' && click_block_1.ClickBlock) === 'function' && _b) || Object, (typeof (_c = typeof core_1.NgZone !== 'undefined' && core_1.NgZone) === 'function' && _c) || Object])
], IonicApp);
return IonicApp;
var _a, _b, _c;
})();
exports.IonicApp = IonicApp;
/***/ },
/* 7 */
/***/ function(module, exports) {
module.exports = require("angular2")["platform"]["browser"];
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
/**
* @ngdoc service
* @name Config
* @module ionic
* @description
* Config allows you to set the modes of your components
*/
var platform_1 = __webpack_require__(9);
var util_1 = __webpack_require__(10);
/**
* @name Config
* @demo /docs/v2/demos/config/
* @description
* Config lets you change multiple or a single value in an apps mode configuration. Things such as tab placement, icon changes, and view animations can be set here.
*
* ```ts
* @App({
* template: ``
* config: {
* backButtonText: 'Go Back',
* iconMode: 'ios',
* modalEnter: 'modal-slide-in',
* modalLeave: 'modal-slide-out',
* tabbarPlacement: 'bottom',
* pageTransition: 'ios',
* }
* })
* ```
*
* Config can be overwritting at multiple levels, allowing deeper configuration. Taking the example from earlier, we can override any setting we want based on a platform.
* ```ts
* @App({
* template: ``
* config: {
* tabbarPlacement: 'bottom',
* platforms: {
* ios: {
* tabbarPlacement: 'top',
* }
* }
* }
* })
* ```
*
* We could also configure these values at a component level. Take `tabbarPlacement`, we can configure this as a property on our `ion-tabs`.
*
* ```html
*
*
*
* ```
*
* The property will override anything else set in the apps.
*
* The last way we could configure is through URL query strings. This is useful for testing while in the browser.
* Simply add `?ionic=` to the url.
*
* ```bash
* http://localhost:8100/?ionicTabbarPlacement=bottom
* ```
*
* Custom values can be added to config, and looked up at a later point in time.
*
* ``` javascript
* config.set('ios', 'favoriteColor', 'green');
* // from any page in your app:
* config.get('favoriteColor'); // 'green'
* ```
*
*
* A config value can come from anywhere and be anything, but there are a default set of values.
*
*
* | Config property | Default iOS Value | Default MD Value |
* |----------------------------|------------------------|---------------------------|
* | activator | highlight | ripple |
* | actionSheetEnter | action-sheet-slide-in | action-sheet-md-slide-in |
* | actionSheetLeave | action-sheet-slide-out | action-sheet-md-slide-out |
* | alertEnter | alert-pop-in | alert-md-pop-in |
* | alertLeave | alert-pop-out | alert-md-pop-out |
* | backButtonText | Back | |
* | backButtonIcon | ion-ios-arrow-back | ion-md-arrow-back |
* | iconMode | ios | md |
* | menuType | reveal | overlay |
* | modalEnter | modal-slide-in | modal-md-slide-in |
* | modalLeave | modal-slide-out | modal-md-slide-out |
* | pageTransition | ios-transition | md-transition |
* | pageTransitionDelay | 16 | 120 |
* | tabbarPlacement | bottom | top |
* | tabbarHighlight | | top |
* | tabSubPage | | true |
*
**/
var Config = (function () {
function Config(config) {
this._s = config && util_1.isObject(config) && !util_1.isArray(config) ? config : {};
this._c = {}; // cached values
}
/**
* For setting and getting multiple config values
*/
/**
* @private
* @name settings()
* @description
*/
Config.prototype.settings = function () {
var args = arguments;
switch (args.length) {
case 0:
return this._s;
case 1:
// settings({...})
this._s = args[0];
this._c = {}; // clear cache
break;
case 2:
// settings('ios', {...})
this._s.platforms = this._s.platforms || {};
this._s.platforms[args[0]] = args[1];
this._c = {}; // clear cache
break;
}
return this;
};
/**
* @name set
* @description
* Sets a single config value.
*
* @param {String} [platform] - The platform (either 'ios' or 'android') that the config value should apply to. Leaving this blank will apply the config value to all platforms.
* @param {String} [key] - The key used to look up the value at a later point in time.
* @param {String} [value] - The config value being stored.
*/
Config.prototype.set = function () {
var args = arguments;
var arg0 = args[0];
var arg1 = args[1];
switch (args.length) {
case 2:
// set('key', 'value') = set key/value pair
// arg1 = value
this._s[arg0] = arg1;
delete this._c[arg0]; // clear cache
break;
case 3:
// setting('ios', 'key', 'value') = set key/value pair for platform
// arg0 = platform
// arg1 = key
// arg2 = value
this._s.platforms = this._s.platforms || {};
this._s.platforms[arg0] = this._s.platforms[arg0] || {};
this._s.platforms[arg0][arg1] = args[2];
delete this._c[arg1]; // clear cache
break;
}
return this;
};
/**
* @name get
* @description
* Returns a single config value, given a key.
*
* @param {String} [key] - the key for the config value
*/
Config.prototype.get = function (key) {
if (!util_1.isDefined(this._c[key])) {
if (!util_1.isDefined(key)) {
throw 'config key is not defined';
}
// if the value was already set this will all be skipped
// if there was no user config then it'll check each of
// the user config's platforms, which already contains
// settings from default platform configs
var userPlatformValue = undefined;
var userDefaultValue = this._s[key];
var userPlatformModeValue = undefined;
var userDefaultModeValue = undefined;
var platformValue = undefined;
var platformModeValue = undefined;
var configObj = null;
if (this.platform) {
var queryStringValue = this.platform.query('ionic' + key.toLowerCase());
if (util_1.isDefined(queryStringValue)) {
return this._c[key] = (queryStringValue === 'true' ? true : queryStringValue === 'false' ? false : queryStringValue);
}
// check the platform settings object for this value
// loop though each of the active platforms
// array of active platforms, which also knows the hierarchy,
// with the last one the most important
var activePlatformKeys = this.platform.platforms();
// loop through all of the active platforms we're on
for (var i = 0, l = activePlatformKeys.length; i < l; i++) {
// get user defined platform values
if (this._s.platforms) {
configObj = this._s.platforms[activePlatformKeys[i]];
if (configObj) {
if (util_1.isDefined(configObj[key])) {
userPlatformValue = configObj[key];
}
configObj = Config.getModeConfig(configObj.mode);
if (configObj && util_1.isDefined(configObj[key])) {
userPlatformModeValue = configObj[key];
}
}
}
// get default platform's setting
configObj = platform_1.Platform.get(activePlatformKeys[i]);
if (configObj && configObj.settings) {
if (util_1.isDefined(configObj.settings[key])) {
// found a setting for this platform
platformValue = configObj.settings[key];
}
configObj = Config.getModeConfig(configObj.settings.mode);
if (configObj && util_1.isDefined(configObj[key])) {
// found setting for this platform's mode
platformModeValue = configObj[key];
}
}
}
}
configObj = Config.getModeConfig(this._s.mode);
if (configObj && util_1.isDefined(configObj[key])) {
userDefaultModeValue = configObj[key];
}
// cache the value
this._c[key] = util_1.isDefined(userPlatformValue) ? userPlatformValue :
util_1.isDefined(userDefaultValue) ? userDefaultValue :
util_1.isDefined(userPlatformModeValue) ? userPlatformModeValue :
util_1.isDefined(userDefaultModeValue) ? userDefaultModeValue :
util_1.isDefined(platformValue) ? platformValue :
util_1.isDefined(platformModeValue) ? platformModeValue :
null;
}
// return key's value
// either it came directly from the user config
// or it was from the users platform configs
// or it was from the default platform configs
// in that order
if (util_1.isFunction(this._c[key])) {
return this._c[key](this.platform);
}
return this._c[key];
};
/**
* @private
*/
Config.prototype.setPlatform = function (platform) {
this.platform = platform;
};
Config.setModeConfig = function (mode, config) {
modeConfigs[mode] = config;
};
Config.getModeConfig = function (mode) {
return modeConfigs[mode] || null;
};
return Config;
})();
exports.Config = Config;
var modeConfigs = {};
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
var util_1 = __webpack_require__(10);
var dom_1 = __webpack_require__(11);
/**
* @name Platform
* @description
* Platform returns the availble information about your current platform.
* Platforms in Ionic 2 are much more complex then in V1, returns not just a single platform,
* but a hierarchy of information, such as a devices OS, phone vs tablet, or mobile vs browser.
* With this information you can completely custimize your app to fit any device and platform.
*
* @usage
* ```ts
* import {Platform} 'ionic/ionic';
* export MyClass {
* constructor(platform: Platform){
* this.platform = platform;
* }
* }
* ```
* @demo /docs/v2/demos/platform/
*/
var Platform = (function () {
function Platform(platforms) {
var _this = this;
if (platforms === void 0) { platforms = []; }
this._platforms = platforms;
this._versions = {};
this._dir = null;
this._lang = null;
this._onResizes = [];
this._readyPromise = new Promise(function (res) { _this._readyResolve = res; });
}
// Methods
// **********************************************
/**
* @param {string} platformName
* @returns {bool} returns true/false based on platform you place
* @description
* Depending on the platform name, isPlatform will return true or flase
*
* ```
* import {Platform} 'ionic/ionic';
* export MyClass {
* constructor(platform: Platform){
* this.platform = platform;
* if(this.platform.is('ios'){
* // what ever you need to do for
* // if the platfomr is ios
* }
* }
* }
* ```
*/
Platform.prototype.is = function (platformName) {
return (this._platforms.indexOf(platformName) > -1);
};
/**
* @returns {array} the array of platforms
* @description
* Depending on what device you are on, `platforms` can return multiple values.
* Each possible value is a hierarchy of platforms. For example, on an iPhone,
* it would return mobile, ios, and iphone.
*
* ```
* import {Platform} 'ionic/ionic';
* export MyClass {
* constructor(platform: Platform){
* this.platform = platform;
* console.log(this.platform.platforms());
* // This will return an array of all the availble platforms
* // From if your on mobile, to mobile os, and device name
* }
* }
* ```
*/
Platform.prototype.platforms = function () {
// get the array of active platforms, which also knows the hierarchy,
// with the last one the most important
return this._platforms;
};
/**
* Returns an object containing information about the paltform
*
* ```
* import {Platform} 'ionic/ionic';
* export MyClass {
* constructor(platform: Platform){
* this.platform = platform;
* console.log(this.platform.versions());
* }
* }
* ```
* @param {string} [platformName] optional platformName
* @returns {object} An object with various platform info
*
*/
Platform.prototype.versions = function (platformName) {
if (arguments.length) {
// get a specific platform's version
return this._versions[platformName];
}
// get all the platforms that have a valid parsed version
return this._versions;
};
/**
* @private
*/
Platform.prototype.version = function () {
for (var platformName in this._versions) {
if (this._versions[platformName]) {
return this._versions[platformName];
}
}
return {};
};
/**
* Returns a promise when the platform is ready and native functionality can be called
*
* ```
* import {Platform} 'ionic/ionic';
* export MyClass {
* constructor(platform: Platform){
* this.platform = platform;
* this.platform.ready().then(() => {
* console.log('Platform ready');
* // The platform is now ready, execute any native code you want
* });
* }
* }
* ```
* @returns {promise} Returns a promsie when device ready has fired
*/
Platform.prototype.ready = function () {
return this._readyPromise;
};
/**
* @private
*/
Platform.prototype.prepareReady = function (config) {
var self = this;
function resolve() {
self._readyResolve(config);
}
if (this._engineReady) {
// the engine provide a ready promise, use this instead
this._engineReady(resolve);
}
else {
// there is no custom ready method from the engine
// use the default dom ready
dom_1.ready(resolve);
}
};
/**
* Set the app's language direction, which will update the `dir` attribute
* on the app's root `` element. We recommend the app's `index.html`
* file already has the correct `dir` attribute value set, such as
* `` or ``. This method is useful if the
* direction needs to be dynamically changed per user/session.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
* @param {string} dir Examples: `rtl`, `ltr`
*/
Platform.prototype.setDir = function (dir, updateDocument) {
this._dir = (dir || '').toLowerCase();
if (updateDocument !== false) {
document.documentElement.setAttribute('dir', dir);
}
};
/**
* Returns app's language direction.
* We recommend the app's `index.html` file already has the correct `dir`
* attribute value set, such as `` or ``.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
* @returns {string}
*/
Platform.prototype.dir = function () {
return this._dir;
};
/**
* Returns if this app is using right-to-left language direction or not.
* We recommend the app's `index.html` file already has the correct `dir`
* attribute value set, such as `` or ``.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
* @returns {boolean}
*/
Platform.prototype.isRTL = function () {
return (this._dir === 'rtl');
};
/**
* Set the app's language and optionally the country code, which will update
* the `lang` attribute on the app's root `` element.
* We recommend the app's `index.html` file already has the correct `lang`
* attribute value set, such as ``. This method is useful if
* the language needs to be dynamically changed per user/session.
* [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
* @param {string} language Examples: `en-US`, `en-GB`, `ar`, `de`, `zh`, `es-MX`
*/
Platform.prototype.setLang = function (language, updateDocument) {
this._lang = language;
if (updateDocument !== false) {
document.documentElement.setAttribute('lang', language);
}
};
/**
* Returns app's language and optional country code.
* We recommend the app's `index.html` file already has the correct `lang`
* attribute value set, such as ``.
* [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
* @returns {string}
*/
Platform.prototype.lang = function () {
return this._lang;
};
// Methods meant to be overridden by the engine
// **********************************************
// Provided NOOP methods so they do not error when
// called by engines (the browser) doesn't provide them
/**
* @private
*/
Platform.prototype.on = function () { };
/**
* @private
*/
Platform.prototype.onHardwareBackButton = function () { };
/**
* @private
*/
Platform.prototype.registerBackButtonAction = function () { };
/**
* @private
*/
Platform.prototype.exitApp = function () { };
/**
* @private
*/
Platform.prototype.fullScreen = function () { };
/**
* @private
*/
Platform.prototype.showStatusBar = function () { };
// Getter/Setter Methods
// **********************************************
/**
* @private
*/
Platform.prototype.url = function (val) {
if (arguments.length) {
this._url = val;
this._qs = util_1.getQuerystring(val);
}
return this._url;
};
/**
* @private
*/
Platform.prototype.query = function (key) {
return (this._qs || {})[key];
};
/**
* @private
*/
Platform.prototype.userAgent = function (val) {
if (arguments.length) {
this._ua = val;
}
return this._ua || '';
};
/**
* @private
*/
Platform.prototype.navigatorPlatform = function (val) {
if (arguments.length) {
this._bPlt = val;
}
return this._bPlt || '';
};
/**
* @private
*/
Platform.prototype.width = function () {
return dom_1.windowDimensions().width;
};
/**
* @private
*/
Platform.prototype.height = function () {
return dom_1.windowDimensions().height;
};
/**
* @private
*/
Platform.prototype.isPortrait = function () {
return this.width() < this.height();
};
/**
* @private
*/
Platform.prototype.isLandscape = function () {
return !this.isPortrait();
};
/**
* @private
*/
Platform.prototype.windowResize = function () {
var self = this;
clearTimeout(self._resizeTimer);
self._resizeTimer = setTimeout(function () {
dom_1.flushDimensionCache();
for (var i = 0; i < self._onResizes.length; i++) {
try {
self._onResizes[i]();
}
catch (e) {
console.error(e);
}
}
}, 250);
};
/**
* @private
*/
Platform.prototype.onResize = function (cb) {
this._onResizes.push(cb);
};
// Platform Registry
// **********************************************
/**
* @private
*/
Platform.register = function (platformConfig) {
platformRegistry[platformConfig.name] = platformConfig;
};
/**
* @private
*/
Platform.registry = function () {
return platformRegistry;
};
/**
* @private
*/
Platform.get = function (platformName) {
return platformRegistry[platformName] || {};
};
/**
* @private
*/
Platform.setDefault = function (platformName) {
platformDefault = platformName;
};
/**
* @private
*/
Platform.prototype.testQuery = function (queryValue, queryTestValue) {
var valueSplit = queryValue.toLowerCase().split(';');
return valueSplit.indexOf(queryTestValue) > -1;
};
/**
* @private
*/
Platform.prototype.testUserAgent = function (userAgentExpression) {
var rgx = new RegExp(userAgentExpression, 'i');
return rgx.test(this._ua || '');
};
/**
* @private
*/
Platform.prototype.testNavigatorPlatform = function (navigatorPlatformExpression) {
var rgx = new RegExp(navigatorPlatformExpression, 'i');
return rgx.test(this._bPlt);
};
/**
* @private
*/
Platform.prototype.matchUserAgentVersion = function (userAgentExpression) {
if (this._ua && userAgentExpression) {
var val = this._ua.match(userAgentExpression);
if (val) {
return {
major: val[1],
minor: val[2]
};
}
}
};
/**
* @private
*/
Platform.prototype.isPlatform = function (queryTestValue, userAgentExpression) {
if (!userAgentExpression) {
userAgentExpression = queryTestValue;
}
var queryValue = this.query('ionicplatform');
if (queryValue) {
return this.testQuery(queryValue, queryTestValue);
}
return this.testUserAgent(userAgentExpression);
};
/**
* @private
*/
Platform.prototype.load = function (platformOverride) {
var rootPlatformNode = null;
var engineNode = null;
var self = this;
this.platformOverride = platformOverride;
// figure out the most specific platform and active engine
var tmpPlatform = null;
for (var platformName in platformRegistry) {
tmpPlatform = this.matchPlatform(platformName);
if (tmpPlatform) {
// we found a platform match!
// check if its more specific than the one we already have
if (tmpPlatform.isEngine) {
// because it matched then this should be the active engine
// you cannot have more than one active engine
engineNode = tmpPlatform;
}
else if (!rootPlatformNode || tmpPlatform.depth > rootPlatformNode.depth) {
// only find the root node for platforms that are not engines
// set this node as the root since we either don't already
// have one, or this one is more specific that the current one
rootPlatformNode = tmpPlatform;
}
}
}
if (!rootPlatformNode) {
rootPlatformNode = new PlatformNode(platformDefault);
}
// build a Platform instance filled with the
// hierarchy of active platforms and settings
if (rootPlatformNode) {
// check if we found an engine node (cordova/node-webkit/etc)
if (engineNode) {
// add the engine to the first in the platform hierarchy
// the original rootPlatformNode now becomes a child
// of the engineNode, which is not the new root
engineNode.child(rootPlatformNode);
rootPlatformNode.parent(engineNode);
rootPlatformNode = engineNode;
// add any events which the engine would provide
// for example, Cordova provides its own ready event
var engineMethods = engineNode.methods();
engineMethods._engineReady = engineMethods.ready;
delete engineMethods.ready;
util_1.extend(this, engineMethods);
}
var platformNode = rootPlatformNode;
while (platformNode) {
insertSuperset(platformNode);
platformNode = platformNode.child();
}
// make sure the root noot is actually the root
// incase a node was inserted before the root
platformNode = rootPlatformNode.parent();
while (platformNode) {
rootPlatformNode = platformNode;
platformNode = platformNode.parent();
}
platformNode = rootPlatformNode;
while (platformNode) {
// set the array of active platforms with
// the last one in the array the most important
this._platforms.push(platformNode.name());
// get the platforms version if a version parser was provided
this._versions[platformNode.name()] = platformNode.version(this);
// go to the next platform child
platformNode = platformNode.child();
}
}
if (this._platforms.indexOf('mobile') > -1 && this._platforms.indexOf('cordova') === -1) {
this._platforms.push('mobileweb');
}
};
/**
* @private
*/
Platform.prototype.matchPlatform = function (platformName) {
// build a PlatformNode and assign config data to it
// use it's getRoot method to build up its hierarchy
// depending on which platforms match
var platformNode = new PlatformNode(platformName);
var rootNode = platformNode.getRoot(this, 0);
if (rootNode) {
rootNode.depth = 0;
var childPlatform = rootNode.child();
while (childPlatform) {
rootNode.depth++;
childPlatform = childPlatform.child();
}
}
return rootNode;
};
return Platform;
})();
exports.Platform = Platform;
function insertSuperset(platformNode) {
var supersetPlaformName = platformNode.superset();
if (supersetPlaformName) {
// add a platform in between two exist platforms
// so we can build the correct hierarchy of active platforms
var supersetPlatform = new PlatformNode(supersetPlaformName);
supersetPlatform.parent(platformNode.parent());
supersetPlatform.child(platformNode);
if (supersetPlatform.parent()) {
supersetPlatform.parent().child(supersetPlatform);
}
platformNode.parent(supersetPlatform);
}
}
var PlatformNode = (function () {
function PlatformNode(platformName) {
this.c = Platform.get(platformName);
this.isEngine = this.c.isEngine;
}
PlatformNode.prototype.name = function () {
return this.c.name;
};
PlatformNode.prototype.settings = function () {
return this.c.settings || {};
};
PlatformNode.prototype.superset = function () {
return this.c.superset;
};
PlatformNode.prototype.methods = function () {
return this.c.methods || {};
};
PlatformNode.prototype.parent = function (val) {
if (arguments.length) {
this._parent = val;
}
return this._parent;
};
PlatformNode.prototype.child = function (val) {
if (arguments.length) {
this._child = val;
}
return this._child;
};
PlatformNode.prototype.isMatch = function (p) {
if (p.platformOverride && !this.isEngine) {
return (p.platformOverride === this.c.name);
}
else if (!this.c.isMatch) {
return false;
}
return this.c.isMatch(p);
};
PlatformNode.prototype.version = function (p) {
if (this.c.versionParser) {
var v = this.c.versionParser(p);
if (v) {
var str = v.major + '.' + v.minor;
return {
str: str,
num: parseFloat(str),
major: parseInt(v.major, 10),
minor: parseInt(v.minor, 10)
};
}
}
};
PlatformNode.prototype.getRoot = function (p) {
if (this.isMatch(p)) {
var parents = this.getSubsetParents(this.name());
if (!parents.length) {
return this;
}
var platform = null;
var rootPlatform = null;
for (var i = 0; i < parents.length; i++) {
platform = new PlatformNode(parents[i]);
platform.child(this);
rootPlatform = platform.getRoot(p);
if (rootPlatform) {
this.parent(platform);
return rootPlatform;
}
}
}
return null;
};
PlatformNode.prototype.getSubsetParents = function (subsetPlatformName) {
var platformRegistry = Platform.registry();
var parentPlatformNames = [];
var platform = null;
for (var platformName in platformRegistry) {
platform = platformRegistry[platformName];
if (platform.subsets && platform.subsets.indexOf(subsetPlatformName) > -1) {
parentPlatformNames.push(platformName);
}
}
return parentPlatformNames;
};
return PlatformNode;
})();
var platformRegistry = {};
var platformDefault = null;
/***/ },
/* 10 */
/***/ function(module, exports) {
// Simple noop function
function noop() { }
exports.noop = noop;
;
/**
* Given a min and max, restrict the given number
* to the range.
* @param min the minimum
* @param n the value
* @param max the maximum
*/
function clamp(min, n, max) {
return Math.max(min, Math.min(n, max));
}
exports.clamp = clamp;
/**
* Extend the destination with an arbitrary number of other objects.
* @param dst the destination
* @param ... the param objects
*/
function extend(dst) {
return _baseExtend(dst, [].slice.call(arguments, 1), false);
}
exports.extend = extend;
/**
* Do a deep extend (merge).
* @param dst the destination
* @param ... the param objects
*/
function merge(dst) {
return _baseExtend(dst, [].slice.call(arguments, 1), true);
}
exports.merge = merge;
function _baseExtend(dst, objs, deep) {
for (var i = 0, ii = objs.length; i < ii; ++i) {
var obj = objs[i];
if (!obj || !exports.isObject(obj) && !exports.isFunction(obj))
continue;
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
var src = obj[key];
if (deep && exports.isObject(src)) {
if (!exports.isObject(dst[key]))
dst[key] = exports.isArray(src) ? [] : {};
_baseExtend(dst[key], [src], true);
}
else {
dst[key] = src;
}
}
}
return dst;
}
function debounce(func, wait, immediate) {
var timeout, args, context, timestamp, result;
return function () {
context = this;
args = arguments;
timestamp = new Date();
var later = function () {
var last = (new Date()) - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
}
else {
timeout = null;
if (!immediate)
result = func.apply(context, args);
}
};
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow)
result = func.apply(context, args);
return result;
};
}
exports.debounce = debounce;
/**
* Apply default arguments if they don't exist in
* the first object.
* @param the destination to apply defaults to.
*/
function defaults(dest) {
for (var i = arguments.length - 1; i >= 1; i--) {
var source = arguments[i] || {};
for (var key in source) {
if (source.hasOwnProperty(key) && !dest.hasOwnProperty(key)) {
dest[key] = source[key];
}
}
}
return dest;
}
exports.defaults = defaults;
exports.isBoolean = function (val) { return typeof val === 'boolean'; };
exports.isString = function (val) { return typeof val === 'string'; };
exports.isNumber = function (val) { return typeof val === 'number'; };
exports.isFunction = function (val) { return typeof val === 'function'; };
exports.isDefined = function (val) { return typeof val !== 'undefined'; };
exports.isUndefined = function (val) { return typeof val === 'undefined'; };
exports.isBlank = function (val) { return val === undefined || val === null; };
exports.isObject = function (val) { return typeof val === 'object'; };
exports.isArray = Array.isArray;
exports.isTrueProperty = function (val) { return typeof val !== 'undefined' && val !== "false"; };
/**
* Convert a string in the format thisIsAString to a slug format this-is-a-string
*/
function pascalCaseToDashCase(str) {
if (str === void 0) { str = ''; }
return str.charAt(0).toLowerCase() + str.substring(1).replace(/[A-Z]/g, function (match) {
return '-' + match.toLowerCase();
});
}
exports.pascalCaseToDashCase = pascalCaseToDashCase;
var uid = 0;
function nextUid() {
return ++uid;
}
exports.nextUid = nextUid;
exports.array = {
find: function (arr, cb) {
for (var i = 0, ii = arr.length; i < ii; i++) {
if (cb(arr[i], i))
return arr[i];
}
},
remove: function (arr, itemOrIndex) {
var index = -1;
if (exports.isNumber(itemOrIndex)) {
index = itemOrIndex;
}
else {
index = arr.indexOf(itemOrIndex);
}
if (index < 0) {
return false;
}
arr.splice(index, 1);
return true;
}
};
/**
* Grab the query string param value for the given key.
* @param key the key to look for
*/
function getQuerystring(url, key) {
var queryParams = {};
if (url) {
var startIndex = url.indexOf('?');
if (startIndex !== -1) {
var queries = url.slice(startIndex + 1).split('&');
queries.forEach(function (param) {
var split = param.split('=');
queryParams[split[0].toLowerCase()] = split[1].split('#')[0];
});
}
if (key) {
return queryParams[key] || '';
}
}
return queryParams;
}
exports.getQuerystring = getQuerystring;
/**
* Throttle the given fun, only allowing it to be
* called at most every `wait` ms.
*/
function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function () {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
};
return function () {
var now = Date.now();
if (!previous && options.leading === false)
previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
}
else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
exports.throttle = throttle;
/***/ },
/* 11 */
/***/ function(module, exports) {
// requestAnimationFrame is polyfilled for old Android
// within the web-animations polyfill
exports.raf = window.requestAnimationFrame;
function rafFrames(framesToWait, callback) {
framesToWait = Math.ceil(framesToWait);
if (framesToWait < 2) {
exports.raf(callback);
}
else {
setTimeout(function () {
exports.raf(callback);
}, (framesToWait - 1) * 17);
}
}
exports.rafFrames = rafFrames;
exports.CSS = {};
(function () {
// transform
var i, keys = ['webkitTransform', 'transform', '-webkit-transform', 'webkit-transform',
'-moz-transform', 'moz-transform', 'MozTransform', 'mozTransform', 'msTransform'];
for (i = 0; i < keys.length; i++) {
if (document.documentElement.style[keys[i]] !== undefined) {
exports.CSS.transform = keys[i];
break;
}
}
// transition
keys = ['webkitTransition', 'mozTransition', 'msTransition', 'transition'];
for (i = 0; i < keys.length; i++) {
if (document.documentElement.style[keys[i]] !== undefined) {
exports.CSS.transition = keys[i];
break;
}
}
// The only prefix we care about is webkit for transitions.
var isWebkit = exports.CSS.transition.indexOf('webkit') > -1;
exports.CSS.prefix = isWebkit ? '-webkit-' : '';
// transition duration
exports.CSS.transitionDuration = (isWebkit ? '-webkit-' : '') + 'transition-duration';
// To be sure transitionend works everywhere, include *both* the webkit and non-webkit events
exports.CSS.transitionEnd = (isWebkit ? 'webkitTransitionEnd ' : '') + 'transitionend';
})();
if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
exports.CSS.animation = 'WebkitAnimation';
exports.CSS.animationStart = 'webkitAnimationStart animationstart';
exports.CSS.animationEnd = 'webkitAnimationEnd animationend';
}
else {
exports.CSS.animation = 'animation';
exports.CSS.animationStart = 'animationstart';
exports.CSS.animationEnd = 'animationend';
}
function transitionEnd(el) {
return cssPromise(el, exports.CSS.transitionEnd);
}
exports.transitionEnd = transitionEnd;
function animationStart(el, animationName) {
return cssPromise(el, exports.CSS.animationStart, animationName);
}
exports.animationStart = animationStart;
function animationEnd(el, animationName) {
return cssPromise(el, exports.CSS.animationEnd, animationName);
}
exports.animationEnd = animationEnd;
function cssPromise(el, eventNames, animationName) {
return new Promise(function (resolve) {
eventNames.split(' ').forEach(function (eventName) {
el.addEventListener(eventName, onEvent);
});
function onEvent(ev) {
if (ev.animationName && animationName) {
// do not resolve if a bubbled up ev.animationName
// is not the same as the passed in animationName arg
if (ev.animationName !== animationName) {
return;
}
}
else if (ev.target !== el) {
// do not resolve if the event's target element is not
// the same as the element the listener was added to
return;
}
ev.stopPropagation();
eventNames.split(' ').forEach(function (eventName) {
el.removeEventListener(eventName, onEvent);
});
resolve(ev);
}
});
}
function ready(callback) {
var promise = null;
if (!callback) {
// a callback wasn't provided, so let's return a promise instead
promise = new Promise(function (resolve) { callback = resolve; });
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
callback();
}
else {
function completed() {
document.removeEventListener('DOMContentLoaded', completed, false);
window.removeEventListener('load', completed, false);
callback();
}
document.addEventListener('DOMContentLoaded', completed, false);
window.addEventListener('load', completed, false);
}
return promise;
}
exports.ready = ready;
function windowLoad(callback) {
var promise = null;
if (!callback) {
// a callback wasn't provided, so let's return a promise instead
promise = new Promise(function (resolve) { callback = resolve; });
}
if (document.readyState === 'complete') {
callback();
}
else {
function completed() {
window.removeEventListener('load', completed, false);
callback();
}
window.addEventListener('load', completed, false);
}
return promise;
}
exports.windowLoad = windowLoad;
function pointerCoord(ev) {
// get coordinates for either a mouse click
// or a touch depending on the given event
var c = { x: 0, y: 0 };
if (ev) {
var touches = ev.touches && ev.touches.length ? ev.touches : [ev];
var e = (ev.changedTouches && ev.changedTouches[0]) || touches[0];
if (e) {
c.x = e.clientX || e.pageX || 0;
c.y = e.clientY || e.pageY || 0;
}
}
return c;
}
exports.pointerCoord = pointerCoord;
function hasPointerMoved(threshold, startCoord, endCoord) {
return startCoord && endCoord &&
(Math.abs(startCoord.x - endCoord.x) > threshold || Math.abs(startCoord.y - endCoord.y) > threshold);
}
exports.hasPointerMoved = hasPointerMoved;
function isActive(ele) {
return !!(ele && (document.activeElement === ele));
}
exports.isActive = isActive;
function hasFocus(ele) {
return isActive(ele) && (ele.parentElement.querySelector(':focus') === ele);
}
exports.hasFocus = hasFocus;
function isTextInput(ele) {
return !!ele &&
(ele.tagName == 'TEXTAREA' ||
ele.contentEditable === 'true' ||
(ele.tagName == 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type)));
}
exports.isTextInput = isTextInput;
function hasFocusedTextInput() {
var ele = document.activeElement;
if (isTextInput(ele)) {
return (ele.parentElement.querySelector(':focus') === ele);
}
return false;
}
exports.hasFocusedTextInput = hasFocusedTextInput;
var matchesFn;
['matches', 'webkitMatchesSelector', 'mozMatchesSelector', 'msMatchesSelector'].some(function (fn) {
if (typeof document.documentElement[fn] == 'function') {
matchesFn = fn;
}
});
function closest(ele, selector, checkSelf) {
if (ele && matchesFn) {
// traverse parents
ele = (checkSelf ? ele : ele.parentElement);
while (ele !== null) {
if (ele[matchesFn](selector)) {
return ele;
}
ele = ele.parentElement;
}
}
return null;
}
exports.closest = closest;
function removeElement(ele) {
ele && ele.parentNode && ele.parentNode.removeChild(ele);
}
exports.removeElement = removeElement;
/**
* Get the element offsetWidth and offsetHeight. Values are cached
* to reduce DOM reads. Cache is cleared on a window resize.
* @param {TODO} ele TODO
*/
function getDimensions(ion, ele) {
if (!ion._dimId) {
ion._dimId = ++dimensionIds;
if (ion._dimId % 1000 === 0) {
// periodically flush dimensions
flushDimensionCache();
}
}
var dimensions = dimensionCache[ion._dimId];
if (!dimensions) {
var ele_1 = ion.getNativeElement();
// make sure we got good values before caching
if (ele_1.offsetWidth && ele_1.offsetHeight) {
dimensions = dimensionCache[ion._dimId] = {
width: ele_1.offsetWidth,
height: ele_1.offsetHeight,
left: ele_1.offsetLeft,
top: ele_1.offsetTop
};
}
else {
// do not cache bad values
return { width: 0, height: 0, left: 0, top: 0 };
}
}
return dimensions;
}
exports.getDimensions = getDimensions;
function windowDimensions() {
if (!dimensionCache.win) {
// make sure we got good values before caching
if (window.innerWidth && window.innerHeight) {
dimensionCache.win = {
width: window.innerWidth,
height: window.innerHeight
};
}
else {
// do not cache bad values
return { width: 0, height: 0 };
}
}
return dimensionCache.win;
}
exports.windowDimensions = windowDimensions;
function flushDimensionCache() {
dimensionCache = {};
}
exports.flushDimensionCache = flushDimensionCache;
var dimensionCache = {};
var dimensionIds = 0;
function isStaticPositioned(element) {
return (element.style.position || 'static') === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
function parentOffsetEl(element) {
var offsetParent = element.offsetParent || document;
while (offsetParent && offsetParent !== document && isStaticPositioned(offsetParent)) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || document;
}
exports.parentOffsetEl = parentOffsetEl;
;
/**
* Get the current coordinates of the element, relative to the offset parent.
* Read-only equivalent of [jQuery's position function](http://api.jquery.com/position/).
* @param {element} element The element to get the position of.
* @returns {object} Returns an object containing the properties top, left, width and height.
*/
function position(element) {
var elBCR = offset(element);
var offsetParentBCR = { top: 0, left: 0 };
var offsetParentEl = parentOffsetEl(element);
if (offsetParentEl != document) {
offsetParentBCR = offset(offsetParentEl);
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
var boundingClientRect = element.getBoundingClientRect();
return {
width: boundingClientRect.width || element.offsetWidth,
height: boundingClientRect.height || element.offsetHeight,
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
}
exports.position = position;
/**
* Get the current coordinates of the element, relative to the document.
* Read-only equivalent of [jQuery's offset function](http://api.jquery.com/offset/).
* @param {element} element The element to get the offset of.
* @returns {object} Returns an object containing the properties top, left, width and height.
*/
function offset(element) {
var boundingClientRect = element.getBoundingClientRect();
return {
width: boundingClientRect.width || element.offsetWidth,
height: boundingClientRect.height || element.offsetHeight,
top: boundingClientRect.top + (window.pageYOffset || document.documentElement.scrollTop),
left: boundingClientRect.left + (window.pageXOffset || document.documentElement.scrollLeft)
};
}
exports.offset = offset;
/***/ },
/* 12 */
/***/ function(module, exports) {
var CSS_CLICK_BLOCK = 'click-block-active';
var DEFAULT_EXPIRE = 330;
var cbEle, fallbackTimerId;
var isShowing = false;
/**
* @private
*/
var ClickBlock = (function () {
function ClickBlock() {
}
ClickBlock.prototype.enable = function () {
cbEle = document.createElement('click-block');
document.body.appendChild(cbEle);
cbEle.addEventListener('touchmove', function (ev) {
ev.preventDefault();
ev.stopPropagation();
});
this._enabled = true;
};
ClickBlock.prototype.show = function (shouldShow, expire) {
if (this._enabled) {
if (shouldShow) {
show(expire);
}
else {
hide();
}
}
};
return ClickBlock;
})();
exports.ClickBlock = ClickBlock;
function show(expire) {
clearTimeout(fallbackTimerId);
fallbackTimerId = setTimeout(hide, expire || DEFAULT_EXPIRE);
if (!isShowing) {
cbEle.classList.add(CSS_CLICK_BLOCK);
isShowing = true;
}
}
function hide() {
clearTimeout(fallbackTimerId);
if (isShowing) {
cbEle.classList.remove(CSS_CLICK_BLOCK);
isShowing = false;
}
}
/***/ },
/* 13 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
/**
* The Input component is used to focus text input elements.
*
* @usage
* ```html
*
* Name
*
*
* ```
*/
var Form = (function () {
function Form() {
this._inputs = [];
this._ids = -1;
this._focused = null;
this.focusCtrl(document);
}
Form.prototype.register = function (input) {
this._inputs.push(input);
};
Form.prototype.deregister = function (input) {
var index = this._inputs.indexOf(input);
if (index > -1) {
this._inputs.splice(index, 1);
}
if (input === this._focused) {
this._focused = null;
}
};
Form.prototype.focusCtrl = function (document) {
// raw DOM fun
var focusCtrl = document.createElement('focus-ctrl');
focusCtrl.setAttribute('aria-hidden', true);
this._blur = document.createElement('button');
this._blur.tabIndex = -1;
focusCtrl.appendChild(this._blur);
document.body.appendChild(focusCtrl);
};
Form.prototype.focusOut = function () {
console.debug('focusOut');
document.activeElement && document.activeElement.blur();
this._blur.focus();
};
Form.prototype.setAsFocused = function (input) {
this._focused = input;
};
/**
* Focuses the next input element, if it exists.
*/
Form.prototype.focusNext = function (currentInput) {
console.debug('focusNext');
var index = this._inputs.indexOf(currentInput);
if (index > -1 && (index + 1) < this._inputs.length) {
var nextInput = this._inputs[index + 1];
if (nextInput !== this._focused) {
return nextInput.initFocus();
}
}
index = this._inputs.indexOf(this._focused);
if (index > 0) {
var previousInput = this._inputs[index - 1];
if (previousInput) {
previousInput.initFocus();
}
}
};
Form.prototype.nextId = function () {
return ++this._ids;
};
Form = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [])
], Form);
return Form;
})();
exports.Form = Form;
/***/ },
/* 14 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
var config_1 = __webpack_require__(8);
var form_1 = __webpack_require__(13);
var dom_1 = __webpack_require__(11);
/**
* @name Keyboard
* @description
* The `Keyboard` class allows you to work with the keyboard events provide by the Ionic keyboard plugin.
*
* @usage
* ```ts
* export class MyClass{
* constructor(keyboard: Keyboard){
* this.keyboard = keyboard;
* }
* }
*
* ```
*/
var Keyboard = (function () {
function Keyboard(config, form, zone) {
var _this = this;
this.form = form;
this.zone = zone;
zone.runOutsideAngular(function () {
_this.focusOutline(config.get('focusOutline'), document);
});
}
/**
* Chech to see if the keyboard is open or not.
*
* ```ts
* export class MyClass{
* constructor(keyboard: Keyboard){
* this.keyboard = keyboard;
* }
* keyboardCheck(){
* setTimeout(() => console.log('is the keyboard open ', this.keyboard.isOpen()));
* }
* }
*
* ```
*
* @return {Bool} returns a true or flase value if the keyboard is open or not
*/
Keyboard.prototype.isOpen = function () {
return dom_1.hasFocusedTextInput();
};
/**
* When the keyboard is closed, call any methods you want
*
* ```ts
* export class MyClass{
* constructor(keyboard: Keyboard){
* this.keyboard = keyboard;
* this.keyboard.onClose(this.closeCallback);
* }
* closeCallback(){
* // call what ever functionality you want on keyboard close
* console.log('Closing time");
* }
* }
*
* ```
* @param {Function} callback method you want to call when the keyboard has been closed
* @return {Function} returns a callback that gets fired when the keyboard is closed
*/
Keyboard.prototype.onClose = function (callback, pollingInternval) {
if (pollingInternval === void 0) { pollingInternval = KEYBOARD_CLOSE_POLLING; }
console.debug('keyboard onClose');
var self = this;
var checks = 0;
var promise = null;
if (!callback) {
// a callback wasn't provided, so let's return a promise instead
promise = new Promise(function (resolve) { callback = resolve; });
}
self.zone.runOutsideAngular(function () {
function checkKeyboard() {
console.debug('keyboard isOpen', self.isOpen(), checks);
if (!self.isOpen() || checks > 100) {
dom_1.rafFrames(30, function () {
self.zone.run(function () {
console.debug('keyboard closed');
callback();
});
});
}
else {
setTimeout(checkKeyboard, pollingInternval);
}
checks++;
}
setTimeout(checkKeyboard, pollingInternval);
});
return promise;
};
/**
* Progamatically close they keyboard
*
*/
Keyboard.prototype.close = function () {
var _this = this;
console.debug('keyboard close()');
dom_1.raf(function () {
if (dom_1.hasFocusedTextInput()) {
// only focus out when a text input has focus
_this.form.focusOut();
}
});
};
/**
* @private
*/
Keyboard.prototype.focusOutline = function (setting, document) {
/* Focus Outline
* --------------------------------------------------
* By default, when a keydown event happens from a tab key, then
* the 'focus-outline' css class is added to the body element
* so focusable elements have an outline. On a mousedown or
* touchstart event, then the 'focus-outline' css class is removed.
*
* Config default overrides:
* focusOutline: true - Always add the focus-outline
* focusOutline: false - Do not add the focus-outline
*/
var self = this;
var isKeyInputEnabled = false;
function cssClass() {
dom_1.raf(function () {
document.body.classList[isKeyInputEnabled ? 'add' : 'remove']('focus-outline');
});
}
if (setting === true) {
isKeyInputEnabled = true;
return cssClass();
}
else if (setting === false) {
return;
}
// default is to add the focus-outline when the tab key is used
function keyDown(ev) {
if (!isKeyInputEnabled && ev.keyCode == 9) {
isKeyInputEnabled = true;
enableKeyInput();
}
}
function pointerDown() {
isKeyInputEnabled = false;
enableKeyInput();
}
function enableKeyInput() {
cssClass();
self.zone.runOutsideAngular(function () {
document.removeEventListener('mousedown', pointerDown);
document.removeEventListener('touchstart', pointerDown);
if (isKeyInputEnabled) {
document.addEventListener('mousedown', pointerDown);
document.addEventListener('touchstart', pointerDown);
}
});
}
document.addEventListener('keydown', keyDown);
};
Keyboard = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [(typeof (_a = typeof config_1.Config !== 'undefined' && config_1.Config) === 'function' && _a) || Object, (typeof (_b = typeof form_1.Form !== 'undefined' && form_1.Form) === 'function' && _b) || Object, (typeof (_c = typeof core_1.NgZone !== 'undefined' && core_1.NgZone) === 'function' && _c) || Object])
], Keyboard);
return Keyboard;
var _a, _b, _c;
})();
exports.Keyboard = Keyboard;
var KEYBOARD_CLOSE_POLLING = 150;
/***/ },
/* 15 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
/**
* Events is a pub/sub style event system for sending and responding to application-level
* events across your app.
* @usage
* ```ts
* // first page (publish an event when a user is created)
* function createUser(user) {
* console.log('User created!')
* events.publish('user:created', user);
* }
*
* // second page (listen for the user created event)
* events.subscribe('user:created', (user) => {
* console.log('Welcome', user);
* });
*
* ```
*/
var Events = (function () {
function Events() {
this.channels = [];
}
/**
* Subscribe to an event topic. Events that get posted to that topic
* will trigger the provided handler.
*
* @param topic the topic to subscribe to
* @param handler the event handler
*/
Events.prototype.subscribe = function (topic) {
var _this = this;
var handlers = [];
for (var _i = 1; _i < arguments.length; _i++) {
handlers[_i - 1] = arguments[_i];
}
if (!this.channels[topic]) {
this.channels[topic] = [];
}
handlers.forEach(function (handler) {
_this.channels[topic].push(handler);
});
};
/**
* Unsubscribe from the given topic. Your handler will
* no longer receive events published to this topic.
*
* @param topic the topic to unsubscribe from
* @param handler the event handler
*
* @return true if a handler was removed
*/
Events.prototype.unsubscribe = function (topic, handler) {
var t = this.channels[topic];
if (!t) {
// Wasn't found, wasn't removed
return false;
}
if (!handler) {
// Remove all handlers for this topic
delete this.channels[topic];
return true;
}
// We need to find and remove a specific handler
var i = t.indexOf(handler);
if (i < 0) {
// Wasn't found, wasn't removed
return false;
}
t.splice(i, 1);
// If the channel is empty now, remove it from the channel map
if (!t.length) {
delete this.channels[topic];
}
return true;
};
/**
* Publish an event to the given topic.
*
* @param topic the topic to publish to
* @param eventData the data to send as the event
*/
Events.prototype.publish = function (topic) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var t = this.channels[topic];
if (!t) {
return null;
}
var responses = [];
t.forEach(function (handler) {
responses.push(handler(args));
});
return responses;
};
Events = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [])
], Events);
return Events;
})();
exports.Events = Events;
/***/ },
/* 16 */
/***/ function(module, exports) {
/**
* @private
* Map of possible pages that can be navigated to using an Ionic NavController
*/
var NavRegistry = (function () {
function NavRegistry(pages) {
if (pages === void 0) { pages = []; }
this._pages = new Map(pages.map(function (page) { return [page.name, page]; }));
}
NavRegistry.prototype.get = function (pageName) {
return this._pages.get(pageName);
};
NavRegistry.prototype.set = function (page) {
this._pages.set(page.name, page);
};
return NavRegistry;
})();
exports.NavRegistry = NavRegistry;
/***/ },
/* 17 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
/**
* @private
* Provide multi-language and i18n support in your app. Translate works by
* mapping full strings to language translated ones. That means that you don't need
* to provide strings for your default language, just new languages.
*
* @usage
* ```js
* Translate.translations({
* 'de': {
* 'Welcome to MyApp': 'Willkommen auf'
* }
* })
*
* Changing the default language:
*
* Translate.setLanguage('de');
* ```
*
* Usage in a template:
*
* ```js
* {{ 'Welcome to MyApp' | translate }}
* ```
*/
var Translate = (function () {
function Translate() {
this._transMap = {};
}
Translate.prototype.translations = function (lang, map) {
this._transMap[lang] = map;
};
Translate.prototype.setLanguage = function (lang) {
this._language = lang;
};
Translate.prototype.getTranslations = function (lang) {
return this._transMap[lang];
};
Translate.prototype.translate = function (key, lang) {
// If the language isn't specified and we have no overridden one, return the string passed.
if (!lang && !this._language) {
return key;
}
var setLanguage = lang || this._language;
var map = this.getTranslations(setLanguage);
if (!map) {
console.warn('I18N: No translation for key', key, 'using language', setLanguage);
return '';
}
return this._getTranslation(map, key);
};
Translate.prototype._getTranslation = function (map, key) {
return map && map[key] || '';
};
Translate = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [])
], Translate);
return Translate;
})();
exports.Translate = Translate;
/***/ },
/* 18 */
/***/ function(module, exports) {
var FeatureDetect = (function () {
function FeatureDetect() {
}
FeatureDetect.prototype.run = function (window, document) {
this._results = {};
for (var name in featureDetects) {
this._results[name] = featureDetects[name](window, document, document.body);
}
};
FeatureDetect.prototype.has = function (featureName) {
return !!this._results[featureName];
};
FeatureDetect.add = function (name, fn) {
featureDetects[name] = fn;
};
return FeatureDetect;
})();
exports.FeatureDetect = FeatureDetect;
var featureDetects = {};
// FeatureDetect.add('sticky', function(window, document) {
// // css position sticky
// let ele = document.createElement('div');
// ele.style.cssText = 'position:-webkit-sticky;position:sticky';
// return ele.style.position.indexOf('sticky') > -1;
// });
FeatureDetect.add('hairlines', function (window, document, body) {
/**
* Hairline Shim
* Add the "hairline" CSS class name to the body tag
* if the browser supports subpixels.
*/
var canDo = false;
if (window.devicePixelRatio >= 2) {
var hairlineEle = document.createElement('div');
hairlineEle.style.border = '.5px solid transparent';
body.appendChild(hairlineEle);
if (hairlineEle.offsetHeight === 1) {
body.classList.add('hairlines');
canDo = true;
}
body.removeChild(hairlineEle);
}
return canDo;
});
/***/ },
/* 19 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
var app_1 = __webpack_require__(6);
var config_1 = __webpack_require__(8);
var dom_1 = __webpack_require__(11);
var activator_1 = __webpack_require__(20);
var ripple_1 = __webpack_require__(21);
/**
* @private
*/
var TapClick = (function () {
function TapClick(app, config, zone) {
var self = this;
self.app = app;
self.zone = zone;
self.lastTouch = 0;
self.disableClick = 0;
self.lastActivated = 0;
if (config.get('activator') == 'ripple') {
self.activator = new ripple_1.RippleActivator(app, config, zone);
}
else if (config.get('activator') == 'highlight') {
self.activator = new activator_1.Activator(app, config, zone);
}
self.usePolyfill = (config.get('tapPolyfill') === true);
zone.runOutsideAngular(function () {
addListener('click', self.click.bind(self), true);
addListener('touchstart', self.touchStart.bind(self));
addListener('touchend', self.touchEnd.bind(self));
addListener('touchcancel', self.pointerCancel.bind(self));
addListener('mousedown', self.mouseDown.bind(self), true);
addListener('mouseup', self.mouseUp.bind(self), true);
});
self.pointerMove = function (ev) {
if (dom_1.hasPointerMoved(POINTER_MOVE_UNTIL_CANCEL, self.startCoord, dom_1.pointerCoord(ev))) {
self.pointerCancel(ev);
}
};
}
TapClick.prototype.touchStart = function (ev) {
this.lastTouch = Date.now();
this.pointerStart(ev);
};
TapClick.prototype.touchEnd = function (ev) {
this.lastTouch = Date.now();
if (this.usePolyfill && this.startCoord && this.app.isEnabled()) {
// only dispatch mouse click events from a touchend event
// when tapPolyfill config is true, and the startCoordand endCoord
// are not too far off from each other
var endCoord = dom_1.pointerCoord(ev);
if (!dom_1.hasPointerMoved(POINTER_TOLERANCE, this.startCoord, endCoord)) {
// prevent native mouse click events for XX amount of time
this.disableClick = this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT;
if (this.app.isScrolling()) {
// do not fire off a click event while the app was scrolling
console.debug('click from touch prevented by scrolling ' + Date.now());
}
else {
// dispatch a mouse click event
console.debug('create click from touch ' + Date.now());
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
clickEvent.isIonicTap = true;
ev.target.dispatchEvent(clickEvent);
}
}
}
this.pointerEnd(ev);
};
TapClick.prototype.mouseDown = function (ev) {
if (this.isDisabledNativeClick()) {
console.debug('mouseDown prevent ' + ev.target.tagName + ' ' + Date.now());
// does not prevent default on purpose
// so native blur events from inputs can happen
ev.stopPropagation();
}
else if (this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT < Date.now()) {
this.pointerStart(ev);
}
};
TapClick.prototype.mouseUp = function (ev) {
if (this.isDisabledNativeClick()) {
console.debug('mouseUp prevent ' + ev.target.tagName + ' ' + Date.now());
ev.preventDefault();
ev.stopPropagation();
}
if (this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT < Date.now()) {
this.pointerEnd(ev);
}
};
TapClick.prototype.pointerStart = function (ev) {
var activatableEle = getActivatableTarget(ev.target);
if (activatableEle) {
this.startCoord = dom_1.pointerCoord(ev);
var now = Date.now();
if (this.lastActivated + 150 < now) {
this.activator && this.activator.downAction(ev, activatableEle, this.startCoord.x, this.startCoord.y);
this.lastActivated = now;
}
this.moveListeners(true);
}
else {
this.startCoord = null;
}
};
TapClick.prototype.pointerEnd = function (ev) {
this.moveListeners(false);
this.activator && this.activator.upAction();
};
TapClick.prototype.pointerCancel = function (ev) {
console.debug('pointerCancel from ' + ev.type + ' ' + Date.now());
this.activator && this.activator.clearState();
this.moveListeners(false);
};
TapClick.prototype.moveListeners = function (shouldAdd) {
removeListener(this.usePolyfill ? 'touchmove' : 'mousemove', this.pointerMove);
//this.zone.runOutsideAngular(() => {
if (shouldAdd) {
addListener(this.usePolyfill ? 'touchmove' : 'mousemove', this.pointerMove);
}
else {
}
//});
};
TapClick.prototype.click = function (ev) {
var preventReason = null;
if (!this.app.isEnabled()) {
preventReason = 'appDisabled';
}
else if (!ev.isIonicTap && this.isDisabledNativeClick()) {
preventReason = 'nativeClick';
}
if (preventReason !== null) {
console.debug('click prevent ' + preventReason + ' ' + Date.now());
ev.preventDefault();
ev.stopPropagation();
}
};
TapClick.prototype.isDisabledNativeClick = function () {
return this.disableClick > Date.now();
};
TapClick = __decorate([
core_1.Injectable(),
__metadata('design:paramtypes', [(typeof (_a = typeof app_1.IonicApp !== 'undefined' && app_1.IonicApp) === 'function' && _a) || Object, (typeof (_b = typeof config_1.Config !== 'undefined' && config_1.Config) === 'function' && _b) || Object, (typeof (_c = typeof core_1.NgZone !== 'undefined' && core_1.NgZone) === 'function' && _c) || Object])
], TapClick);
return TapClick;
var _a, _b, _c;
})();
exports.TapClick = TapClick;
function getActivatableTarget(ele) {
var targetEle = ele;
for (var x = 0; x < 4; x++) {
if (!targetEle)
break;
if (isActivatable(targetEle))
return targetEle;
targetEle = targetEle.parentElement;
}
return null;
}
/**
* @private
*/
function isActivatable(ele) {
if (ACTIVATABLE_ELEMENTS.test(ele.tagName)) {
return true;
}
var attributes = ele.attributes;
for (var i = 0, l = attributes.length; i < l; i++) {
if (ACTIVATABLE_ATTRIBUTES.test(attributes[i].name)) {
return true;
}
}
return false;
}
exports.isActivatable = isActivatable;
function addListener(type, listener, useCapture) {
document.addEventListener(type, listener, useCapture);
}
function removeListener(type, listener) {
document.removeEventListener(type, listener);
}
var ACTIVATABLE_ELEMENTS = /^(A|BUTTON)$/;
var ACTIVATABLE_ATTRIBUTES = /tappable|button/i;
var POINTER_TOLERANCE = 4;
var POINTER_MOVE_UNTIL_CANCEL = 10;
var DISABLE_NATIVE_CLICK_AMOUNT = 2500;
/***/ },
/* 20 */
/***/ function(module, exports, __webpack_require__) {
var dom_1 = __webpack_require__(11);
var Activator = (function () {
function Activator(app, config, zone) {
this.app = app;
this.zone = zone;
this.queue = [];
this.active = [];
this.clearStateDefers = 5;
this.clearAttempt = 0;
this.activatedClass = config.get('activatedClass') || 'activated';
this.x = 0;
this.y = 0;
}
Activator.prototype.downAction = function (ev, activatableEle, pointerX, pointerY, callback) {
// the user just pressed down
var self = this;
if (self.disableActivated(ev))
return false;
// remember where they pressed
self.x = pointerX;
self.y = pointerY;
// queue to have this element activated
self.queue.push(activatableEle);
function activateCss() {
var activatableEle;
for (var i = 0; i < self.queue.length; i++) {
activatableEle = self.queue[i];
if (activatableEle && activatableEle.parentNode) {
self.active.push(activatableEle);
activatableEle.classList.add(self.activatedClass);
}
}
self.queue = [];
}
this.zone.runOutsideAngular(function () {
dom_1.rafFrames(2, activateCss);
});
return true;
};
Activator.prototype.upAction = function () {
// the user was pressing down, then just let up
var self = this;
function activateUp() {
self.clearState();
}
this.zone.runOutsideAngular(function () {
dom_1.rafFrames(self.clearStateDefers, activateUp);
});
};
Activator.prototype.clearState = function () {
// all states should return to normal
var _this = this;
if (!this.app.isEnabled()) {
// the app is actively disabled, so don't bother deactivating anything.
// this makes it easier on the GPU so it doesn't have to redraw any
// buttons during a transition. This will retry in XX milliseconds.
setTimeout(function () {
_this.clearState();
}, 600);
}
else {
// not actively transitioning, good to deactivate any elements
this.deactivate();
}
};
Activator.prototype.deactivate = function () {
// remove the active class from all active elements
var self = this;
self.queue = [];
function deactivate() {
for (var i = 0; i < self.active.length; i++) {
self.active[i].classList.remove(self.activatedClass);
}
self.active = [];
}
dom_1.rafFrames(2, deactivate);
};
Activator.prototype.disableActivated = function (ev) {
if (ev.defaultPrevented)
return true;
var targetEle = ev.target;
for (var x = 0; x < 4; x++) {
if (!targetEle)
break;
if (targetEle.hasAttribute('disable-activated'))
return true;
targetEle = targetEle.parentElement;
}
return false;
};
return Activator;
})();
exports.Activator = Activator;
/***/ },
/* 21 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var activator_1 = __webpack_require__(20);
var animation_1 = __webpack_require__(22);
var dom_1 = __webpack_require__(11);
var RippleActivator = (function (_super) {
__extends(RippleActivator, _super);
function RippleActivator(app, config, zone) {
_super.call(this, app, config, zone);
this.expands = {};
this.fades = {};
this.expandSpeed = null;
}
RippleActivator.prototype.downAction = function (ev, activatableEle, pointerX, pointerY) {
var _this = this;
if (_super.prototype.downAction.call(this, ev, activatableEle, pointerX, pointerY)) {
// create a new ripple element
this.expandSpeed = EXPAND_DOWN_PLAYBACK_RATE;
this.zone.runOutsideAngular(function () {
dom_1.raf(function () {
var clientRect = activatableEle.getBoundingClientRect();
dom_1.raf(function () {
_this.createRipple(activatableEle, pointerX, pointerY, clientRect);
});
});
});
}
};
RippleActivator.prototype.createRipple = function (activatableEle, pointerX, pointerY, clientRect) {
var _this = this;
var clientPointerX = (pointerX - clientRect.left);
var clientPointerY = (pointerY - clientRect.top);
var x = Math.max(Math.abs(clientRect.width - clientPointerX), clientPointerX) * 2;
var y = Math.max(Math.abs(clientRect.height - clientPointerY), clientPointerY) * 2;
var diameter = Math.max(Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), 64);
var radius = Math.sqrt(clientRect.width + clientRect.height);
var duration = (1000 * Math.sqrt(radius / TOUCH_DOWN_ACCEL) + 0.5);
var rippleEle = document.createElement('md-ripple');
var rippleId = Date.now();
var eleStyle = rippleEle.style;
eleStyle.width = eleStyle.height = diameter + 'px';
eleStyle.marginTop = eleStyle.marginLeft = -(diameter / 2) + 'px';
eleStyle.left = clientPointerX + 'px';
eleStyle.top = clientPointerY + 'px';
activatableEle.appendChild(rippleEle);
// create the animation for the fade out, but don't start it yet
this.fades[rippleId] = new animation_1.Animation(rippleEle, { renderDelay: 0 });
this.fades[rippleId]
.fadeOut()
.duration(FADE_OUT_DURATION)
.playbackRate(1)
.onFinish(function () {
dom_1.raf(function () {
_this.fades[rippleId].dispose(true);
delete _this.fades[rippleId];
});
});
// expand the circle from the users starting point
// start slow, and when they let up, then speed up the animation
this.expands[rippleId] = new animation_1.Animation(rippleEle, { renderDelay: 0 });
this.expands[rippleId]
.fromTo('scale', '0.001', '1')
.duration(duration)
.playbackRate(this.expandSpeed)
.onFinish(function () {
_this.expands[rippleId].dispose();
delete _this.expands[rippleId];
_this.next();
})
.play();
};
RippleActivator.prototype.upAction = function () {
var _this = this;
this.deactivate();
this.expandSpeed = 1;
this.zone.runOutsideAngular(function () {
dom_1.rafFrames(4, function () {
_this.next();
});
});
};
RippleActivator.prototype.next = function () {
var now = Date.now();
var rippleId;
for (rippleId in this.expands) {
if (parseInt(rippleId, 10) + 4000 < now) {
this.expands[rippleId].dispose(true);
delete this.expands[rippleId];
}
else if (this.expands[rippleId].playbackRate() === EXPAND_DOWN_PLAYBACK_RATE) {
this.expands[rippleId].playbackRate(EXPAND_OUT_PLAYBACK_RATE);
}
}
for (rippleId in this.fades) {
if (parseInt(rippleId, 10) + 4000 < now) {
this.fades[rippleId].dispose(true);
delete this.fades[rippleId];
}
else if (!this.fades[rippleId].isPlaying) {
this.fades[rippleId].isPlaying = true;
this.fades[rippleId].play();
}
}
};
RippleActivator.prototype.clearState = function () {
this.deactivate();
this.next();
};
return RippleActivator;
})(activator_1.Activator);
exports.RippleActivator = RippleActivator;
var TOUCH_DOWN_ACCEL = 512;
var EXPAND_DOWN_PLAYBACK_RATE = 0.35;
var EXPAND_OUT_PLAYBACK_RATE = 3;
var FADE_OUT_DURATION = 700;
/***/ },
/* 22 */
/***/ function(module, exports, __webpack_require__) {
var dom_1 = __webpack_require__(11);
var util_1 = __webpack_require__(10);
/**
Animation Steps/Process
-----------------------
- Construct animation (doesn't start)
- Client play()'s animation, returns promise
- Add before classes to elements
- Remove before classes from elements
- Elements staged in "from" effect w/ inline styles
- Call onReady()
- Wait for RENDER_DELAY milliseconds (give browser time to render)
- Call onPlay()
- Run from/to animation on elements
- Animations finish async
- Set inline styles w/ the "to" effects on elements
- Add after classes to elements
- Remove after classes from elements
- Call onFinish()
- Resolve play()'s promise
**/
/**
* @private
**/
var Animation = (function () {
function Animation(ele, opts) {
if (opts === void 0) { opts = {}; }
this.reset();
this._opts = util_1.extend({
renderDelay: 16
}, opts);
this.elements(ele);
if (!document.documentElement.animate) {
console.error('Web Animations polyfill missing');
}
}
Animation.prototype.reset = function () {
this._el = [];
this._chld = [];
this._ani = [];
this._bfAdd = [];
this._bfSty = {};
this._bfRmv = [];
this._afAdd = [];
this._afRmv = [];
this._readys = [];
this._plays = [];
this._finishes = [];
};
Animation.prototype.elements = function (ele) {
if (ele) {
if (typeof ele === 'string') {
// string query selector
ele = document.querySelectorAll(ele);
}
if (ele.length) {
// array of elements
for (var i = 0; i < ele.length; i++) {
this.addElement(ele[i]);
}
}
else {
// single element
this.addElement(ele);
}
}
return this;
};
Animation.prototype.addElement = function (ele) {
// ensure only HTML Element nodes
if (ele) {
if (ele.nativeElement) {
// angular ElementRef
ele = ele.nativeElement;
}
if (ele.nodeType === 1) {
this._el.push(ele);
}
}
};
Animation.prototype.parent = function (parentAnimation) {
this._parent = parentAnimation;
return this;
};
Animation.prototype.add = function (childAnimations) {
var _childAnimations = Array.isArray(childAnimations) ? childAnimations : arguments;
for (var i = 0; i < _childAnimations.length; i++) {
_childAnimations[i].parent(this);
this._chld.push(_childAnimations[i]);
}
return this;
};
Animation.prototype.duration = function (value) {
if (arguments.length) {
this._duration = value;
return this;
}
return this._duration || (this._parent && this._parent.duration()) || 0;
};
Animation.prototype.clearDuration = function () {
this._duration = null;
for (var i = 0, l = this._chld.length; i < l; i++) {
this._chld[i].clearDuration();
}
};
Animation.prototype.easing = function (name, opts) {
if (arguments.length) {
this._easing = {
name: name,
opts: opts
};
return this;
}
return this._easing || (this._parent && this._parent.easing());
};
Animation.prototype.playbackRate = function (value) {
if (arguments.length) {
this._rate = value;
var i;
for (i = 0; i < this._chld.length; i++) {
this._chld[i].playbackRate(value);
}
for (i = 0; i < this._ani.length; i++) {
this._ani[i].playbackRate(value);
}
return this;
}
return (typeof this._rate !== 'undefined' ? this._rate : this._parent && this._parent.playbackRate());
};
Animation.prototype.reverse = function () {
return this.playbackRate(-1);
};
Animation.prototype.forward = function () {
return this.playbackRate(1);
};
Animation.prototype.from = function (property, value) {
if (!this._from) {
this._from = {};
}
this._from[property] = value;
return this;
};
Animation.prototype.to = function (property, value) {
if (!this._to) {
this._to = {};
}
this._to[property] = value;
return this;
};
Animation.prototype.fromTo = function (property, from, to) {
return this.from(property, from).to(property, to);
};
Animation.prototype.fadeIn = function () {
return this.fromTo('opacity', 0.001, 1);
};
Animation.prototype.fadeOut = function () {
return this.fromTo('opacity', 0.999, 0);
};
Object.defineProperty(Animation.prototype, "before", {
get: function () {
var _this = this;
return {
addClass: function (className) {
_this._bfAdd.push(className);
return _this;
},
removeClass: function (className) {
_this._bfRmv.push(className);
return _this;
},
setStyles: function (styles) {
_this._bfSty = styles;
return _this;
}
};
},
enumerable: true,
configurable: true
});
Object.defineProperty(Animation.prototype, "after", {
get: function () {
var _this = this;
return {
addClass: function (className) {
_this._afAdd.push(className);
return _this;
},
removeClass: function (className) {
_this._afRmv.push(className);
return _this;
}
};
},
enumerable: true,
configurable: true
});
Animation.prototype.play = function (done) {
var self = this;
// the actual play() method which may or may not start async
function beginPlay(beginPlayDone) {
var tasks = [];
self._chld.forEach(function (childAnimation) {
tasks.push(function (taskDone) {
childAnimation.play(taskDone);
});
});
self._ani.forEach(function (animation) {
tasks.push(function (taskDone) {
animation.play(taskDone);
});
});
parallel(tasks, beginPlayDone);
}
if (!self._parent) {
// this is the top level animation and is in full control
// of when the async play() should actually kick off
// stage all animations and child animations at their starting point
self.stage();
var promise;
if (!done) {
promise = new Promise(function (res) { done = res; });
}
function kickoff() {
// synchronously call all onPlay()'s before play()
self._onPlay();
beginPlay(function () {
self._onFinish();
done();
});
}
if (self._duration > 16 && self._opts.renderDelay > 0) {
// begin each animation when everything is rendered in their starting point
// give the browser some time to render everything in place before starting
dom_1.rafFrames(self._opts.renderDelay / 16, kickoff);
}
else {
// no need to render everything in there place before animating in
// just kick it off immediately to render them in their "to" locations
kickoff();
}
return promise;
}
// this is a child animation, it is told exactly when to
// start by the top level animation
beginPlay(done);
};
Animation.prototype.stage = function () {
// before the RENDER_DELAY
// before the animations have started
if (!this._isStaged) {
this._isStaged = true;
var i, p, l, j, ele, animation;
for (i = 0, l = this._chld.length; i < l; i++) {
this._chld[i].stage();
}
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (j = 0; j < this._bfAdd.length; j++) {
ele.classList.add(this._bfAdd[j]);
}
for (p in this._bfSty) {
ele.style[p] = this._bfSty[p];
}
for (j = 0; j < this._bfRmv.length; j++) {
ele.classList.remove(this._bfRmv[j]);
}
}
if (this._to) {
// only animate the elements if there are defined "to" effects
for (i = 0; i < this._el.length; i++) {
animation = new Animate(this._el[i], this._from, this._to, this.duration(), this.easing(), this.playbackRate());
if (animation.shouldAnimate) {
this._ani.push(animation);
}
}
}
for (i = 0; i < this._readys.length; i++) {
this._readys[i](this);
}
}
};
Animation.prototype._onPlay = function () {
// after the RENDER_DELAY
// before the animations have started
var i;
this._isFinished = false;
for (i = 0; i < this._chld.length; i++) {
this._chld[i]._onPlay();
}
for (i = 0; i < this._plays.length; i++) {
this._plays[i](this);
}
};
Animation.prototype._onFinish = function () {
// after the animations have finished
if (!this._isFinished && !this.isProgress) {
this._isFinished = true;
var i, j, ele;
for (i = 0; i < this._chld.length; i++) {
this._chld[i]._onFinish();
}
if (this.playbackRate() < 0) {
// reverse direction
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (j = 0; j < this._bfAdd.length; j++) {
ele.classList.remove(this._bfAdd[j]);
}
for (j = 0; j < this._bfRmv.length; j++) {
ele.classList.add(this._bfRmv[j]);
}
}
}
else {
// normal direction
for (i = 0; i < this._el.length; i++) {
ele = this._el[i];
for (j = 0; j < this._afAdd.length; j++) {
ele.classList.add(this._afAdd[j]);
}
for (j = 0; j < this._afRmv.length; j++) {
ele.classList.remove(this._afRmv[j]);
}
}
}
for (i = 0; i < this._finishes.length; i++) {
this._finishes[i](this);
}
}
};
Animation.prototype.pause = function () {
var i;
for (i = 0; i < this._chld.length; i++) {
this._chld[i].pause();
}
for (i = 0; i < this._ani.length; i++) {
this._ani[i].pause();
}
};
Animation.prototype.progressStart = function () {
this.isProgress = true;
for (var i = 0; i < this._chld.length; i++) {
this._chld[i].progressStart();
}
this.duration(1000);
this.play();
this.pause();
};
Animation.prototype.progress = function (value) {
value = Math.min(1, Math.max(0, value));
this.isProgress = true;
var i;
for (i = 0; i < this._chld.length; i++) {
this._chld[i].progress(value);
}
for (i = 0; i < this._ani.length; i++) {
this._ani[i].progress(value);
}
};
/**
* Get the current time of the first animation
* in the list. To get a specific time of an animation, call
* subAnimationInstance.getCurrentTime()
*/
Animation.prototype.getCurrentTime = function () {
if (this._chld.length > 0) {
return this._chld[0].getCurrentTime();
}
if (this._ani.length > 0) {
return this._ani[0].getCurrentTime();
}
return 0;
};
Animation.prototype.progressEnd = function (shouldComplete, rate) {
if (rate === void 0) { rate = 3; }
var promises = [];
this.isProgress = false;
for (var i = 0; i < this._chld.length; i++) {
promises.push(this._chld[i].progressEnd(shouldComplete));
}
this._ani.forEach(function (animation) {
if (shouldComplete) {
animation.playbackRate(rate);
}
else {
animation.playbackRate(rate * -1);
}
promises.push(new Promise(function (resolve) {
animation.play(resolve);
}));
});
return Promise.all(promises);
};
Animation.prototype.onReady = function (fn, clear) {
if (clear) {
this._readys = [];
}
this._readys.push(fn);
return this;
};
Animation.prototype.onPlay = function (fn, clear) {
if (clear) {
this._plays = [];
}
this._plays.push(fn);
return this;
};
Animation.prototype.onFinish = function (fn, clear) {
if (clear) {
this._finishes = [];
}
this._finishes.push(fn);
return this;
};
Animation.prototype.clone = function () {
function copy(dest, src) {
// undo what stage() may have already done
util_1.extend(dest, src);
dest._isFinished = dest._isStaged = dest.isProgress = false;
dest._chld = [];
dest._ani = [];
for (var i = 0; i < src._chld.length; i++) {
dest.add(copy(new Animation(), src._chld[i]));
}
return dest;
}
return copy(new Animation(), this);
};
Animation.prototype.dispose = function (removeElement) {
var i;
for (i = 0; i < this._chld.length; i++) {
this._chld[i].dispose(removeElement);
}
for (i = 0; i < this._ani.length; i++) {
this._ani[i].dispose(removeElement);
}
if (removeElement) {
for (i = 0; i < this._el.length; i++) {
this._el[i].parentNode && this._el[i].parentNode.removeChild(this._el[i]);
}
}
this.reset();
};
/*
STATIC CLASSES
*/
Animation.create = function (element, name) {
var AnimationClass = AnimationRegistry[name];
if (!AnimationClass) {
// couldn't find an animation by the given name
// fallback to just the base Animation class
AnimationClass = Animation;
}
return new AnimationClass(element);
};
Animation.createTransition = function (enteringView, leavingView, opts) {
if (opts === void 0) { opts = {}; }
var TransitionClass = AnimationRegistry[opts.animation];
if (!TransitionClass) {
// didn't find a transition animation, default to ios-transition
TransitionClass = AnimationRegistry['ios-transition'];
}
return new TransitionClass(enteringView, leavingView, opts);
};
Animation.register = function (name, AnimationClass) {
AnimationRegistry[name] = AnimationClass;
};
return Animation;
})();
exports.Animation = Animation;
/**
* @private
**/
var Animate = (function () {
function Animate(ele, fromEffect, toEffect, duration, easingConfig, playbackRate) {
// https://w3c.github.io/web-animations/
// not using the direct API methods because they're still in flux
// however, element.animate() seems locked in and uses the latest
// and correct API methods under the hood, so really doesn't matter
if (!fromEffect) {
return console.error(ele.tagName, 'animation fromEffect required, toEffect:', toEffect);
}
this.toEffect = parseEffect(toEffect);
this.shouldAnimate = (duration > 32);
if (!this.shouldAnimate) {
return inlineStyle(ele, this.toEffect);
}
this.ele = ele;
// stage where the element will start from
this.fromEffect = parseEffect(fromEffect);
inlineStyle(ele, this.fromEffect);
this.duration = duration;
this.rate = (typeof playbackRate !== 'undefined' ? playbackRate : 1);
this.easing = easingConfig && easingConfig.name || 'linear';
this.effects = [convertProperties(this.fromEffect)];
if (this.easing in EASING_FN) {
insertEffects(this.effects, this.fromEffect, this.toEffect, easingConfig);
}
else if (this.easing in CUBIC_BEZIERS) {
this.easing = 'cubic-bezier(' + CUBIC_BEZIERS[this.easing] + ')';
}
this.effects.push(convertProperties(this.toEffect));
}
Animate.prototype.play = function (done) {
var self = this;
if (self.ani) {
self.ani.play();
}
else {
// https://developers.google.com/web/updates/2014/05/Web-Animations---element-animate-is-now-in-Chrome-36
// https://w3c.github.io/web-animations/
// Future versions will use "new window.Animation" rather than "element.animate()"
self.ani = self.ele.animate(self.effects, {
duration: self.duration || 0,
easing: self.easing,
playbackRate: self.rate // old way of setting playbackRate, but still necessary
});
self.ani.playbackRate = self.rate;
}
self.ani.onfinish = function () {
// lock in where the element will stop at
// if the playbackRate is negative then it needs to return
// to its "from" effects
if (self.ani) {
inlineStyle(self.ele, self.rate < 0 ? self.fromEffect : self.toEffect);
self.ani = self.ani.onfinish = null;
done && done();
}
};
};
Animate.prototype.pause = function () {
this.ani && this.ani.pause();
};
Animate.prototype.progress = function (value) {
if (this.ani) {
// passed a number between 0 and 1
if (this.ani.playState !== 'paused') {
this.ani.pause();
}
// don't let the progress finish the animation
// leave it off JUST before it's finished
value = Math.min(0.999, Math.max(0.001, value));
this.ani.currentTime = (this.duration * value);
}
};
Animate.prototype.getCurrentTime = function () {
return (this.ani && this.ani.currentTime) || 0;
};
Animate.prototype.playbackRate = function (value) {
this.rate = value;
if (this.ani) {
this.ani.playbackRate = value;
}
};
Animate.prototype.dispose = function () {
this.ele = this.ani = this.effects = this.toEffect = null;
};
return Animate;
})();
function insertEffects(effects, fromEffect, toEffect, easingConfig) {
easingConfig.opts = easingConfig.opts || {};
var increment = easingConfig.opts.increment || 0.04;
var easingFn = EASING_FN[easingConfig.name];
var pos, tweenEffect, addEffect, property, toProperty, fromValue, diffValue;
for (pos = increment; pos <= (1 - increment); pos += increment) {
tweenEffect = {};
addEffect = false;
for (property in toEffect) {
toProperty = toEffect[property];
if (toProperty.tween) {
fromValue = fromEffect[property].num;
diffValue = toProperty.num - fromValue;
tweenEffect[property] = {
value: roundValue((easingFn(pos, easingConfig.opts) * diffValue) + fromValue) + toProperty.unit
};
addEffect = true;
}
}
if (addEffect) {
effects.push(convertProperties(tweenEffect));
}
}
}
function parseEffect(inputEffect) {
var val, r, num, property;
var outputEffect = {};
for (property in inputEffect) {
val = inputEffect[property];
r = val.toString().match(/(^-?\d*\.?\d*)(.*)/);
num = parseFloat(r[1]);
outputEffect[property] = {
value: val,
num: num,
unit: (r[0] != r[2] ? r[2] : ''),
tween: !isNaN(num) && (ANIMATE_PROPERTIES.indexOf(property) > -1)
};
}
return outputEffect;
}
function convertProperties(inputEffect) {
var outputEffect = {};
var transforms = [];
var value, property;
for (property in inputEffect) {
value = inputEffect[property].value;
if (TRANSFORMS.indexOf(property) > -1) {
transforms.push(property + '(' + value + ')');
}
else {
outputEffect[property] = value;
}
}
if (transforms.length) {
transforms.push('translateZ(0px)');
outputEffect.transform = transforms.join(' ');
}
return outputEffect;
}
function inlineStyle(ele, effect) {
if (ele && effect) {
var transforms = [];
var value, property;
for (property in effect) {
value = effect[property].value;
if (TRANSFORMS.indexOf(property) > -1) {
transforms.push(property + '(' + value + ')');
}
else {
ele.style[property] = value;
}
}
if (transforms.length) {
transforms.push('translateZ(0px)');
ele.style[dom_1.CSS.transform] = transforms.join(' ');
}
}
}
function roundValue(val) {
return Math.round(val * 10000) / 10000;
}
var TRANSFORMS = ['translateX', 'translateY', 'translateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ',
'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'perspective'];
var ANIMATE_PROPERTIES = TRANSFORMS.concat('opacity');
// Robert Penner's Easing Functions
// http://robertpenner.com/easing/
var CUBIC_BEZIERS = {
// default browser suppored easing
// ease
// ease-in
// ease-out
// ease-in-out
// Cubic
'ease-in-cubic': '0.55,0.055,0.675,0.19',
'ease-out-cubic': '0.215,0.61,0.355,1',
'ease-in-Out-cubic': '0.645,0.045,0.355,1',
// Circ
'ease-in-circ': '0.6,0.04,0.98,0.335',
'ease-out-circ': '0.075,0.82,0.165,1',
'ease-in-out-circ': '0.785,0.135,0.15,0.86',
// Expo
'ease-in-expo': '0.95,0.05,0.795,0.035',
'ease-out-expo': '0.19,1,0.22,1',
'ease-in-out-expo': '1,0,0,1',
// Quad
'ease-in-quad': '0.55,0.085,0.68,0.53',
'ease-out-quad': '0.25,0.46,0.45,0.94',
'ease-in-out-quad': '0.455,0.03,0.515,0.955',
// Quart
'ease-in-quart': '0.895,0.03,0.685,0.22',
'ease-out-quart': '0.165,0.84,0.44,1',
'ease-in-out-quart': '0.77,0,0.175,1',
// Quint
'ease-in-quint': '0.755,0.05,0.855,0.06',
'ease-out-quint': '0.23,1,0.32,1',
'ease-in-out-quint': '0.86,0,0.07,1',
// Sine
'ease-in-sine': '0.47,0,0.745,0.715',
'ease-out-sine': '0.39,0.575,0.565,1',
'ease-in-out-sine': '0.445,0.05,0.55,0.95',
// Back
'ease-in-back': '0.6,-0.28,0.735,0.045',
'ease-out-back': '0.175,0.885,0.32,1.275',
'ease-in-out-back': '0.68,-0.55,0.265,1.55',
};
var EASING_FN = {
'elastic': function (pos) {
return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1;
},
'swing-from-to': function (pos, opts) {
var s = opts.s || 1.70158;
return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) :
0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2);
},
'swing-from': function (pos, opts) {
var s = opts.s || 1.70158;
return pos * pos * ((s + 1) * pos - s);
},
'swing-to': function (pos, opts) {
var s = opts.s || 1.70158;
return (pos -= 1) * pos * ((s + 1) * pos + s) + 1;
},
'bounce': function (pos) {
if (pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
}
else if (pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75);
}
else if (pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375);
}
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375);
},
'bounce-past': function (pos) {
if (pos < (1 / 2.75)) {
return (7.5625 * pos * pos);
}
else if (pos < (2 / 2.75)) {
return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75);
}
else if (pos < (2.5 / 2.75)) {
return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375);
}
return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375);
},
'ease-out-bounce': function (pos) {
if ((pos) < (1 / 2.75)) {
return (7.5625 * pos * pos);
}
else if (pos < (2 / 2.75)) {
return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75);
}
else if (pos < (2.5 / 2.75)) {
return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375);
}
return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375);
},
'ease-from-to': function (pos) {
if ((pos /= 0.5) < 1)
return 0.5 * Math.pow(pos, 4);
return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2);
},
'ease-from': function (pos, opts) {
return Math.pow(pos, opts.s || 4);
},
'ease-to': function (pos, opts) {
return Math.pow(pos, opts.s || 0.25);
},
/*
* scripty2, Thomas Fuchs (MIT Licence)
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
'spring': function (pos, opts) {
var damping = opts.damping || 4.5;
var elasticity = opts.elasticity || 6;
return 1 - (Math.cos(pos * damping * Math.PI) * Math.exp(-pos * elasticity));
},
'sinusoidal': function (pos) {
return (-Math.cos(pos * Math.PI) / 2) + 0.5;
}
};
var AnimationRegistry = {};
function parallel(tasks, done) {
var l = tasks.length;
if (!l) {
done && done();
return;
}
var completed = 0;
function taskCompleted() {
completed++;
if (completed === l) {
done && done();
}
}
for (var i = 0; i < l; i++) {
tasks[i](taskCompleted);
}
}
/***/ },
/* 23 */
/***/ function(module, exports, __webpack_require__) {
var common_1 = __webpack_require__(24);
var overlay_1 = __webpack_require__(25);
var menu_1 = __webpack_require__(26);
var menu_toggle_1 = __webpack_require__(35);
var menu_close_1 = __webpack_require__(44);
var button_1 = __webpack_require__(43);
var blur_1 = __webpack_require__(45);
var content_1 = __webpack_require__(46);
var scroll_1 = __webpack_require__(48);
var pull_to_refresh_1 = __webpack_require__(49);
var slides_1 = __webpack_require__(50);
var tabs_1 = __webpack_require__(52);
var tab_1 = __webpack_require__(55);
var list_1 = __webpack_require__(56);
var item_1 = __webpack_require__(59);
var item_sliding_1 = __webpack_require__(60);
var toolbar_1 = __webpack_require__(42);
var icon_1 = __webpack_require__(41);
var checkbox_1 = __webpack_require__(61);
var toggle_1 = __webpack_require__(62);
var text_input_1 = __webpack_require__(63);
var label_1 = __webpack_require__(64);
var segment_1 = __webpack_require__(65);
var radio_1 = __webpack_require__(66);
var searchbar_1 = __webpack_require__(67);
var nav_1 = __webpack_require__(68);
var nav_push_1 = __webpack_require__(69);
var nav_router_1 = __webpack_require__(70);
var navbar_1 = __webpack_require__(40);
var id_1 = __webpack_require__(53);
var show_hide_when_1 = __webpack_require__(71);
/**
* @name IONIC_DIRECTIVES
* @private
* @description
* The core Ionic directives as well as Angular's CORE_DIRECTIVES and
* FORM_DIRECTIVES. Automatically available in every [@Page](../Page/) template.
*
* **Angular**
* - CORE_DIRECTIVES
* - FORM_DIRECTIVES
*
* **Content**
* - Menu
* - MenuToggle
* - MenuClose
*
* - Button
* - Blur
* - Content
* - Scroll
* - Refresher
*
* **Lists**
* - List
* - ListHeader
* - Item
* - ItemSliding
*
* **Slides**
* - Slides
* - Slide
* - SlideLazy
*
* **Tabs**
* - Tabs
* - Tab
*
* **Toolbar**
* - Toolbar
* - ToolbarTitle
* - ToolbarItem
*
* **Media**
* - Icon
*
* **Forms**
* - Searchbar
* - Segment
* - SegmentButton
* - Checkbox
* - RadioGroup
* - RadioButton
* - Toggle
* - TextInput
* - TextInputElement
* - Label
*
* **Nav**
* - Nav
* - NavbarTemplate
* - Navbar
* - NavPush
* - NavPop
* - NavRouter
* - IdRef
*
* - ShowWhen
* - HideWhen
*/
exports.IONIC_DIRECTIVES = [
// Angular
common_1.CORE_DIRECTIVES,
common_1.FORM_DIRECTIVES,
// Content
overlay_1.OverlayNav,
menu_1.Menu,
menu_toggle_1.MenuToggle,
menu_close_1.MenuClose,
button_1.Button,
blur_1.Blur,
content_1.Content,
scroll_1.Scroll,
pull_to_refresh_1.Refresher,
// Lists
list_1.List,
list_1.ListHeader,
item_1.Item,
item_sliding_1.ItemSliding,
// Slides
slides_1.Slides,
slides_1.Slide,
slides_1.SlideLazy,
// Tabs
tabs_1.Tabs,
tab_1.Tab,
// Toolbar
toolbar_1.Toolbar,
toolbar_1.ToolbarTitle,
toolbar_1.ToolbarItem,
// Media
icon_1.Icon,
// Forms
searchbar_1.Searchbar,
searchbar_1.SearchbarInput,
segment_1.Segment,
segment_1.SegmentButton,
checkbox_1.Checkbox,
radio_1.RadioGroup,
radio_1.RadioButton,
toggle_1.Toggle,
text_input_1.TextInput,
text_input_1.TextInputElement,
label_1.Label,
// Nav
nav_1.Nav,
navbar_1.NavbarTemplate,
navbar_1.Navbar,
nav_push_1.NavPush,
nav_push_1.NavPop,
nav_router_1.NavRouter,
id_1.IdRef,
show_hide_when_1.ShowWhen,
show_hide_when_1.HideWhen
];
/***/ },
/* 24 */
/***/ function(module, exports) {
module.exports = require("angular2")["common"];
/***/ },
/* 25 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(3);
/**
* @private
*/
var OverlayNav = (function () {
function OverlayNav() {
// deprecated warning
console.warn(' is no longer needed and can be safely removed.');
console.warn('https://github.com/driftyco/ionic2/blob/master/CHANGELOG.md#overlay-refactor');
console.warn('See the v2 docs for an update on how overlays work.');
}
OverlayNav = __decorate([
core_1.Directive({
selector: 'ion-overlay'
}),
__metadata('design:paramtypes', [])
], OverlayNav);
return OverlayNav;
})();
exports.OverlayNav = OverlayNav;
/***/ },
/* 26 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var core_1 = __webpack_require__(3);
var ion_1 = __webpack_require__(27);
var app_1 = __webpack_require__(6);
var config_1 = __webpack_require__(8);
var platform_1 = __webpack_require__(9);
var keyboard_1 = __webpack_require__(14);
var gestures = __webpack_require__(28);
/**
* @name Menu
* @description
* _For basic Menu usage, see the [Menu section](../../../../components/#menus)
* of the Component docs._
*
* Menu is a side-menu navigation that can be dragged out or toggled to show.
*
* @usage
* In order to use Menu, you must specify a [reference](https://angular.io/docs/ts/latest/guide/user-input.html#local-variables)
* to the content element that Menu should listen on for drag events, using the `content` property:
*
* ```html
*
*
*
* ...
*
*
*
*
*
* ```
*
* By default, Menus are on the left, but this can be overriden with the `side`
* property:
* ```html
*
* ```
*
* Menus can optionally be given an `id` attribute which allows the app to
* to get ahold of menu references. If no `id` is given then the menu
* automatically receives an `id` created from the side it is on, such as
* `leftMenu` or `rightMenu`. When using more than one menu it is always
* recommended to give each menu a unique `id`. Additionally menuToggle and
* menuClose directives should be given menu id values of their respective
* menu.
*
* Menu supports two display styles: overlay, and reveal. Overlay
* is the traditional Android drawer style, and Reveal is the traditional iOS
* style. By default, Menu will adjust to the correct style for the platform,
* but this can be overriden using the `type` property:
* ```html
*
* ```
*
* To programatically interact with the menu, you first get the menu component.
*
* ```ts
* @Page({
* `
* `
* )}
* export class MyClass{
* constructor(app: IonicApp){
* this.app = app;
* this.menu;
* }
*
* // Wait until the page is ready
* ngAfterViewInit(){
* this.menu = this.app.getComponent('leftMenu');
* }
*
* // Open the menu programatically
* openMenu(){
* this.menu.open();
* }
*
* }
* ```
*
* If you want to use any of the APIs down below, make sure to grabe the menu component by it's ID
*
* @demo /docs/v2/demos/menu/
*
* @see {@link /docs/v2/components#menus Menu Component Docs}
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
* @see {@link ../../nav/Nav Nav API Docs}
*
*/
var Menu = (function (_super) {
__extends(Menu, _super);
function Menu(elementRef, config, app, platform, keyboard, zone) {
_super.call(this, elementRef, config);
this.app = app;
this.platform = platform;
this.keyboard = keyboard;
this.zone = zone;
this.opening = new core_1.EventEmitter('opening');
this.isOpen = false;
this._preventTime = 0;
this.isEnabled = true;
}
/**
* @private
*/
Menu.prototype.ngOnInit = function () {
_super.prototype.ngOnInit.call(this);
var self = this;
var content = self.content;
self._cntEle = (content instanceof Node) ? content : content && content.getNativeElement && content.getNativeElement();
if (!self._cntEle) {
return console.error('Menu: must have a [content] element to listen for drag events on. Example:\n\n\n\n');
}
if (self.side !== 'left' && self.side !== 'right') {
self.side = 'left';
}
if (!self.id) {
// Auto register
self.id = self.side + 'Menu';
if (self.app.getComponent(self.id)) {
// id already exists, make sure this one is unique
self.id += (++menuIds);
}
self.app.register(self.id, self);
}
self._initGesture();
self._initType(self.type);
self._cntEle.classList.add('menu-content');
self._cntEle.classList.add('menu-content-' + self.type);
self.onContentClick = function (ev) {
if (self.isEnabled) {
ev.preventDefault();
ev.stopPropagation();
self.close();
}
};
};
/**
* @private
*/
Menu.prototype._initGesture = function () {
var _this = this;
this.zone.runOutsideAngular(function () {
switch (_this.side) {
case 'right':
_this._gesture = new gestures.RightMenuGesture(_this);
break;
case 'left':
_this._gesture = new gestures.LeftMenuGesture(_this);
break;
}
_this._targetGesture = new gestures.TargetGesture(_this);
});
};
/**
* @private
*/
Menu.prototype._initType = function (type) {
type = type && type.trim().toLowerCase();
if (!type) {
type = this.config.get('menuType');
}
this.type = type;
};
/**
* @private
*/
Menu.prototype._getType = function () {
if (!this._type) {
this._type = new menuTypes[this.type](this);
if (this.config.get('animate') === false) {
this._type.open.duration(33);
this._type.close.duration(33);
}
}
return this._type;
};
/**
* Sets the state of the Menu to open or not.
* @param {boolean} isOpen If the Menu is open or not.
* @return {Promise} returns a promise once set
*/
Menu.prototype.setOpen = function (shouldOpen) {
var _this = this;
// _isPrevented is used to prevent unwanted opening/closing after swiping open/close
// or swiping open the menu while pressing down on the menuToggle button
if (shouldOpen === this.isOpen || this._isPrevented()) {
return Promise.resolve();
}
this._before();
return this._getType().setOpen(shouldOpen).then(function () {
_this._after(shouldOpen);
});
};
/**
* @private
*/
Menu.prototype.setProgressStart = function () {
// user started swiping the menu open/close
if (this._isPrevented() || !this.isEnabled)
return;
this._before();
this._getType().setProgressStart(this.isOpen);
};
/**
* @private
*/
Menu.prototype.setProgess = function (value) {
// user actively dragging the menu
if (this.isEnabled) {
this._prevent();
this._getType().setProgess(value);
this.opening.next(value);
}
};
/**
* @private
*/
Menu.prototype.setProgressEnd = function (shouldComplete) {
var _this = this;
// user has finished dragging the menu
if (this.isEnabled) {
this._prevent();
this._getType().setProgressEnd(shouldComplete).then(function (isOpen) {
_this._after(isOpen);
});
}
};
/**
* @private
*/
Menu.prototype._before = function () {
// this places the menu into the correct location before it animates in
// this css class doesn't actually kick off any animations
if (this.isEnabled) {
this.getNativeElement().classList.add('show-menu');
this.getBackdropElement().classList.add('show-backdrop');
this._prevent();
this.keyboard.close();
}
};
/**
* @private
*/
Menu.prototype._after = function (isOpen) {
// keep opening/closing the menu disabled for a touch more yet
// only add listeners/css if it's enabled and isOpen
// and only remove listeners/css if it's not open
if ((this.isEnabled && isOpen) || !isOpen) {
this._prevent();
this.isOpen = isOpen;
this._cntEle.classList[isOpen ? 'add' : 'remove']('menu-content-open');
this._cntEle.removeEventListener('click', this.onContentClick);
if (isOpen) {
this._cntEle.addEventListener('click', this.onContentClick);
}
else {
this.getNativeElement().classList.remove('show-menu');
this.getBackdropElement().classList.remove('show-backdrop');
}
}
};
/**
* @private
*/
Menu.prototype._prevent = function () {
// used to prevent unwanted opening/closing after swiping open/close
// or swiping open the menu while pressing down on the menuToggle
this._preventTime = Date.now() + 20;
};
/**
* @private
*/
Menu.prototype._isPrevented = function () {
return this._preventTime > Date.now();
};
/**
* Progamatically open the Menu
* @return {Promise} returns a promise when the menu is fully opened
*/
Menu.prototype.open = function () {
return this.setOpen(true);
};
/**
* Progamatically close the Menu
* @return {Promise} returns a promise when the menu is fully closed
*/
Menu.prototype.close = function () {
return this.setOpen(false);
};
/**
* Toggle the menu. If it's closed, it will open, and if opened, it will close
* @return {Promise} returns a promise when the menu has been toggled
*/
Menu.prototype.toggle = function () {
return this.setOpen(!this.isOpen);
};
/**
* Used to enable or disable a menu. For example, there could be multiple
* left menus, but only one of them should be able to be dragged open.
* @param {boolean} shouldEnable True if it should be enabled, false if not.
* @return {Menu} Returns the instance of the menu, which is useful for chaining.
*/
Menu.prototype.enable = function (shouldEnable) {
this.isEnabled = shouldEnable;
if (!shouldEnable) {
this.close();
}
return this;
};
/**
* @private
*/
Menu.prototype.getMenuElement = function () {
return this.getNativeElement();
};
/**
* @private
*/
Menu.prototype.getContentElement = function () {
return this._cntEle;
};
/**
* @private
*/
Menu.prototype.getBackdropElement = function () {
return this.backdrop.elementRef.nativeElement;
};
/**
* @private
*/
Menu.register = function (name, cls) {
menuTypes[name] = cls;
};
/**
* @private
*/
Menu.prototype.ngOnDestroy = function () {
this.app.unregister(this.id);
this._gesture && this._gesture.destroy();
this._targetGesture && this._targetGesture.destroy();
this._type && this._type.ngOnDestroy();
this._cntEle = null;
};
Menu.getById = function (app, menuId) {
var menu = null;
if (menuId) {
menu = app.getComponent(menuId);
if (!menu) {
console.error('Menu with id "' + menuId + '" cannot be found for menuToggle');
return;
}
}
else {
menu = app.getComponent('leftMenu');
if (!menu) {
menu = app.getComponent('rightMenu');
}
if (!menu) {
console.error('Menu with id "leftMenu" or "rightMenu" cannot be found for menuToggle');
return;
}
}
return menu;
};
Menu = __decorate([
core_1.Component({
selector: 'ion-menu',
inputs: [
'content',
'id',
'side',
'type'
],
defaultInputs: {
'side': 'left',
'menuType': 'reveal'
},
outputs: ['opening'],
host: {
'role': 'navigation',
'[attr.side]': 'side',
'[attr.type]': 'type'
},
template: '',
directives: [core_1.forwardRef(function () { return MenuBackdrop; })]
}),
__metadata('design:paramtypes', [(typeof (_a = typeof core_1.ElementRef !== 'undefined' && core_1.ElementRef) === 'function' && _a) || Object, (typeof (_b = typeof config_1.Config !== 'undefined' && config_1.Config) === 'function' && _b) || Object, (typeof (_c = typeof app_1.IonicApp !== 'undefined' && app_1.IonicApp) === 'function' && _c) || Object, (typeof (_d = typeof platform_1.Platform !== 'undefined' && platform_1.Platform) === 'function' && _d) || Object, (typeof (_e = typeof keyboard_1.Keyboard !== 'undefined' && keyboard_1.Keyboard) === 'function' && _e) || Object, (typeof (_f = typeof core_1.NgZone !== 'undefined' && core_1.NgZone) === 'function' && _f) || Object])
], Menu);
return Menu;
var _a, _b, _c, _d, _e, _f;
})(ion_1.Ion);
exports.Menu = Menu;
var menuTypes = {};
var menuIds = 0;
var MenuBackdrop = (function () {
function MenuBackdrop(menu, elementRef) {
this.menu = menu;
this.elementRef = elementRef;
menu.backdrop = this;
}
/**
* @private
*/
MenuBackdrop.prototype.clicked = function (ev) {
console.debug('backdrop clicked');
ev.preventDefault();
ev.stopPropagation();
this.menu.close();
};
MenuBackdrop = __decorate([
core_1.Directive({
selector: '.backdrop',
host: {
'(click)': 'clicked($event)'
}
}),
__param(0, core_1.Host()),
__metadata('design:paramtypes', [Menu, (typeof (_a = typeof core_1.ElementRef !== 'undefined' && core_1.ElementRef) === 'function' && _a) || Object])
], MenuBackdrop);
return MenuBackdrop;
var _a;
})();
/***/ },
/* 27 */
/***/ function(module, exports, __webpack_require__) {
var dom = __webpack_require__(11);
/**
* Base class for all Ionic components. Exposes some common functionality
* that all Ionic components need, such as accessing underlying native elements and
* sending/receiving app-level events.
*/
var Ion = (function () {
function Ion(elementRef, config) {
this.elementRef = elementRef;
this.config = config;
}
Ion.prototype.ngOnInit = function () {
var cls = this.constructor;
if (cls.defaultInputs && this.config) {
for (var prop in cls.defaultInputs) {
// Priority:
// ---------
// 1) Value set from within constructor
// 2) Value set from the host element's attribute
// 3) Value set by the users global config
// 4) Value set by the default mode/platform config
// 5) Value set from the component's default
if (this[prop]) {
// this property has already been set on the instance
// could be from the user setting the element's attribute
// or from the user setting it within the constructor
continue;
}
// get the property values from a global user/platform config
var configVal = this.config.get(prop);
if (configVal) {
this[prop] = configVal;
continue;
}
// wasn't set yet, so go with property's default value
this[prop] = cls.defaultInputs[prop];
}
}
};
Ion.prototype.getElementRef = function () {
return this.elementRef;
};
Ion.prototype.getNativeElement = function () {
return this.elementRef.nativeElement;
};
Ion.prototype.getDimensions = function () {
return dom.getDimensions(this);
};
Ion.prototype.width = function () {
return dom.getDimensions(this).width;
};
Ion.prototype.height = function () {
return dom.getDimensions(this).height;
};
return Ion;
})();
exports.Ion = Ion;
/***/ },
/* 28 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var slide_edge_gesture_1 = __webpack_require__(29);
var util = __webpack_require__(33);
var MenuContentGesture = (function (_super) {
__extends(MenuContentGesture, _super);
function MenuContentGesture(menu, targetEl, options) {
if (options === void 0) { options = {}; }
_super.call(this, targetEl, util.extend({
direction: (menu.side === 'left' || menu.side === 'right') ? 'x' : 'y',
edge: menu.side,
threshold: 75
}, options));
this.menu = menu;
this.listen();
}
MenuContentGesture.prototype.canStart = function (ev) {
return this.menu.isOpen && this.menu.isEnabled ? true : _super.prototype.canStart.call(this, ev);
};
// Set CSS, then wait one frame for it to apply before sliding starts
MenuContentGesture.prototype.onSlideBeforeStart = function (slide, ev) {
this.menu.setProgressStart();
};
MenuContentGesture.prototype.onSlide = function (slide, ev) {
this.menu.setProgess(slide.distance / slide.max);
};
MenuContentGesture.prototype.onSlideEnd = function (slide, ev) {
var shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
this.menu.setProgressEnd(shouldComplete);
};
MenuContentGesture.prototype.getElementStartPos = function (slide, ev) {
return this.menu.isOpen ? slide.max : slide.min;
};
MenuContentGesture.prototype.getSlideBoundaries = function () {
return {
min: 0,
max: this.menu.width()
};
};
return MenuContentGesture;
})(slide_edge_gesture_1.SlideEdgeGesture);
exports.MenuContentGesture = MenuContentGesture;
/**
* Support dragging the target menu as well as the content.
*/
var TargetGesture = (function (_super) {
__extends(TargetGesture, _super);
function TargetGesture(menu) {
_super.call(this, menu, menu.getNativeElement(), {
threshold: 0
});
}
return TargetGesture;
})(MenuContentGesture);
exports.TargetGesture = TargetGesture;
var LeftMenuGesture = (function (_super) {
__extends(LeftMenuGesture, _super);
function LeftMenuGesture(menu) {
_super.call(this, menu, menu.getContentElement());
}
return LeftMenuGesture;
})(MenuContentGesture);
exports.LeftMenuGesture = LeftMenuGesture;
var RightMenuGesture = (function (_super) {
__extends(RightMenuGesture, _super);
function RightMenuGesture(menu) {
_super.call(this, menu, menu.getContentElement());
}
RightMenuGesture.prototype.onSlide = function (slide, ev) {
this.menu.setProgess(slide.distance / slide.min);
};
RightMenuGesture.prototype.getElementStartPos = function (slide, ev) {
return this.menu.isOpen ? slide.min : slide.max;
};
RightMenuGesture.prototype.getSlideBoundaries = function () {
return {
min: -this.menu.width(),
max: 0
};
};
return RightMenuGesture;
})(MenuContentGesture);
exports.RightMenuGesture = RightMenuGesture;
/***/ },
/* 29 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var slide_gesture_1 = __webpack_require__(30);
var util_1 = __webpack_require__(10);
var dom_1 = __webpack_require__(11);
var SlideEdgeGesture = (function (_super) {
__extends(SlideEdgeGesture, _super);
function SlideEdgeGesture(element, opts) {
if (opts === void 0) { opts = {}; }
util_1.defaults(opts, {
edge: 'left',
threshold: 50
});
_super.call(this, element, opts);
// Can check corners through use of eg 'left top'
this.edges = opts.edge.split(' ');
this.threshold = opts.threshold;
}
SlideEdgeGesture.prototype.canStart = function (ev) {
var _this = this;
this._d = this.getContainerDimensions();
return this.edges.every(function (edge) { return _this._checkEdge(edge, ev.center); });
};
SlideEdgeGesture.prototype.getContainerDimensions = function () {
return {
left: 0,
top: 0,
width: dom_1.windowDimensions().width,
height: dom_1.windowDimensions().height
};
};
SlideEdgeGesture.prototype._checkEdge = function (edge, pos) {
switch (edge) {
case 'left': return pos.x <= this._d.left + this.threshold;
case 'right': return pos.x >= this._d.width - this.threshold;
case 'top': return pos.y <= this._d.top + this.threshold;
case 'bottom': return pos.y >= this._d.height - this.threshold;
}
};
return SlideEdgeGesture;
})(slide_gesture_1.SlideGesture);
exports.SlideEdgeGesture = SlideEdgeGesture;
/***/ },
/* 30 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var drag_gesture_1 = __webpack_require__(31);
var util = __webpack_require__(33);
var SlideGesture = (function (_super) {
__extends(SlideGesture, _super);
function SlideGesture(element, opts) {
if (opts === void 0) { opts = {}; }
_super.call(this, element, opts);
this.element = element;
}
/*
* Get the min and max for the slide. pageX/pageY.
* Only called on dragstart.
*/
SlideGesture.prototype.getSlideBoundaries = function (slide, ev) {
return {
min: 0,
max: this.element.offsetWidth
};
};
/*
* Get the element's pos when the drag starts.
* For example, an open side menu starts at 100% and a closed
* sidemenu starts at 0%.
*/
SlideGesture.prototype.getElementStartPos = function (slide, ev) {
return 0;
};
SlideGesture.prototype.canStart = function () {
return true;
};
SlideGesture.prototype.onDragStart = function (ev) {
var _this = this;
if (!this.canStart(ev))
return false;
this.slide = {};
var promise = this.onSlideBeforeStart(this.slide, ev) || Promise.resolve();
promise.then(function () {
var _a = _this.getSlideBoundaries(_this.slide, ev), min = _a.min, max = _a.max;
_this.slide.min = min;
_this.slide.max = max;
_this.slide.elementStartPos = _this.getElementStartPos(_this.slide, ev);
_this.slide.pointerStartPos = ev.center[_this.direction];
_this.slide.started = true;
_this.onSlideStart(_this.slide, ev);
}).catch(function () {
_this.slide = null;
});
};
SlideGesture.prototype.onDrag = function (ev) {
if (!this.slide || !this.slide.started)
return;
this.slide.pos = ev.center[this.direction];
this.slide.distance = util.clamp(this.slide.min, this.slide.pos - this.slide.pointerStartPos + this.slide.elementStartPos, this.slide.max);
this.slide.delta = this.slide.pos - this.slide.pointerStartPos;
this.onSlide(this.slide, ev);
};
SlideGesture.prototype.onDragEnd = function (ev) {
if (!this.slide || !this.slide.started)
return;
this.onSlideEnd(this.slide, ev);
this.slide = null;
};
SlideGesture.prototype.onSlideBeforeStart = function () { };
SlideGesture.prototype.onSlideStart = function () { };
SlideGesture.prototype.onSlide = function () { };
SlideGesture.prototype.onSlideEnd = function () { };
return SlideGesture;
})(drag_gesture_1.DragGesture);
exports.SlideGesture = SlideGesture;
/***/ },
/* 31 */
/***/ function(module, exports, __webpack_require__) {
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var gesture_1 = __webpack_require__(32);
var util = __webpack_require__(33);
var DragGesture = (function (_super) {
__extends(DragGesture, _super);
function DragGesture(element, opts) {
if (opts === void 0) { opts = {}; }
util.defaults(opts, {});
_super.call(this, element, opts);
}
DragGesture.prototype.listen = function () {
var _this = this;
_super.prototype.listen.call(this);
this.on('panstart', function (ev) {
if (_this.onDragStart(ev) !== false) {
_this.dragging = true;
}
});
this.on('panmove', function (ev) {
if (!_this.dragging)
return;
if (_this.onDrag(ev) === false) {
_this.dragging = false;
}
});
this.on('panend', function (ev) {
if (!_this.dragging)
return;
_this.onDragEnd(ev);
_this.dragging = false;
});
this.hammertime.get('pan').set(this._options);
};
DragGesture.prototype.onDrag = function () { };
DragGesture.prototype.onDragStart = function () { };
DragGesture.prototype.onDragEnd = function () { };
return DragGesture;
})(gesture_1.Gesture);
exports.DragGesture = DragGesture;
/***/ },
/* 32 */
/***/ function(module, exports, __webpack_require__) {
var util = __webpack_require__(33);
var hammer_1 = __webpack_require__(34);
/**
* A gesture recognizer class.
*
* TODO(mlynch): Re-enable the DOM event simulation that was causing issues (or verify hammer does this already, it might);
*/
var Gesture = (function () {
function Gesture(element, opts) {
if (opts === void 0) { opts = {}; }
util.defaults(opts, {
domEvents: true
});
this.element = element;
// Map 'x' or 'y' string to hammerjs opts
this.direction = opts.direction || 'x';
opts.direction = this.direction === 'x' ?
hammer_1.Hammer.DIRECTION_HORIZONTAL :
hammer_1.Hammer.DIRECTION_VERTICAL;
this._options = opts;
this._callbacks = {};
}
Gesture.prototype.options = function (opts) {
if (opts === void 0) { opts = {}; }
util.extend(this._options, opts);
};
Gesture.prototype.on = function (type, cb) {
if (type == 'pinch' || type == 'rotate') {
this.hammertime.get('pinch').set({ enable: true });
}
this.hammertime.on(type, cb);
(this._callbacks[type] || (this._callbacks[type] = [])).push(cb);
};
Gesture.prototype.off = function (type, cb) {
this.hammertime.off(type, this._callbacks[type] ? cb : null);
};
Gesture.prototype.listen = function () {
this.hammertime = hammer_1.Hammer(this.element, this._options);
};
Gesture.prototype.unlisten = function () {
if (this.hammertime) {
for (var type in this._callbacks) {
for (var i = 0; i < this._callbacks[type].length; i++) {
this.hammertime.off(type, this._callbacks[type]);
}
}
this.hammertime.destroy();
this.hammertime = null;
this._callbacks = {};
}
};
Gesture.prototype.destroy = function () {
this.unlisten();
};
return Gesture;
})();
exports.Gesture = Gesture;
/***/ },
/* 33 */
/***/ function(module, exports, __webpack_require__) {
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
var domUtil = __webpack_require__(11);
exports.dom = domUtil;
__export(__webpack_require__(10));
/***/ },
/* 34 */
/***/ function(module, exports) {
/*! Hammer.JS - v2.0.4 - 2014-09-28
* http://hammerjs.github.io/
*
* Copyright (c) 2014 Jorik Tangelder;
* Licensed under the MIT license */
var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
var TEST_ELEMENT = document.createElement('div');
var TYPE_FUNCTION = 'function';
var round = Math.round;
var abs = Math.abs;
var now = Date.now;
/**
* set a timeout with a given scope
* @param {Function} fn
* @param {Number} timeout
* @param {Object} context
* @returns {number}
*/
function setTimeoutContext(fn, timeout, context) {
return setTimeout(bindFn(fn, context), timeout);
}
/**
* if the argument is an array, we want to execute the fn on each entry
* if it aint an array we don't want to do a thing.
* this is used by all the methods that accept a single and array argument.
* @param {*|Array} arg
* @param {String} fn
* @param {Object} [context]
* @returns {Boolean}
*/
function invokeArrayArg(arg, fn, context) {
if (Array.isArray(arg)) {
each(arg, context[fn], context);
return true;
}
return false;
}
/**
* walk objects and arrays
* @param {Object} obj
* @param {Function} iterator
* @param {Object} context
*/
function each(obj, iterator, context) {
var i;
if (!obj) {
return;
}
if (obj.forEach) {
obj.forEach(iterator, context);
}
else if (obj.length !== undefined) {
i = 0;
while (i < obj.length) {
iterator.call(context, obj[i], i, obj);
i++;
}
}
else {
for (i in obj) {
obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
}
}
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge]
* @returns {Object} dest
*/
function extend(dest, src, merge) {
var keys = Object.keys(src);
var i = 0;
while (i < keys.length) {
if (!merge || (merge && dest[keys[i]] === undefined)) {
dest[keys[i]] = src[keys[i]];
}
i++;
}
return dest;
}
/**
* merge the values from src in the dest.
* means that properties that exist in dest will not be overwritten by src
* @param {Object} dest
* @param {Object} src
* @returns {Object} dest
*/
function merge(dest, src) {
return extend(dest, src, true);
}
/**
* simple class inheritance
* @param {Function} child
* @param {Function} base
* @param {Object} [properties]
*/
function inherit(child, base, properties) {
var baseP = base.prototype, childP;
childP = child.prototype = Object.create(baseP);
childP.constructor = child;
childP._super = baseP;
if (properties) {
extend(childP, properties);
}
}
/**
* simple function bind
* @param {Function} fn
* @param {Object} context
* @returns {Function}
*/
function bindFn(fn, context) {
return function boundFn() {
return fn.apply(context, arguments);
};
}
/**
* let a boolean value also be a function that must return a boolean
* this first item in args will be used as the context
* @param {Boolean|Function} val
* @param {Array} [args]
* @returns {Boolean}
*/
function boolOrFn(val, args) {
if (typeof val == TYPE_FUNCTION) {
return val.apply(args ? args[0] || undefined : undefined, args);
}
return val;
}
/**
* use the val2 when val1 is undefined
* @param {*} val1
* @param {*} val2
* @returns {*}
*/
function ifUndefined(val1, val2) {
return (val1 === undefined) ? val2 : val1;
}
/**
* addEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function addEventListeners(target, types, handler) {
each(splitStr(types), function (type) {
//console.debug('hammer addEventListener', type, target.tagName);
target.addEventListener(type, handler, false);
});
}
/**
* removeEventListener with multiple events at once
* @param {EventTarget} target
* @param {String} types
* @param {Function} handler
*/
function removeEventListeners(target, types, handler) {
each(splitStr(types), function (type) {
//console.debug('hammer removeEventListener', type, target.tagName);
target.removeEventListener(type, handler, false);
});
}
/**
* find if a node is in the given parent
* @method hasParent
* @param {HTMLElement} node
* @param {HTMLElement} parent
* @return {Boolean} found
*/
function hasParent(node, parent) {
while (node) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
/**
* small indexOf wrapper
* @param {String} str
* @param {String} find
* @returns {Boolean} found
*/
function inStr(str, find) {
return str.indexOf(find) > -1;
}
/**
* split string on whitespace
* @param {String} str
* @returns {Array} words
*/
function splitStr(str) {
return str.trim().split(/\s+/g);
}
/**
* find if a array contains the object using indexOf or a simple polyFill
* @param {Array} src
* @param {String} find
* @param {String} [findByKey]
* @return {Boolean|Number} false when not found, or the index
*/
function inArray(src, find, findByKey) {
if (src.indexOf && !findByKey) {
return src.indexOf(find);
}
else {
var i = 0;
while (i < src.length) {
if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
return i;
}
i++;
}
return -1;
}
}
/**
* convert array-like objects to real arrays
* @param {Object} obj
* @returns {Array}
*/
function toArray(obj) {
return Array.prototype.slice.call(obj, 0);
}
/**
* unique array with objects based on a key (like 'id') or just by the array's value
* @param {Array} src [{id:1},{id:2},{id:1}]
* @param {String} [key]
* @param {Boolean} [sort=False]
* @returns {Array} [{id:1},{id:2}]
*/
function uniqueArray(src, key, sort) {
var results = [];
var values = [];
var i = 0;
while (i < src.length) {
var val = key ? src[i][key] : src[i];
if (inArray(values, val) < 0) {
results.push(src[i]);
}
values[i] = val;
i++;
}
if (sort) {
if (!key) {
results = results.sort();
}
else {
results = results.sort(function sortUniqueArray(a, b) {
return a[key] > b[key];
});
}
}
return results;
}
/**
* get the prefixed property
* @param {Object} obj
* @param {String} property
* @returns {String|Undefined} prefixed
*/
function prefixed(obj, property) {
var prefix, prop;
var camelProp = property[0].toUpperCase() + property.slice(1);
var i = 0;
while (i < VENDOR_PREFIXES.length) {
prefix = VENDOR_PREFIXES[i];
prop = (prefix) ? prefix + camelProp : property;
if (prop in obj) {
return prop;
}
i++;
}
return undefined;
}
/**
* get a unique id
* @returns {number} uniqueId
*/
var _uniqueId = 1;
function uniqueId() {
return _uniqueId++;
}
/**
* get the window object of an element
* @param {HTMLElement} element
* @returns {DocumentView|Window}
*/
function getWindowForElement(element) {
var doc = element.ownerDocument;
return (doc.defaultView || doc.parentWindow);
}
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
var SUPPORT_TOUCH = ('ontouchstart' in window);
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
var INPUT_TYPE_TOUCH = 'touch';
var INPUT_TYPE_PEN = 'pen';
var INPUT_TYPE_MOUSE = 'mouse';
var INPUT_TYPE_KINECT = 'kinect';
var COMPUTE_INTERVAL = 25;
var INPUT_START = 1;
var INPUT_MOVE = 2;
var INPUT_END = 4;
var INPUT_CANCEL = 8;
var DIRECTION_NONE = 1;
var DIRECTION_LEFT = 2;
var DIRECTION_RIGHT = 4;
var DIRECTION_UP = 8;
var DIRECTION_DOWN = 16;
var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
var PROPS_XY = ['x', 'y'];
var PROPS_CLIENT_XY = ['clientX', 'clientY'];
/**
* create new input type manager
* @param {Manager} manager
* @param {Function} callback
* @returns {Input}
* @constructor
*/
function Input(manager, callback) {
var self = this;
this.manager = manager;
this.callback = callback;
this.element = manager.element;
this.target = manager.options.inputTarget;
// smaller wrapper around the handler, for the scope and the enabled state of the manager,
// so when disabled the input events are completely bypassed.
this.domHandler = function (ev) {
if (boolOrFn(manager.options.enable, [manager])) {
self.handler(ev);
}
};
this.init();
}
Input.prototype = {
/**
* should handle the inputEvent data and trigger the callback
* @virtual
*/
handler: function () { },
/**
* bind the events
*/
init: function () {
//console.debug('hammer Input init')
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
},
/**
* unbind the events
*/
destroy: function () {
this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
}
};
/**
* create new input type manager
* called by the Manager constructor
* @param {Hammer} manager
* @returns {Input}
*/
function createInputInstance(manager) {
var Type;
var inputClass = manager.options.inputClass;
if (inputClass) {
Type = inputClass;
}
else if (SUPPORT_POINTER_EVENTS) {
Type = PointerEventInput;
}
else if (SUPPORT_ONLY_TOUCH) {
Type = TouchInput;
}
else if (!SUPPORT_TOUCH) {
Type = MouseInput;
}
else {
Type = TouchMouseInput;
}
return new (Type)(manager, inputHandler);
}
/**
* handle input events
* @param {Manager} manager
* @param {String} eventType
* @param {Object} input
*/
function inputHandler(manager, eventType, input) {
var pointersLen = input.pointers.length;
var changedPointersLen = input.changedPointers.length;
var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
input.isFirst = !!isFirst;
input.isFinal = !!isFinal;
if (isFirst) {
manager.session = {};
}
// source event is the normalized value of the domEvents
// like 'touchstart, mouseup, pointerdown'
input.eventType = eventType;
// compute scale, rotation etc
computeInputData(manager, input);
// emit secret event
manager.emit('hammer.input', input);
manager.recognize(input);
manager.session.prevInput = input;
}
/**
* extend the data with some usable properties like scale, rotate, velocity etc
* @param {Object} manager
* @param {Object} input
*/
function computeInputData(manager, input) {
var session = manager.session;
var pointers = input.pointers;
var pointersLength = pointers.length;
// store the first input to calculate the distance and direction
if (!session.firstInput) {
session.firstInput = simpleCloneInputData(input);
}
// to compute scale and rotation we need to store the multiple touches
if (pointersLength > 1 && !session.firstMultiple) {
session.firstMultiple = simpleCloneInputData(input);
}
else if (pointersLength === 1) {
session.firstMultiple = false;
}
var firstInput = session.firstInput;
var firstMultiple = session.firstMultiple;
var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
var center = input.center = getCenter(pointers);
input.timeStamp = now();
input.deltaTime = input.timeStamp - firstInput.timeStamp;
input.angle = getAngle(offsetCenter, center);
input.distance = getDistance(offsetCenter, center);
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
computeIntervalInputData(session, input);
// find the correct target
var target = manager.element;
if (hasParent(input.srcEvent.target, target)) {
target = input.srcEvent.target;
}
input.target = target;
}
function computeDeltaXY(session, input) {
var center = input.center;
var offset = session.offsetDelta || {};
var prevDelta = session.prevDelta || {};
var prevInput = session.prevInput || {};
if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
prevDelta = session.prevDelta = {
x: prevInput.deltaX || 0,
y: prevInput.deltaY || 0
};
offset = session.offsetDelta = {
x: center.x,
y: center.y
};
}
input.deltaX = prevDelta.x + (center.x - offset.x);
input.deltaY = prevDelta.y + (center.y - offset.y);
}
/**
* velocity is calculated every x ms
* @param {Object} session
* @param {Object} input
*/
function computeIntervalInputData(session, input) {
var last = session.lastInterval || input, deltaTime = input.timeStamp - last.timeStamp, velocity, velocityX, velocityY, direction;
if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
var deltaX = last.deltaX - input.deltaX;
var deltaY = last.deltaY - input.deltaY;
var v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
velocityY = v.y;
velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
direction = getDirection(deltaX, deltaY);
session.lastInterval = input;
}
else {
// use latest velocity info if it doesn't overtake a minimum period
velocity = last.velocity;
velocityX = last.velocityX;
velocityY = last.velocityY;
direction = last.direction;
}
input.velocity = velocity;
input.velocityX = velocityX;
input.velocityY = velocityY;
input.direction = direction;
}
/**
* create a simple clone from the input used for storage of firstInput and firstMultiple
* @param {Object} input
* @returns {Object} clonedInputData
*/
function simpleCloneInputData(input) {
// make a simple copy of the pointers because we will get a reference if we don't
// we only need clientXY for the calculations
var pointers = [];
var i = 0;
while (i < input.pointers.length) {
pointers[i] = {
clientX: round(input.pointers[i].clientX),
clientY: round(input.pointers[i].clientY)
};
i++;
}
return {
timeStamp: now(),
pointers: pointers,
center: getCenter(pointers),
deltaX: input.deltaX,
deltaY: input.deltaY
};
}
/**
* get the center of all the pointers
* @param {Array} pointers
* @return {Object} center contains `x` and `y` properties
*/
function getCenter(pointers) {
var pointersLength = pointers.length;
// no need to loop when only one touch
if (pointersLength === 1) {
return {
x: round(pointers[0].clientX),
y: round(pointers[0].clientY)
};
}
var x = 0, y = 0, i = 0;
while (i < pointersLength) {
x += pointers[i].clientX;
y += pointers[i].clientY;
i++;
}
return {
x: round(x / pointersLength),
y: round(y / pointersLength)
};
}
/**
* calculate the velocity between two points. unit is in px per ms.
* @param {Number} deltaTime
* @param {Number} x
* @param {Number} y
* @return {Object} velocity `x` and `y`
*/
function getVelocity(deltaTime, x, y) {
return {
x: x / deltaTime || 0,
y: y / deltaTime || 0
};
}
/**
* get the direction between two points
* @param {Number} x
* @param {Number} y
* @return {Number} direction
*/
function getDirection(x, y) {
if (x === y) {
return DIRECTION_NONE;
}
if (abs(x) >= abs(y)) {
return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
/**
* calculate the absolute distance between two points
* @param {Object} p1 {x, y}
* @param {Object} p2 {x, y}
* @param {Array} [props] containing x and y keys
* @return {Number} distance
*/
function getDistance(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
return Math.sqrt((x * x) + (y * y));
}
/**
* calculate the angle between two coordinates
* @param {Object} p1
* @param {Object} p2
* @param {Array} [props] containing x and y keys
* @return {Number} angle
*/
function getAngle(p1, p2, props) {
if (!props) {
props = PROPS_XY;
}
var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]];
return Math.atan2(y, x) * 180 / Math.PI;
}
/**
* calculate the rotation degrees between two pointersets
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} rotation
*/
function getRotation(start, end) {
return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
}
/**
* calculate the scale factor between two pointersets
* no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
* @param {Array} start array of pointers
* @param {Array} end array of pointers
* @return {Number} scale
*/
function getScale(start, end) {
return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
}
var MOUSE_INPUT_MAP = {
mousedown: INPUT_START,
mousemove: INPUT_MOVE,
mouseup: INPUT_END
};
var MOUSE_ELEMENT_EVENTS = 'mousedown';
var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
/**
* Mouse events input
* @constructor
* @extends Input
*/
function MouseInput() {
this.evEl = MOUSE_ELEMENT_EVENTS;
this.evWin = MOUSE_WINDOW_EVENTS;
this.allow = true; // used by Input.TouchMouse to disable mouse events
this.pressed = false; // mousedown state
Input.apply(this, arguments);
}
inherit(MouseInput, Input, {
/**
* handle mouse events
* @param {Object} ev
*/
handler: function MEhandler(ev) {
var eventType = MOUSE_INPUT_MAP[ev.type];
// on start we want to have the left mouse button down
if (eventType & INPUT_START && ev.button === 0) {
this.pressed = true;
}
if (eventType & INPUT_MOVE && ev.which !== 1) {
eventType = INPUT_END;
}
// mouse must be down, and mouse events are allowed (see the TouchMouse input)
if (!this.pressed || !this.allow) {
return;
}
if (eventType & INPUT_END) {
this.pressed = false;
}
this.callback(this.manager, eventType, {
pointers: [ev],
changedPointers: [ev],
pointerType: INPUT_TYPE_MOUSE,
srcEvent: ev
});
}
});
var POINTER_INPUT_MAP = {
pointerdown: INPUT_START,
pointermove: INPUT_MOVE,
pointerup: INPUT_END,
pointercancel: INPUT_CANCEL,
pointerout: INPUT_CANCEL
};
// in IE10 the pointer types is defined as an enum
var IE10_POINTER_TYPE_ENUM = {
2: INPUT_TYPE_TOUCH,
3: INPUT_TYPE_PEN,
4: INPUT_TYPE_MOUSE,
5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
};
var POINTER_ELEMENT_EVENTS = 'pointerdown';
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent) {
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
/**
* Pointer events input
* @constructor
* @extends Input
*/
function PointerEventInput() {
this.evEl = POINTER_ELEMENT_EVENTS;
this.evWin = POINTER_WINDOW_EVENTS;
Input.apply(this, arguments);
this.store = (this.manager.session.pointerEvents = []);
}
inherit(PointerEventInput, Input, {
/**
* handle mouse events
* @param {Object} ev
*/
handler: function PEhandler(ev) {
var store = this.store;
var removePointer = false;
var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
var isTouch = (pointerType == INPUT_TYPE_TOUCH);
// get index of the event in the store
var storeIndex = inArray(store, ev.pointerId, 'pointerId');
// start and mouse must be down
if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
if (storeIndex < 0) {
store.push(ev);
storeIndex = store.length - 1;
}
}
else if (eventType & (INPUT_END | INPUT_CANCEL)) {
removePointer = true;
}
// it not found, so the pointer hasn't been down (so it's probably a hover)
if (storeIndex < 0) {
return;
}
// update the event in the store
store[storeIndex] = ev;
this.callback(this.manager, eventType, {
pointers: store,
changedPointers: [ev],
pointerType: pointerType,
srcEvent: ev
});
if (removePointer) {
// remove from the store
store.splice(storeIndex, 1);
}
}
});
var SINGLE_TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* Touch events input
* @constructor
* @extends Input
*/
function SingleTouchInput() {
this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
this.started = false;
Input.apply(this, arguments);
}
inherit(SingleTouchInput, Input, {
handler: function TEhandler(ev) {
var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
// should we handle the touch events?
if (type === INPUT_START) {
this.started = true;
}
if (!this.started) {
return;
}
var touches = normalizeSingleTouches.call(this, ev, type);
// when done, reset the started state
if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
this.started = false;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
});
/**
* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/
function normalizeSingleTouches(ev, type) {
var all = toArray(ev.touches);
var changed = toArray(ev.changedTouches);
if (type & (INPUT_END | INPUT_CANCEL)) {
all = uniqueArray(all.concat(changed), 'identifier', true);
}
return [all, changed];
}
var TOUCH_INPUT_MAP = {
touchstart: INPUT_START,
touchmove: INPUT_MOVE,
touchend: INPUT_END,
touchcancel: INPUT_CANCEL
};
var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
/**
* Multi-user touch events input
* @constructor
* @extends Input
*/
function TouchInput() {
this.evTarget = TOUCH_TARGET_EVENTS;
this.targetIds = {};
Input.apply(this, arguments);
}
inherit(TouchInput, Input, {
handler: function MTEhandler(ev) {
var type = TOUCH_INPUT_MAP[ev.type];
var touches = getTouches.call(this, ev, type);
if (!touches) {
return;
}
this.callback(this.manager, type, {
pointers: touches[0],
changedPointers: touches[1],
pointerType: INPUT_TYPE_TOUCH,
srcEvent: ev
});
}
});
/**
* @this {TouchInput}
* @param {Object} ev
* @param {Number} type flag
* @returns {undefined|Array} [all, changed]
*/
function getTouches(ev, type) {
var allTouches = toArray(ev.touches);
var targetIds = this.targetIds;
// when there is only one touch, the process can be simplified
if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
targetIds[allTouches[0].identifier] = true;
return [allTouches, allTouches];
}
var i, targetTouches, changedTouches = toArray(ev.changedTouches), changedTargetTouches = [], target = this.target;
// get target touches from touches
targetTouches = allTouches.filter(function (touch) {
return hasParent(touch.target, target);
});
// collect touches
if (type === INPUT_START) {
i = 0;
while (i < targetTouches.length) {
targetIds[targetTouches[i].identifier] = true;
i++;
}
}
// filter changed touches to only contain touches that exist in the collected target ids
i = 0;
while (i < changedTouches.length) {
if (targetIds[changedTouches[i].identifier]) {
changedTargetTouches.push(changedTouches[i]);
}
// cleanup removed touches
if (type & (INPUT_END | INPUT_CANCEL)) {
delete targetIds[changedTouches[i].identifier];
}
i++;
}
if (!changedTargetTouches.length) {
return;
}
return [
// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
changedTargetTouches
];
}
/**
* Combined touch and mouse input
*
* Touch has a higher priority then mouse, and while touching no mouse events are allowed.
* This because touch devices also emit mouse events while doing a touch.
*
* @constructor
* @extends Input
*/
function TouchMouseInput() {
Input.apply(this, arguments);
var handler = bindFn(this.handler, this);
this.touch = new TouchInput(this.manager, handler);
this.mouse = new MouseInput(this.manager, handler);
}
inherit(TouchMouseInput, Input, {
/**
* handle mouse and touch events
* @param {Hammer} manager
* @param {String} inputEvent
* @param {Object} inputData
*/
handler: function TMEhandler(manager, inputEvent, inputData) {
var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
// when we're in a touch event, so block all upcoming mouse events
// most mobile browser also emit mouseevents, right after touchstart
if (isTouch) {
this.mouse.allow = false;
}
else if (isMouse && !this.mouse.allow) {
return;
}
// reset the allowMouse when we're done
if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
this.mouse.allow = true;
}
this.callback(manager, inputEvent, inputData);
},
/**
* remove the event listeners
*/
destroy: function destroy() {
this.touch.destroy();
this.mouse.destroy();
}
});
var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
// magical touchAction value
var TOUCH_ACTION_COMPUTE = 'compute';
var TOUCH_ACTION_AUTO = 'auto';
var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
var TOUCH_ACTION_NONE = 'none';
var TOUCH_ACTION_PAN_X = 'pan-x';
var TOUCH_ACTION_PAN_Y = 'pan-y';
/**
* Touch Action
* sets the touchAction property or uses the js alternative
* @param {Manager} manager
* @param {String} value
* @constructor
*/
function TouchAction(manager, value) {
this.manager = manager;
this.set(value);
}
TouchAction.prototype = {
/**
* set the touchAction value on the element or enable the polyfill
* @param {String} value
*/
set: function (value) {
// find out the touch-action by the event handlers
if (value == TOUCH_ACTION_COMPUTE) {
value = this.compute();
}
if (NATIVE_TOUCH_ACTION) {
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
},
/**
* just re-set the touchAction value
*/
update: function () {
this.set(this.manager.options.touchAction);
},
/**
* compute the value for the touchAction property based on the recognizer's settings
* @returns {String} value
*/
compute: function () {
var actions = [];
each(this.manager.recognizers, function (recognizer) {
if (boolOrFn(recognizer.options.enable, [recognizer])) {
actions = actions.concat(recognizer.getTouchAction());
}
});
return cleanTouchActions(actions.join(' '));
},
/**
* this method is called on each input cycle and provides the preventing of the browser behavior
* @param {Object} input
*/
preventDefaults: function (input) {
// not needed with native support for the touchAction property
if (NATIVE_TOUCH_ACTION) {
return;
}
var srcEvent = input.srcEvent;
var direction = input.offsetDirection;
// if the touch action did prevented once this session
if (this.manager.session.prevented) {
srcEvent.preventDefault();
return;
}
var actions = this.actions;
var hasNone = inStr(actions, TOUCH_ACTION_NONE);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
if (hasNone ||
(hasPanY && direction & DIRECTION_HORIZONTAL) ||
(hasPanX && direction & DIRECTION_VERTICAL)) {
return this.preventSrc(srcEvent);
}
},
/**
* call preventDefault to prevent the browser's default behavior (scrolling in most cases)
* @param {Object} srcEvent
*/
preventSrc: function (srcEvent) {
this.manager.session.prevented = true;
srcEvent.preventDefault();
}
};
/**
* when the touchActions are collected they are not a valid value, so we need to clean things up. *
* @param {String} actions
* @returns {*}
*/
function cleanTouchActions(actions) {
// none
if (inStr(actions, TOUCH_ACTION_NONE)) {
return TOUCH_ACTION_NONE;
}
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
// pan-x and pan-y can be combined
if (hasPanX && hasPanY) {
return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
}
// pan-x OR pan-y
if (hasPanX || hasPanY) {
return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
}
// manipulation
if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
return TOUCH_ACTION_MANIPULATION;
}
return TOUCH_ACTION_AUTO;
}
/**
* Recognizer flow explained; *
* All recognizers have the initial state of POSSIBLE when a input session starts.
* The definition of a input session is from the first input until the last input, with all it's movement in it. *
* Example session for mouse-input: mousedown -> mousemove -> mouseup
*
* On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
* which determines with state it should be.
*
* If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
* POSSIBLE to give it another change on the next cycle.
*
* Possible
* |
* +-----+---------------+
* | |
* +-----+-----+ |
* | | |
* Failed Cancelled |
* +-------+------+
* | |
* Recognized Began
* |
* Changed
* |
* Ended/Recognized
*/
var STATE_POSSIBLE = 1;
var STATE_BEGAN = 2;
var STATE_CHANGED = 4;
var STATE_ENDED = 8;
var STATE_RECOGNIZED = STATE_ENDED;
var STATE_CANCELLED = 16;
var STATE_FAILED = 32;
/**
* Recognizer
* Every recognizer needs to extend from this class.
* @constructor
* @param {Object} options
*/
function Recognizer(options) {
this.id = uniqueId();
this.manager = null;
this.options = merge(options || {}, this.defaults);
// default is enable true
this.options.enable = ifUndefined(this.options.enable, true);
this.state = STATE_POSSIBLE;
this.simultaneous = {};
this.requireFail = [];
}
Recognizer.prototype = {
/**
* @virtual
* @type {Object}
*/
defaults: {},
/**
* set options
* @param {Object} options
* @return {Recognizer}
*/
set: function (options) {
extend(this.options, options);
// also update the touchAction, in case something changed about the directions/enabled state
this.manager && this.manager.touchAction.update();
return this;
},
/**
* recognize simultaneous with an other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
recognizeWith: function (otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
return this;
}
var simultaneous = this.simultaneous;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (!simultaneous[otherRecognizer.id]) {
simultaneous[otherRecognizer.id] = otherRecognizer;
otherRecognizer.recognizeWith(this);
}
return this;
},
/**
* drop the simultaneous link. it doesnt remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRecognizeWith: function (otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
delete this.simultaneous[otherRecognizer.id];
return this;
},
/**
* recognizer can only run when an other is failing
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
requireFailure: function (otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
return this;
}
var requireFail = this.requireFail;
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
if (inArray(requireFail, otherRecognizer) === -1) {
requireFail.push(otherRecognizer);
otherRecognizer.requireFailure(this);
}
return this;
},
/**
* drop the requireFailure link. it does not remove the link on the other recognizer.
* @param {Recognizer} otherRecognizer
* @returns {Recognizer} this
*/
dropRequireFailure: function (otherRecognizer) {
if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
return this;
}
otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
var index = inArray(this.requireFail, otherRecognizer);
if (index > -1) {
this.requireFail.splice(index, 1);
}
return this;
},
/**
* has require failures boolean
* @returns {boolean}
*/
hasRequireFailures: function () {
return this.requireFail.length > 0;
},
/**
* if the recognizer can recognize simultaneous with an other recognizer
* @param {Recognizer} otherRecognizer
* @returns {Boolean}
*/
canRecognizeWith: function (otherRecognizer) {
return !!this.simultaneous[otherRecognizer.id];
},
/**
* You should use `tryEmit` instead of `emit` directly to check
* that all the needed recognizers has failed before emitting.
* @param {Object} input
*/
emit: function (input) {
var self = this;
var state = this.state;
function emit(withState) {
self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
}
// 'panstart' and 'panmove'
if (state < STATE_ENDED) {
emit(true);
}
emit(); // simple 'eventName' events
// panend and pancancel
if (state >= STATE_ENDED) {
emit(true);
}
},
/**
* Check that all the require failure recognizers has failed,
* if true, it emits a gesture event,
* otherwise, setup the state to FAILED.
* @param {Object} input
*/
tryEmit: function (input) {
if (this.canEmit()) {
return this.emit(input);
}
// it's failing anyway
this.state = STATE_FAILED;
},
/**
* can we emit?
* @returns {boolean}
*/
canEmit: function () {
var i = 0;
while (i < this.requireFail.length) {
if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
return false;
}
i++;
}
return true;
},
/**
* update the recognizer
* @param {Object} inputData
*/
recognize: function (inputData) {
// make a new copy of the inputData
// so we can change the inputData without messing up the other recognizers
var inputDataClone = extend({}, inputData);
// is is enabled and allow recognizing?
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
this.reset();
this.state = STATE_FAILED;
return;
}
// reset when we've reached the end
if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
this.state = STATE_POSSIBLE;
}
this.state = this.process(inputDataClone);
// the recognizer has recognized a gesture
// so trigger an event
if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
this.tryEmit(inputDataClone);
}
},
/**
* return the state of the recognizer
* the actual recognizing happens in this method
* @virtual
* @param {Object} inputData
* @returns {Const} STATE
*/
process: function (inputData) { },
/**
* return the preferred touch-action
* @virtual
* @returns {Array}
*/
getTouchAction: function () { },
/**
* called when the gesture isn't allowed to recognize
* like when another is being recognized or it is disabled
* @virtual
*/
reset: function () { }
};
/**
* get a usable string, used as event postfix
* @param {Const} state
* @returns {String} state
*/
function stateStr(state) {
if (state & STATE_CANCELLED) {
return 'cancel';
}
else if (state & STATE_ENDED) {
return 'end';
}
else if (state & STATE_CHANGED) {
return 'move';
}
else if (state & STATE_BEGAN) {
return 'start';
}
return '';
}
/**
* direction cons to string
* @param {Const} direction
* @returns {String}
*/
function directionStr(direction) {
if (direction == DIRECTION_DOWN) {
return 'down';
}
else if (direction == DIRECTION_UP) {
return 'up';
}
else if (direction == DIRECTION_LEFT) {
return 'left';
}
else if (direction == DIRECTION_RIGHT) {
return 'right';
}
return '';
}
/**
* get a recognizer by name if it is bound to a manager
* @param {Recognizer|String} otherRecognizer
* @param {Recognizer} recognizer
* @returns {Recognizer}
*/
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
var manager = recognizer.manager;
if (manager) {
return manager.get(otherRecognizer);
}
return otherRecognizer;
}
/**
* This recognizer is just used as a base for the simple attribute recognizers.
* @constructor
* @extends Recognizer
*/
function AttrRecognizer() {
Recognizer.apply(this, arguments);
}
inherit(AttrRecognizer, Recognizer, {
/**
* @namespace
* @memberof AttrRecognizer
*/
defaults: {
/**
* @type {Number}
* @default 1
*/
pointers: 1
},
/**
* Used to check if it the recognizer receives valid input, like input.distance > 10.
* @memberof AttrRecognizer
* @param {Object} input
* @returns {Boolean} recognized
*/
attrTest: function (input) {
var optionPointers = this.options.pointers;
return optionPointers === 0 || input.pointers.length === optionPointers;
},
/**
* Process the input and return the state for the recognizer
* @memberof AttrRecognizer
* @param {Object} input
* @returns {*} State
*/
process: function (input) {
var state = this.state;
var eventType = input.eventType;
var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
var isValid = this.attrTest(input);
// on cancel input and we've recognized before, return STATE_CANCELLED
if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
return state | STATE_CANCELLED;
}
else if (isRecognized || isValid) {
if (eventType & INPUT_END) {
return state | STATE_ENDED;
}
else if (!(state & STATE_BEGAN)) {
return STATE_BEGAN;
}
return state | STATE_CHANGED;
}
return STATE_FAILED;
}
});
/**
* Pan
* Recognized when the pointer is down and moved in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
function PanRecognizer() {
AttrRecognizer.apply(this, arguments);
this.pX = null;
this.pY = null;
}
inherit(PanRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof PanRecognizer
*/
defaults: {
event: 'pan',
threshold: 10,
pointers: 1,
direction: DIRECTION_ALL
},
getTouchAction: function () {
var direction = this.options.direction;
var actions = [];
if (direction & DIRECTION_HORIZONTAL) {
actions.push(TOUCH_ACTION_PAN_Y);
}
if (direction & DIRECTION_VERTICAL) {
actions.push(TOUCH_ACTION_PAN_X);
}
return actions;
},
directionTest: function (input) {
var options = this.options;
var hasMoved = true;
var distance = input.distance;
var direction = input.direction;
var x = input.deltaX;
var y = input.deltaY;
// lock to axis?
if (!(direction & options.direction)) {
if (options.direction & DIRECTION_HORIZONTAL) {
direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
hasMoved = x != this.pX;
distance = Math.abs(input.deltaX);
}
else {
direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
hasMoved = y != this.pY;
distance = Math.abs(input.deltaY);
}
}
input.direction = direction;
return hasMoved && distance > options.threshold && direction & options.direction;
},
attrTest: function (input) {
return AttrRecognizer.prototype.attrTest.call(this, input) &&
(this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
},
emit: function (input) {
this.pX = input.deltaX;
this.pY = input.deltaY;
var direction = directionStr(input.direction);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this._super.emit.call(this, input);
}
});
/**
* Pinch
* Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
* @constructor
* @extends AttrRecognizer
*/
function PinchRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(PinchRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof PinchRecognizer
*/
defaults: {
event: 'pinch',
threshold: 0,
pointers: 2
},
getTouchAction: function () {
return [TOUCH_ACTION_NONE];
},
attrTest: function (input) {
return this._super.attrTest.call(this, input) &&
(Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
},
emit: function (input) {
this._super.emit.call(this, input);
if (input.scale !== 1) {
var inOut = input.scale < 1 ? 'in' : 'out';
this.manager.emit(this.options.event + inOut, input);
}
}
});
/**
* Press
* Recognized when the pointer is down for x ms without any movement.
* @constructor
* @extends Recognizer
*/
function PressRecognizer() {
Recognizer.apply(this, arguments);
this._timer = null;
this._input = null;
}
inherit(PressRecognizer, Recognizer, {
/**
* @namespace
* @memberof PressRecognizer
*/
defaults: {
event: 'press',
pointers: 1,
time: 500,
threshold: 5 // a minimal movement is ok, but keep it low
},
getTouchAction: function () {
return [TOUCH_ACTION_AUTO];
},
process: function (input) {
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTime = input.deltaTime > options.time;
this._input = input;
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
this.reset();
}
else if (input.eventType & INPUT_START) {
this.reset();
this._timer = setTimeoutContext(function () {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.time, this);
}
else if (input.eventType & INPUT_END) {
return STATE_RECOGNIZED;
}
return STATE_FAILED;
},
reset: function () {
clearTimeout(this._timer);
},
emit: function (input) {
if (this.state !== STATE_RECOGNIZED) {
return;
}
if (input && (input.eventType & INPUT_END)) {
this.manager.emit(this.options.event + 'up', input);
}
else {
this._input.timeStamp = now();
this.manager.emit(this.options.event, this._input);
}
}
});
/**
* Rotate
* Recognized when two or more pointer are moving in a circular motion.
* @constructor
* @extends AttrRecognizer
*/
function RotateRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(RotateRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof RotateRecognizer
*/
defaults: {
event: 'rotate',
threshold: 0,
pointers: 2
},
getTouchAction: function () {
return [TOUCH_ACTION_NONE];
},
attrTest: function (input) {
return this._super.attrTest.call(this, input) &&
(Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
}
});
/**
* Swipe
* Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
* @constructor
* @extends AttrRecognizer
*/
function SwipeRecognizer() {
AttrRecognizer.apply(this, arguments);
}
inherit(SwipeRecognizer, AttrRecognizer, {
/**
* @namespace
* @memberof SwipeRecognizer
*/
defaults: {
event: 'swipe',
threshold: 10,
velocity: 0.65,
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
pointers: 1
},
getTouchAction: function () {
return PanRecognizer.prototype.getTouchAction.call(this);
},
attrTest: function (input) {
var direction = this.options.direction;
var velocity;
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
velocity = input.velocity;
}
else if (direction & DIRECTION_HORIZONTAL) {
velocity = input.velocityX;
}
else if (direction & DIRECTION_VERTICAL) {
velocity = input.velocityY;
}
return this._super.attrTest.call(this, input) &&
direction & input.direction &&
input.distance > this.options.threshold &&
abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
},
emit: function (input) {
var direction = directionStr(input.direction);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
this.manager.emit(this.options.event, input);
}
});
/**
* A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
* between the given interval and position. The delay option can be used to recognize multi-taps without firing
* a single tap.
*
* The eventData from the emitted event contains the property `tapCount`, which contains the amount of
* multi-taps being recognized.
* @constructor
* @extends Recognizer
*/
function TapRecognizer() {
Recognizer.apply(this, arguments);
// previous time and center,
// used for tap counting
this.pTime = false;
this.pCenter = false;
this._timer = null;
this._input = null;
this.count = 0;
}
inherit(TapRecognizer, Recognizer, {
/**
* @namespace
* @memberof PinchRecognizer
*/
defaults: {
event: 'tap',
pointers: 1,
taps: 1,
interval: 300,
time: 250,
threshold: 2,
posThreshold: 10 // a multi-tap can be a bit off the initial position
},
getTouchAction: function () {
return [TOUCH_ACTION_MANIPULATION];
},
process: function (input) {
var options = this.options;
var validPointers = input.pointers.length === options.pointers;
var validMovement = input.distance < options.threshold;
var validTouchTime = input.deltaTime < options.time;
this.reset();
if ((input.eventType & INPUT_START) && (this.count === 0)) {
return this.failTimeout();
}
// we only allow little movement
// and we've reached an end event, so a tap is possible
if (validMovement && validTouchTime && validPointers) {
if (input.eventType != INPUT_END) {
return this.failTimeout();
}
var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
this.pTime = input.timeStamp;
this.pCenter = input.center;
if (!validMultiTap || !validInterval) {
this.count = 1;
}
else {
this.count += 1;
}
this._input = input;
// if tap count matches we have recognized it,
// else it has began recognizing...
var tapCount = this.count % options.taps;
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
}
else {
this._timer = setTimeoutContext(function () {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
}
return STATE_FAILED;
},
failTimeout: function () {
this._timer = setTimeoutContext(function () {
this.state = STATE_FAILED;
}, this.options.interval, this);
return STATE_FAILED;
},
reset: function () {
clearTimeout(this._timer);
},
emit: function () {
if (this.state == STATE_RECOGNIZED) {
this._input.tapCount = this.count;
this.manager.emit(this.options.event, this._input);
}
}
});
/**
* Simple way to create an manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
function Hammer(element, options) {
options = options || {};
options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
return new Manager(element, options);
}
exports.Hammer = Hammer;
/**
* @const {string}
*/
Hammer.VERSION = '2.0.4';
/**
* default settings
* @namespace
*/
Hammer.defaults = {
/**
* set if DOM events are being triggered.
* But this is slower and unused by simple implementations, so disabled by default.
* @type {Boolean}
* @default false
*/
domEvents: false,
/**
* The value for the touchAction property/fallback.
* When set to `compute` it will magically set the correct value based on the added recognizers.
* @type {String}
* @default compute
*/
touchAction: TOUCH_ACTION_COMPUTE,
/**
* @type {Boolean}
* @default true
*/
enable: true,
/**
* EXPERIMENTAL FEATURE -- can be removed/changed
* Change the parent input target element.
* If Null, then it is being set the to main element.
* @type {Null|EventTarget}
* @default null
*/
inputTarget: null,
/**
* force an input class
* @type {Null|Function}
* @default null
*/
inputClass: null,
/**
* Default recognizer setup when calling `Hammer()`
* When creating a new Manager these will be skipped.
* @type {Array}
*/
preset: [
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
[RotateRecognizer, { enable: false }],
[PinchRecognizer, { enable: false }, ['rotate']],
[SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }],
[PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
[TapRecognizer],
[TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
[PressRecognizer]
],
/**
* Some CSS properties can be used to improve the working of Hammer.
* Add them to this method and they will be set when creating a new Manager.
* @namespace
*/
cssProps: {
/**
* Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userSelect: 'none',
/**
* Disable the Windows Phone grippers when pressing an element.
* @type {String}
* @default 'none'
*/
touchSelect: 'none',
/**
* Disables the default callout shown when you touch and hold a touch target.
* On iOS, when you touch and hold a touch target such as a link, Safari displays
* a callout containing information about the link. This property allows you to disable that callout.
* @type {String}
* @default 'none'
*/
touchCallout: 'none',
/**
* Specifies whether zooming is enabled. Used by IE10>
* @type {String}
* @default 'none'
*/
contentZooming: 'none',
/**
* Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
* @type {String}
* @default 'none'
*/
userDrag: 'none',
/**
* Overrides the highlight color shown when the user taps a link or a JavaScript
* clickable element in iOS. This property obeys the alpha value, if specified.
* @type {String}
* @default 'rgba(0,0,0,0)'
*/
tapHighlightColor: 'rgba(0,0,0,0)'
}
};
var STOP = 1;
var FORCED_STOP = 2;
/**
* Manager
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
*/
function Manager(element, options) {
options = options || {};
this.options = merge(options, Hammer.defaults);
this.options.inputTarget = this.options.inputTarget || element;
this.handlers = {};
this.session = {};
this.recognizers = [];
this.element = element;
this.input = createInputInstance(this);
this.touchAction = new TouchAction(this, this.options.touchAction);
toggleCssProps(this, true);
each(options.recognizers, function (item) {
var recognizer = this.add(new (item[0])(item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
}, this);
}
Manager.prototype = {
/**
* set options
* @param {Object} options
* @returns {Manager}
*/
set: function (options) {
extend(this.options, options);
// Options that need a little more setup
if (options.touchAction) {
this.touchAction.update();
}
if (options.inputTarget) {
// Clean up existing event listeners and reinitialize
this.input.destroy();
this.input.target = options.inputTarget;
this.input.init();
}
return this;
},
/**
* stop recognizing for this session.
* This session will be discarded, when a new [input]start event is fired.
* When forced, the recognizer cycle is stopped immediately.
* @param {Boolean} [force]
*/
stop: function (force) {
this.session.stopped = force ? FORCED_STOP : STOP;
},
/**
* run the recognizers!
* called by the inputHandler function on every movement of the pointers (touches)
* it walks through all the recognizers and tries to detect the gesture that is being made
* @param {Object} inputData
*/
recognize: function (inputData) {
var session = this.session;
if (session.stopped) {
return;
}
// run the touch-action polyfill
this.touchAction.preventDefaults(inputData);
var recognizer;
var recognizers = this.recognizers;
// this holds the recognizer that is being recognized.
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
// if no recognizer is detecting a thing, it is set to `null`
var curRecognizer = session.curRecognizer;
// reset when the last recognizer is recognized
// or when we're in a new session
if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
curRecognizer = session.curRecognizer = null;
}
var i = 0;
while (i < recognizers.length) {
recognizer = recognizers[i];
// find out if we are allowed try to recognize the input for this one.
// 1. allow if the session is NOT forced stopped (see the .stop() method)
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
// that is being recognized.
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
// this can be setup with the `recognizeWith()` method on the recognizer.
if (session.stopped !== FORCED_STOP && (!curRecognizer || recognizer == curRecognizer ||
recognizer.canRecognizeWith(curRecognizer))) {
recognizer.recognize(inputData);
}
else {
recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
// current active recognizer. but only if we don't already have an active recognizer
if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
},
/**
* get a recognizer by its event name.
* @param {Recognizer|String} recognizer
* @returns {Recognizer|Null}
*/
get: function (recognizer) {
if (recognizer instanceof Recognizer) {
return recognizer;
}
var recognizers = this.recognizers;
for (var i = 0; i < recognizers.length; i++) {
if (recognizers[i].options.event == recognizer) {
return recognizers[i];
}
}
return null;
},
/**
* add a recognizer to the manager
* existing recognizers with the same event name will be removed
* @param {Recognizer} recognizer
* @returns {Recognizer|Manager}
*/
add: function (recognizer) {
if (invokeArrayArg(recognizer, 'add', this)) {
return this;
}
// remove existing
var existing = this.get(recognizer.options.event);
if (existing) {
this.remove(existing);
}
this.recognizers.push(recognizer);
recognizer.manager = this;
this.touchAction.update();
return recognizer;
},
/**
* remove a recognizer by name or instance
* @param {Recognizer|String} recognizer
* @returns {Manager}
*/
remove: function (recognizer) {
if (invokeArrayArg(recognizer, 'remove', this)) {
return this;
}
var recognizers = this.recognizers;
recognizer = this.get(recognizer);
recognizers.splice(inArray(recognizers, recognizer), 1);
this.touchAction.update();
return this;
},
/**
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
on: function (events, handler) {
var handlers = this.handlers;
each(splitStr(events), function (event) {
handlers[event] = handlers[event] || [];
handlers[event].push(handler);
});
return this;
},
/**
* unbind event, leave emit blank to remove all handlers
* @param {String} events
* @param {Function} [handler]
* @returns {EventEmitter} this
*/
off: function (events, handler) {
var handlers = this.handlers;
each(splitStr(events), function (event) {
if (!handler) {
delete handlers[event];
}
else {
handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
},
/**
* emit event to the listeners
* @param {String} event
* @param {Object} data
*/
emit: function (event, data) {
// we also want to trigger dom events
if (this.options.domEvents) {
triggerDomEvent(event, data);
}
// no handlers, so skip it all
var handlers = this.handlers[event] && this.handlers[event].slice();
if (!handlers || !handlers.length) {
return;
}
data.type = event;
data.preventDefault = function () {
data.srcEvent.preventDefault();
};
var i = 0;
while (i < handlers.length) {
handlers[i](data);
i++;
}
},
/**
* destroy the manager and unbinds all events
* it doesn't unbind dom events, that is the user own responsibility
*/
destroy: function () {
this.element && toggleCssProps(this, false);
this.handlers = {};
this.session = {};
this.input.destroy();
this.element = null;
}
};
/**
* add/remove the css properties as defined in manager.options.cssProps
* @param {Manager} manager
* @param {Boolean} add
*/
function toggleCssProps(manager, add) {
var element = manager.element;
each(manager.options.cssProps, function (value, name) {
element.style[prefixed(element.style, name)] = add ? value : '';
});
}
/**
* trigger dom event
* @param {String} event
* @param {Object} data
*/
function triggerDomEvent(event, data) {
var gestureEvent = document.createEvent('Event');
gestureEvent.initEvent(event, true, true);
gestureEvent.gesture = data;
data.target.dispatchEvent(gestureEvent);
}
extend(Hammer, {
INPUT_START: INPUT_START,
INPUT_MOVE: INPUT_MOVE,
INPUT_END: INPUT_END,
INPUT_CANCEL: INPUT_CANCEL,
STATE_POSSIBLE: STATE_POSSIBLE,
STATE_BEGAN: STATE_BEGAN,
STATE_CHANGED: STATE_CHANGED,
STATE_ENDED: STATE_ENDED,
STATE_RECOGNIZED: STATE_RECOGNIZED,
STATE_CANCELLED: STATE_CANCELLED,
STATE_FAILED: STATE_FAILED,
DIRECTION_NONE: DIRECTION_NONE,
DIRECTION_LEFT: DIRECTION_LEFT,
DIRECTION_RIGHT: DIRECTION_RIGHT,
DIRECTION_UP: DIRECTION_UP,
DIRECTION_DOWN: DIRECTION_DOWN,
DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
DIRECTION_VERTICAL: DIRECTION_VERTICAL,
DIRECTION_ALL: DIRECTION_ALL,
Manager: Manager,
Input: Input,
TouchAction: TouchAction,
TouchInput: TouchInput,
MouseInput: MouseInput,
PointerEventInput: PointerEventInput,
TouchMouseInput: TouchMouseInput,
SingleTouchInput: SingleTouchInput,
Recognizer: Recognizer,
AttrRecognizer: AttrRecognizer,
Tap: TapRecognizer,
Pan: PanRecognizer,
Swipe: SwipeRecognizer,
Pinch: PinchRecognizer,
Rotate: RotateRecognizer,
Press: PressRecognizer,
on: addEventListeners,
off: removeEventListeners,
each: each,
merge: merge,
extend: extend,
inherit: inherit,
bindFn: bindFn,
prefixed: prefixed
});
// attach to window for angular2 gesture listeners
window.Hammer = Hammer;
/***/ },
/* 35 */
/***/ function(module, exports, __webpack_require__) {
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var core_1 = __webpack_require__(3);
var app_1 = __webpack_require__(6);
var view_controller_1 = __webpack_require__(36);
var navbar_1 = __webpack_require__(40);
var menu_1 = __webpack_require__(26);
/**
* @name MenuToggle
* @description
* Toggle a menu by placing this directive on any item.
* Note that the menu's id must be either `leftMenu` or `rightMenu`
*
* @usage
* ```html
*
*
Page 1
*
*
*
* ```
* @demo /docs/v2/demos/menu/
* @see {@link /docs/v2/components#menus Menu Component Docs}
* @see {@link ../../menu/Menu Menu API Docs}
*/
var MenuToggle = (function () {
function MenuToggle(app, elementRef, viewCtrl, navbar) {
this.app = app;
this.viewCtrl = viewCtrl;
this.withinNavbar = !!navbar;
// Deprecation warning
if (this.withinNavbar && elementRef.nativeElement.tagName === 'A') {
console.warn('Menu toggles within a navbar should use