@angular/material
Version:
Angular Material
1,224 lines (1,218 loc) • 921 kB
JavaScript
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/platform-browser'), require('rxjs/Subject'), require('rxjs/add/operator/debounceTime'), require('@angular/common'), require('rxjs/Observable'), require('rxjs/Subscription'), require('rxjs/add/observable/fromEvent'), require('rxjs/add/observable/merge'), require('rxjs/add/operator/auditTime'), require('rxjs/add/operator/first'), require('rxjs/add/observable/of'), require('@angular/forms'), require('@angular/animations'), require('rxjs/add/operator/startWith'), require('rxjs/add/operator/filter'), require('@angular/http'), require('rxjs/add/observable/forkJoin'), require('rxjs/add/operator/map'), require('rxjs/add/operator/do'), require('rxjs/add/operator/share'), require('rxjs/add/operator/finally'), require('rxjs/add/operator/catch'), require('rxjs/add/observable/throw'), require('rxjs/add/operator/takeUntil'), require('rxjs/add/operator/switchMap')) :
typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/platform-browser', 'rxjs/Subject', 'rxjs/add/operator/debounceTime', '@angular/common', 'rxjs/Observable', 'rxjs/Subscription', 'rxjs/add/observable/fromEvent', 'rxjs/add/observable/merge', 'rxjs/add/operator/auditTime', 'rxjs/add/operator/first', 'rxjs/add/observable/of', '@angular/forms', '@angular/animations', 'rxjs/add/operator/startWith', 'rxjs/add/operator/filter', '@angular/http', 'rxjs/add/observable/forkJoin', 'rxjs/add/operator/map', 'rxjs/add/operator/do', 'rxjs/add/operator/share', 'rxjs/add/operator/finally', 'rxjs/add/operator/catch', 'rxjs/add/observable/throw', 'rxjs/add/operator/takeUntil', 'rxjs/add/operator/switchMap'], factory) :
(factory((global.ng = global.ng || {}, global.ng.material = global.ng.material || {}),global.ng.core,global.ng.platformBrowser,global.Rx,global.Rx.Observable.prototype,global.ng.common,global.Rx,global.Rx,global.Rx.Observable,global.Rx.Observable,global.Rx.Observable.prototype,global.Rx.Observable.prototype,global.Rx.Observable,global.ng.forms,global.ng.animations,global.Rx.Observable.prototype,global.Rx.Observable.prototype,global.ng.http));
}(this, (function (exports,_angular_core,_angular_platformBrowser,rxjs_Subject,rxjs_add_operator_debounceTime,_angular_common,rxjs_Observable,rxjs_Subscription,rxjs_add_observable_fromEvent,rxjs_add_observable_merge,rxjs_add_operator_auditTime,rxjs_add_operator_first,rxjs_add_observable_of,_angular_forms,_angular_animations,rxjs_add_operator_startWith,rxjs_add_operator_filter,_angular_http) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var MATERIAL_COMPATIBILITY_MODE = new _angular_core.InjectionToken('md-compatibility-mode');
/**
* Returns an exception to be thrown if the consumer has used
* an invalid Material prefix on a component.
* \@docs-private
* @param {?} prefix
* @param {?} nodeName
* @return {?}
*/
function getMdCompatibilityInvalidPrefixError(prefix, nodeName) {
return Error("The \"" + prefix + "-\" prefix cannot be used in ng-material v1 compatibility mode. " +
("It was used on an \"" + nodeName.toLowerCase() + "\" element."));
}
/**
* Selector that matches all elements that may have style collisions with AngularJS Material.
*/
var MAT_ELEMENTS_SELECTOR = "\n [mat-button],\n [mat-fab],\n [mat-icon-button],\n [mat-mini-fab],\n [mat-raised-button],\n [matCardSubtitle],\n [matCardTitle],\n [matDialogActions],\n [matDialogClose],\n [matDialogContent],\n [matDialogTitle],\n [matLine],\n [matTabLabel],\n [matTabLink],\n [matTabNav],\n [matTooltip],\n mat-autocomplete,\n mat-button-toggle,\n mat-button-toggle,\n mat-button-toggle-group,\n mat-card,\n mat-card-actions,\n mat-card-content,\n mat-card-footer,\n mat-card-header,\n mat-card-subtitle,\n mat-card-title,\n mat-card-title-group,\n mat-checkbox,\n mat-chip,\n mat-dialog-actions,\n mat-dialog-container,\n mat-dialog-content,\n mat-divider,\n mat-error,\n mat-grid-list,\n mat-grid-tile,\n mat-grid-tile-footer,\n mat-grid-tile-header,\n mat-hint,\n mat-icon,\n mat-list,\n mat-list-item,\n mat-menu,\n mat-nav-list,\n mat-option,\n mat-placeholder,\n mat-progress-bar,\n mat-pseudo-checkbox,\n mat-radio-button,\n mat-radio-group,\n mat-select,\n mat-sidenav,\n mat-sidenav-container,\n mat-slider,\n mat-spinner,\n mat-tab,\n mat-tab-group,\n mat-toolbar";
/**
* Selector that matches all elements that may have style collisions with AngularJS Material.
*/
var MD_ELEMENTS_SELECTOR = "\n [md-button],\n [md-fab],\n [md-icon-button],\n [md-mini-fab],\n [md-raised-button],\n [mdCardSubtitle],\n [mdCardTitle],\n [mdDialogActions],\n [mdDialogClose],\n [mdDialogContent],\n [mdDialogTitle],\n [mdLine],\n [mdTabLabel],\n [mdTabLink],\n [mdTabNav],\n [mdTooltip],\n md-autocomplete,\n md-button-toggle,\n md-button-toggle,\n md-button-toggle-group,\n md-card,\n md-card-actions,\n md-card-content,\n md-card-footer,\n md-card-header,\n md-card-subtitle,\n md-card-title,\n md-card-title-group,\n md-checkbox,\n md-chip,\n md-dialog-actions,\n md-dialog-container,\n md-dialog-content,\n md-divider,\n md-error,\n md-grid-list,\n md-grid-tile,\n md-grid-tile-footer,\n md-grid-tile-header,\n md-hint,\n md-icon,\n md-list,\n md-list-item,\n md-menu,\n md-nav-list,\n md-option,\n md-placeholder,\n md-progress-bar,\n md-pseudo-checkbox,\n md-radio-button,\n md-radio-group,\n md-select,\n md-sidenav,\n md-sidenav-container,\n md-slider,\n md-spinner,\n md-tab,\n md-tab-group,\n md-toolbar";
/**
* Directive that enforces that the `mat-` prefix cannot be used.
*/
var MatPrefixRejector = (function () {
/**
* @param {?} isCompatibilityMode
* @param {?} elementRef
*/
function MatPrefixRejector(isCompatibilityMode, elementRef) {
if (!isCompatibilityMode) {
throw getMdCompatibilityInvalidPrefixError('mat', elementRef.nativeElement.nodeName);
}
}
return MatPrefixRejector;
}());
MatPrefixRejector.decorators = [
{ type: _angular_core.Directive, args: [{ selector: MAT_ELEMENTS_SELECTOR },] },
];
/**
* @nocollapse
*/
MatPrefixRejector.ctorParameters = function () { return [
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [MATERIAL_COMPATIBILITY_MODE,] },] },
{ type: _angular_core.ElementRef, },
]; };
/**
* Directive that enforces that the `md-` prefix cannot be used.
*/
var MdPrefixRejector = (function () {
/**
* @param {?} isCompatibilityMode
* @param {?} elementRef
*/
function MdPrefixRejector(isCompatibilityMode, elementRef) {
if (isCompatibilityMode) {
throw getMdCompatibilityInvalidPrefixError('md', elementRef.nativeElement.nodeName);
}
}
return MdPrefixRejector;
}());
MdPrefixRejector.decorators = [
{ type: _angular_core.Directive, args: [{ selector: MD_ELEMENTS_SELECTOR },] },
];
/**
* @nocollapse
*/
MdPrefixRejector.ctorParameters = function () { return [
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [MATERIAL_COMPATIBILITY_MODE,] },] },
{ type: _angular_core.ElementRef, },
]; };
/**
* Module that enforces the default compatibility mode settings. When this module is loaded
* without NoConflictStyleCompatibilityMode also being imported, it will throw an error if
* there are any uses of the `mat-` prefix.
*/
var CompatibilityModule = (function () {
function CompatibilityModule() {
}
return CompatibilityModule;
}());
CompatibilityModule.decorators = [
{ type: _angular_core.NgModule, args: [{
declarations: [MatPrefixRejector, MdPrefixRejector],
exports: [MatPrefixRejector, MdPrefixRejector],
},] },
];
/**
* @nocollapse
*/
CompatibilityModule.ctorParameters = function () { return []; };
/**
* Module that enforces "no-conflict" compatibility mode settings. When this module is loaded,
* it will throw an error if there are any uses of the `md-` prefix.
*/
var NoConflictStyleCompatibilityMode = (function () {
function NoConflictStyleCompatibilityMode() {
}
return NoConflictStyleCompatibilityMode;
}());
NoConflictStyleCompatibilityMode.decorators = [
{ type: _angular_core.NgModule, args: [{
providers: [{
provide: MATERIAL_COMPATIBILITY_MODE, useValue: true,
}],
},] },
];
/**
* @nocollapse
*/
NoConflictStyleCompatibilityMode.ctorParameters = function () { return []; };
/**
* Injection token that configures whether the Material sanity checks are enabled.
*/
var MATERIAL_SANITY_CHECKS = new _angular_core.InjectionToken('md-sanity-checks');
/**
* Module that captures anything that should be loaded and/or run for *all* Angular Material
* components. This includes Bidi, compatibility mode, etc.
*
* This module should be imported to each top-level component module (e.g., MdTabsModule).
*/
var MdCommonModule = (function () {
/**
* @param {?} _document
* @param {?} _sanityChecksEnabled
*/
function MdCommonModule(_document, _sanityChecksEnabled) {
this._document = _document;
/**
* Whether we've done the global sanity checks (e.g. a theme is loaded, there is a doctype).
*/
this._hasDoneGlobalChecks = false;
if (_sanityChecksEnabled && !this._hasDoneGlobalChecks && _document && _angular_core.isDevMode()) {
this._checkDoctype();
this._checkTheme();
this._hasDoneGlobalChecks = true;
}
}
/**
* @return {?}
*/
MdCommonModule.prototype._checkDoctype = function () {
if (!this._document.doctype) {
console.warn('Current document does not have a doctype. This may cause ' +
'some Angular Material components not to behave as expected.');
}
};
/**
* @return {?}
*/
MdCommonModule.prototype._checkTheme = function () {
if (typeof getComputedStyle === 'function') {
var /** @type {?} */ testElement = this._document.createElement('div');
testElement.classList.add('mat-theme-loaded-marker');
this._document.body.appendChild(testElement);
if (getComputedStyle(testElement).display !== 'none') {
console.warn('Could not find Angular Material core theme. Most Material ' +
'components may not work as expected. For more info refer ' +
'to the theming guide: https://material.angular.io/guide/theming');
}
this._document.body.removeChild(testElement);
}
};
return MdCommonModule;
}());
MdCommonModule.decorators = [
{ type: _angular_core.NgModule, args: [{
imports: [CompatibilityModule],
exports: [CompatibilityModule],
providers: [{
provide: MATERIAL_SANITY_CHECKS, useValue: true,
}],
},] },
];
/**
* @nocollapse
*/
MdCommonModule.ctorParameters = function () { return [
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [_angular_platformBrowser.DOCUMENT,] },] },
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [MATERIAL_SANITY_CHECKS,] },] },
]; };
/**
* Shared directive to count lines inside a text area, such as a list item.
* Line elements can be extracted with a \@ContentChildren(MdLine) query, then
* counted by checking the query list's length.
*/
var MdLine = (function () {
function MdLine() {
}
return MdLine;
}());
MdLine.decorators = [
{ type: _angular_core.Directive, args: [{
selector: '[md-line], [mat-line], [mdLine], [matLine]',
host: { 'class': 'mat-line' }
},] },
];
/**
* @nocollapse
*/
MdLine.ctorParameters = function () { return []; };
/**
* Helper that takes a query list of lines and sets the correct class on the host.
* \@docs-private
*/
var MdLineSetter = (function () {
/**
* @param {?} _lines
* @param {?} _renderer
* @param {?} _element
*/
function MdLineSetter(_lines, _renderer, _element) {
var _this = this;
this._lines = _lines;
this._renderer = _renderer;
this._element = _element;
this._setLineClass(this._lines.length);
this._lines.changes.subscribe(function () {
_this._setLineClass(_this._lines.length);
});
}
/**
* @param {?} count
* @return {?}
*/
MdLineSetter.prototype._setLineClass = function (count) {
this._resetClasses();
if (count === 2 || count === 3) {
this._setClass("mat-" + count + "-line", true);
}
else if (count > 3) {
this._setClass("mat-multi-line", true);
}
};
/**
* @return {?}
*/
MdLineSetter.prototype._resetClasses = function () {
this._setClass('mat-2-line', false);
this._setClass('mat-3-line', false);
this._setClass('mat-multi-line', false);
};
/**
* @param {?} className
* @param {?} isAdd
* @return {?}
*/
MdLineSetter.prototype._setClass = function (className, isAdd) {
if (isAdd) {
this._renderer.addClass(this._element.nativeElement, className);
}
else {
this._renderer.removeClass(this._element.nativeElement, className);
}
};
return MdLineSetter;
}());
var MdLineModule = (function () {
function MdLineModule() {
}
return MdLineModule;
}());
MdLineModule.decorators = [
{ type: _angular_core.NgModule, args: [{
imports: [MdCommonModule],
exports: [MdLine, MdCommonModule],
declarations: [MdLine],
},] },
];
/**
* @nocollapse
*/
MdLineModule.ctorParameters = function () { return []; };
/**
* Directive to listen for changes of direction of part of the DOM.
*
* Applications should use this directive instead of the native attribute so that Material
* components can listen on changes of direction.
*/
var Dir = (function () {
function Dir() {
/**
* Layout direction of the element.
*/
this._dir = 'ltr';
/**
* Event emitted when the direction changes.
*/
this.dirChange = new _angular_core.EventEmitter();
}
Object.defineProperty(Dir.prototype, "dir", {
/**
* \@docs-private
* @return {?}
*/
get: function () {
return this._dir;
},
/**
* @param {?} v
* @return {?}
*/
set: function (v) {
var /** @type {?} */ old = this._dir;
this._dir = v;
if (old != this._dir) {
this.dirChange.emit();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Dir.prototype, "value", {
/**
* Current layout direction of the element.
* @return {?}
*/
get: function () { return this.dir; },
/**
* @param {?} v
* @return {?}
*/
set: function (v) { this.dir = v; },
enumerable: true,
configurable: true
});
return Dir;
}());
Dir.decorators = [
{ type: _angular_core.Directive, args: [{
selector: '[dir]',
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got.
exportAs: '$implicit'
},] },
];
/**
* @nocollapse
*/
Dir.ctorParameters = function () { return []; };
Dir.propDecorators = {
'_dir': [{ type: _angular_core.Input, args: ['dir',] },],
'dirChange': [{ type: _angular_core.Output },],
'dir': [{ type: _angular_core.HostBinding, args: ['attr.dir',] },],
};
var RtlModule = (function () {
function RtlModule() {
}
return RtlModule;
}());
RtlModule.decorators = [
{ type: _angular_core.NgModule, args: [{
exports: [Dir],
declarations: [Dir]
},] },
];
/**
* @nocollapse
*/
RtlModule.ctorParameters = function () { return []; };
/**
* Factory that creates a new MutationObserver and allows us to stub it out in unit tests.
* \@docs-private
*/
var MdMutationObserverFactory = (function () {
function MdMutationObserverFactory() {
}
/**
* @param {?} callback
* @return {?}
*/
MdMutationObserverFactory.prototype.create = function (callback) {
return typeof MutationObserver === 'undefined' ? null : new MutationObserver(callback);
};
return MdMutationObserverFactory;
}());
MdMutationObserverFactory.decorators = [
{ type: _angular_core.Injectable },
];
/**
* @nocollapse
*/
MdMutationObserverFactory.ctorParameters = function () { return []; };
/**
* Directive that triggers a callback whenever the content of
* its associated element has changed.
*/
var ObserveContent = (function () {
/**
* @param {?} _mutationObserverFactory
* @param {?} _elementRef
*/
function ObserveContent(_mutationObserverFactory, _elementRef) {
this._mutationObserverFactory = _mutationObserverFactory;
this._elementRef = _elementRef;
/**
* Event emitted for each change in the element's content.
*/
this.event = new _angular_core.EventEmitter();
/**
* Used for debouncing the emitted values to the observeContent event.
*/
this._debouncer = new rxjs_Subject.Subject();
}
/**
* @return {?}
*/
ObserveContent.prototype.ngAfterContentInit = function () {
var _this = this;
if (this.debounce > 0) {
this._debouncer
.debounceTime(this.debounce)
.subscribe(function (mutations) { return _this.event.emit(mutations); });
}
else {
this._debouncer.subscribe(function (mutations) { return _this.event.emit(mutations); });
}
this._observer = this._mutationObserverFactory.create(function (mutations) {
_this._debouncer.next(mutations);
});
if (this._observer) {
this._observer.observe(this._elementRef.nativeElement, {
characterData: true,
childList: true,
subtree: true
});
}
};
/**
* @return {?}
*/
ObserveContent.prototype.ngOnDestroy = function () {
if (this._observer) {
this._observer.disconnect();
this._debouncer.complete();
this._debouncer = this._observer = null;
}
};
return ObserveContent;
}());
ObserveContent.decorators = [
{ type: _angular_core.Directive, args: [{
selector: '[cdkObserveContent]'
},] },
];
/**
* @nocollapse
*/
ObserveContent.ctorParameters = function () { return [
{ type: MdMutationObserverFactory, },
{ type: _angular_core.ElementRef, },
]; };
ObserveContent.propDecorators = {
'event': [{ type: _angular_core.Output, args: ['cdkObserveContent',] },],
'debounce': [{ type: _angular_core.Input },],
};
var ObserveContentModule = (function () {
function ObserveContentModule() {
}
return ObserveContentModule;
}());
ObserveContentModule.decorators = [
{ type: _angular_core.NgModule, args: [{
exports: [ObserveContent],
declarations: [ObserveContent],
providers: [MdMutationObserverFactory]
},] },
];
/**
* @nocollapse
*/
ObserveContentModule.ctorParameters = function () { return []; };
var RippleState = {};
RippleState.FADING_IN = 0;
RippleState.VISIBLE = 1;
RippleState.FADING_OUT = 2;
RippleState.HIDDEN = 3;
RippleState[RippleState.FADING_IN] = "FADING_IN";
RippleState[RippleState.VISIBLE] = "VISIBLE";
RippleState[RippleState.FADING_OUT] = "FADING_OUT";
RippleState[RippleState.HIDDEN] = "HIDDEN";
/**
* Reference to a previously launched ripple element.
*/
var RippleRef = (function () {
/**
* @param {?} _renderer
* @param {?} element
* @param {?} config
*/
function RippleRef(_renderer, element, config) {
this._renderer = _renderer;
this.element = element;
this.config = config;
/**
* Current state of the ripple reference.
*/
this.state = RippleState.HIDDEN;
}
/**
* Fades out the ripple element.
* @return {?}
*/
RippleRef.prototype.fadeOut = function () {
this._renderer.fadeOutRipple(this);
};
return RippleRef;
}());
/**
* Fade-in duration for the ripples. Can be modified with the speedFactor option.
*/
var RIPPLE_FADE_IN_DURATION = 450;
/**
* Fade-out duration for the ripples in milliseconds. This can't be modified by the speedFactor.
*/
var RIPPLE_FADE_OUT_DURATION = 400;
/**
* Helper service that performs DOM manipulations. Not intended to be used outside this module.
* The constructor takes a reference to the ripple directive's host element and a map of DOM
* event handlers to be installed on the element that triggers ripple animations.
* This will eventually become a custom renderer once Angular support exists.
* \@docs-private
*/
var RippleRenderer = (function () {
/**
* @param {?} elementRef
* @param {?} _ngZone
* @param {?} _ruler
* @param {?} platform
*/
function RippleRenderer(elementRef, _ngZone, _ruler, platform) {
this._ngZone = _ngZone;
this._ruler = _ruler;
/**
* Whether the mouse is currently down or not.
*/
this._isMousedown = false;
/**
* Events to be registered on the trigger element.
*/
this._triggerEvents = new Map();
/**
* Set of currently active ripple references.
*/
this._activeRipples = new Set();
/**
* Ripple config for all ripples created by events.
*/
this.rippleConfig = {};
/**
* Whether mouse ripples should be created or not.
*/
this.rippleDisabled = false;
// Only do anything if we're on the browser.
if (platform.isBrowser) {
this._containerElement = elementRef.nativeElement;
// Specify events which need to be registered on the trigger.
this._triggerEvents.set('mousedown', this.onMousedown.bind(this));
this._triggerEvents.set('mouseup', this.onMouseup.bind(this));
this._triggerEvents.set('mouseleave', this.onMouseLeave.bind(this));
// By default use the host element as trigger element.
this.setTriggerElement(this._containerElement);
}
}
/**
* Fades in a ripple at the given coordinates.
* @param {?} pageX
* @param {?} pageY
* @param {?=} config
* @return {?}
*/
RippleRenderer.prototype.fadeInRipple = function (pageX, pageY, config) {
var _this = this;
if (config === void 0) { config = {}; }
var /** @type {?} */ containerRect = this._containerElement.getBoundingClientRect();
if (config.centered) {
pageX = containerRect.left + containerRect.width / 2;
pageY = containerRect.top + containerRect.height / 2;
}
else {
// Subtract scroll values from the coordinates because calculations below
// are always relative to the viewport rectangle.
var /** @type {?} */ scrollPosition = this._ruler.getViewportScrollPosition();
pageX -= scrollPosition.left;
pageY -= scrollPosition.top;
}
var /** @type {?} */ radius = config.radius || distanceToFurthestCorner(pageX, pageY, containerRect);
var /** @type {?} */ duration = RIPPLE_FADE_IN_DURATION * (1 / (config.speedFactor || 1));
var /** @type {?} */ offsetX = pageX - containerRect.left;
var /** @type {?} */ offsetY = pageY - containerRect.top;
var /** @type {?} */ ripple = document.createElement('div');
ripple.classList.add('mat-ripple-element');
ripple.style.left = offsetX - radius + "px";
ripple.style.top = offsetY - radius + "px";
ripple.style.height = radius * 2 + "px";
ripple.style.width = radius * 2 + "px";
// If the color is not set, the default CSS color will be used.
ripple.style.backgroundColor = config.color;
ripple.style.transitionDuration = duration + "ms";
this._containerElement.appendChild(ripple);
// By default the browser does not recalculate the styles of dynamically created
// ripple elements. This is critical because then the `scale` would not animate properly.
enforceStyleRecalculation(ripple);
ripple.style.transform = 'scale(1)';
// Exposed reference to the ripple that will be returned.
var /** @type {?} */ rippleRef = new RippleRef(this, ripple, config);
rippleRef.state = RippleState.FADING_IN;
// Add the ripple reference to the list of all active ripples.
this._activeRipples.add(rippleRef);
// Wait for the ripple element to be completely faded in.
// Once it's faded in, the ripple can be hidden immediately if the mouse is released.
this.runTimeoutOutsideZone(function () {
rippleRef.state = RippleState.VISIBLE;
if (!config.persistent && !_this._isMousedown) {
rippleRef.fadeOut();
}
}, duration);
return rippleRef;
};
/**
* Fades out a ripple reference.
* @param {?} rippleRef
* @return {?}
*/
RippleRenderer.prototype.fadeOutRipple = function (rippleRef) {
// For ripples that are not active anymore, don't re-un the fade-out animation.
if (!this._activeRipples.delete(rippleRef)) {
return;
}
var /** @type {?} */ rippleEl = rippleRef.element;
rippleEl.style.transitionDuration = RIPPLE_FADE_OUT_DURATION + "ms";
rippleEl.style.opacity = '0';
rippleRef.state = RippleState.FADING_OUT;
// Once the ripple faded out, the ripple can be safely removed from the DOM.
this.runTimeoutOutsideZone(function () {
rippleRef.state = RippleState.HIDDEN;
rippleEl.parentNode.removeChild(rippleEl);
}, RIPPLE_FADE_OUT_DURATION);
};
/**
* Fades out all currently active ripples.
* @return {?}
*/
RippleRenderer.prototype.fadeOutAll = function () {
this._activeRipples.forEach(function (ripple) { return ripple.fadeOut(); });
};
/**
* Sets the trigger element and registers the mouse events.
* @param {?} element
* @return {?}
*/
RippleRenderer.prototype.setTriggerElement = function (element) {
var _this = this;
// Remove all previously register event listeners from the trigger element.
if (this._triggerElement) {
this._triggerEvents.forEach(function (fn, type) { return _this._triggerElement.removeEventListener(type, fn); });
}
if (element) {
// If the element is not null, register all event listeners on the trigger element.
this._ngZone.runOutsideAngular(function () {
_this._triggerEvents.forEach(function (fn, type) { return element.addEventListener(type, fn); });
});
}
this._triggerElement = element;
};
/**
* Listener being called on mousedown event.
* @param {?} event
* @return {?}
*/
RippleRenderer.prototype.onMousedown = function (event) {
if (!this.rippleDisabled) {
this._isMousedown = true;
this.fadeInRipple(event.pageX, event.pageY, this.rippleConfig);
}
};
/**
* Listener being called on mouseup event.
* @return {?}
*/
RippleRenderer.prototype.onMouseup = function () {
this._isMousedown = false;
// Fade-out all ripples that are completely visible and not persistent.
this._activeRipples.forEach(function (ripple) {
if (!ripple.config.persistent && ripple.state === RippleState.VISIBLE) {
ripple.fadeOut();
}
});
};
/**
* Listener being called on mouseleave event.
* @return {?}
*/
RippleRenderer.prototype.onMouseLeave = function () {
if (this._isMousedown) {
this.onMouseup();
}
};
/**
* Runs a timeout outside of the Angular zone to avoid triggering the change detection.
* @param {?} fn
* @param {?=} delay
* @return {?}
*/
RippleRenderer.prototype.runTimeoutOutsideZone = function (fn, delay) {
if (delay === void 0) { delay = 0; }
this._ngZone.runOutsideAngular(function () { return setTimeout(fn, delay); });
};
return RippleRenderer;
}());
/**
* @param {?} element
* @return {?}
*/
function enforceStyleRecalculation(element) {
// Enforce a style recalculation by calling `getComputedStyle` and accessing any property.
// Calling `getPropertyValue` is important to let optimizers know that this is not a noop.
// See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
window.getComputedStyle(element).getPropertyValue('opacity');
}
/**
* Returns the distance from the point (x, y) to the furthest corner of a rectangle.
* @param {?} x
* @param {?} y
* @param {?} rect
* @return {?}
*/
function distanceToFurthestCorner(x, y, rect) {
var /** @type {?} */ distX = Math.max(Math.abs(x - rect.left), Math.abs(x - rect.right));
var /** @type {?} */ distY = Math.max(Math.abs(y - rect.top), Math.abs(y - rect.bottom));
return Math.sqrt(distX * distX + distY * distY);
}
// Whether the current platform supports the V8 Break Iterator. The V8 check
// is necessary to detect all Blink based browsers.
var hasV8BreakIterator = (typeof (Intl) !== 'undefined' && ((Intl)).v8BreakIterator);
/**
* Service to detect the current platform by comparing the userAgent strings and
* checking browser-specific global properties.
* \@docs-private
*/
var Platform = (function () {
function Platform() {
this.isBrowser = typeof document === 'object' && !!document;
/**
* Layout Engines
*/
this.EDGE = this.isBrowser && /(edge)/i.test(navigator.userAgent);
this.TRIDENT = this.isBrowser && /(msie|trident)/i.test(navigator.userAgent);
// EdgeHTML and Trident mock Blink specific things and need to be excluded from this check.
this.BLINK = this.isBrowser &&
(!!(((window)).chrome || hasV8BreakIterator) && !!CSS && !this.EDGE && !this.TRIDENT);
// Webkit is part of the userAgent in EdgeHTML, Blink and Trident. Therefore we need to
// ensure that Webkit runs standalone and is not used as another engine's base.
this.WEBKIT = this.isBrowser &&
/AppleWebKit/i.test(navigator.userAgent) && !this.BLINK && !this.EDGE && !this.TRIDENT;
/**
* Browsers and Platform Types
*/
this.IOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
// It's difficult to detect the plain Gecko engine, because most of the browsers identify
// them self as Gecko-like browsers and modify the userAgent's according to that.
// Since we only cover one explicit Firefox case, we can simply check for Firefox
// instead of having an unstable check for Gecko.
this.FIREFOX = this.isBrowser && /(firefox|minefield)/i.test(navigator.userAgent);
// Trident on mobile adds the android platform to the userAgent to trick detections.
this.ANDROID = this.isBrowser && /android/i.test(navigator.userAgent) && !this.TRIDENT;
// Safari browsers will include the Safari keyword in their userAgent. Some browsers may fake
// this and just place the Safari keyword in the userAgent. To be more safe about Safari every
// Safari browser should also use Webkit as its layout engine.
this.SAFARI = this.isBrowser && /safari/i.test(navigator.userAgent) && this.WEBKIT;
}
return Platform;
}());
Platform.decorators = [
{ type: _angular_core.Injectable },
];
/**
* @nocollapse
*/
Platform.ctorParameters = function () { return []; };
/**
* Cached result Set of input types support by the current browser.
*/
var supportedInputTypes;
/**
* Types of <input> that *might* be supported.
*/
var candidateInputTypes = [
// `color` must come first. Chrome 56 shows a warning if we change the type to `color` after
// first changing it to something else:
// The specified value "" does not conform to the required format.
// The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers.
'color',
'button',
'checkbox',
'date',
'datetime-local',
'email',
'file',
'hidden',
'image',
'month',
'number',
'password',
'radio',
'range',
'reset',
'search',
'submit',
'tel',
'text',
'time',
'url',
'week',
];
/**
* @return {?} The input types supported by this browser.
*/
function getSupportedInputTypes() {
// Result is cached.
if (supportedInputTypes) {
return supportedInputTypes;
}
// We can't check if an input type is not supported until we're on the browser, so say that
// everything is supported when not on the browser. We don't use `Platform` here since it's
// just a helper function and can't inject it.
if (typeof document !== 'object' || !document) {
supportedInputTypes = new Set(candidateInputTypes);
return supportedInputTypes;
}
var /** @type {?} */ featureTestInput = document.createElement('input');
supportedInputTypes = new Set(candidateInputTypes.filter(function (value) {
featureTestInput.setAttribute('type', value);
return featureTestInput.type === value;
}));
return supportedInputTypes;
}
var PlatformModule = (function () {
function PlatformModule() {
}
return PlatformModule;
}());
PlatformModule.decorators = [
{ type: _angular_core.NgModule, args: [{
providers: [Platform]
},] },
];
/**
* @nocollapse
*/
PlatformModule.ctorParameters = function () { return []; };
/**
* Time in ms to throttle the scrolling events by default.
*/
var DEFAULT_SCROLL_TIME = 20;
/**
* Service contained all registered Scrollable references and emits an event when any one of the
* Scrollable references emit a scrolled event.
*/
var ScrollDispatcher = (function () {
/**
* @param {?} _ngZone
* @param {?} _platform
*/
function ScrollDispatcher(_ngZone, _platform) {
this._ngZone = _ngZone;
this._platform = _platform;
/**
* Subject for notifying that a registered scrollable reference element has been scrolled.
*/
this._scrolled = new rxjs_Subject.Subject();
/**
* Keeps track of the global `scroll` and `resize` subscriptions.
*/
this._globalSubscription = null;
/**
* Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards.
*/
this._scrolledCount = 0;
/**
* Map of all the scrollable references that are registered with the service and their
* scroll event subscriptions.
*/
this.scrollableReferences = new Map();
}
/**
* Registers a Scrollable with the service and listens for its scrolled events. When the
* scrollable is scrolled, the service emits the event in its scrolled observable.
* @param {?} scrollable Scrollable instance to be registered.
* @return {?}
*/
ScrollDispatcher.prototype.register = function (scrollable) {
var _this = this;
var /** @type {?} */ scrollSubscription = scrollable.elementScrolled().subscribe(function () { return _this._notify(); });
this.scrollableReferences.set(scrollable, scrollSubscription);
};
/**
* Deregisters a Scrollable reference and unsubscribes from its scroll event observable.
* @param {?} scrollable Scrollable instance to be deregistered.
* @return {?}
*/
ScrollDispatcher.prototype.deregister = function (scrollable) {
if (this.scrollableReferences.has(scrollable)) {
this.scrollableReferences.get(scrollable).unsubscribe();
this.scrollableReferences.delete(scrollable);
}
};
/**
* Subscribes to an observable that emits an event whenever any of the registered Scrollable
* references (or window, document, or body) fire a scrolled event. Can provide a time in ms
* to override the default "throttle" time.
* @param {?=} auditTimeInMs
* @param {?=} callback
* @return {?}
*/
ScrollDispatcher.prototype.scrolled = function (auditTimeInMs, callback) {
var _this = this;
if (auditTimeInMs === void 0) { auditTimeInMs = DEFAULT_SCROLL_TIME; }
// Scroll events can only happen on the browser, so do nothing if we're not on the browser.
if (!this._platform.isBrowser) {
return rxjs_Subscription.Subscription.EMPTY;
}
// In the case of a 0ms delay, use an observable without auditTime
// since it does add a perceptible delay in processing overhead.
var /** @type {?} */ observable = auditTimeInMs > 0 ?
this._scrolled.asObservable().auditTime(auditTimeInMs) :
this._scrolled.asObservable();
this._scrolledCount++;
if (!this._globalSubscription) {
this._globalSubscription = this._ngZone.runOutsideAngular(function () {
return rxjs_Observable.Observable.merge(rxjs_Observable.Observable.fromEvent(window.document, 'scroll'), rxjs_Observable.Observable.fromEvent(window, 'resize')).subscribe(function () { return _this._notify(); });
});
}
// Note that we need to do the subscribing from here, in order to be able to remove
// the global event listeners once there are no more subscriptions.
var /** @type {?} */ subscription = observable.subscribe(callback);
subscription.add(function () {
_this._scrolledCount--;
if (_this._globalSubscription && !_this.scrollableReferences.size && !_this._scrolledCount) {
_this._globalSubscription.unsubscribe();
_this._globalSubscription = null;
}
});
return subscription;
};
/**
* Returns all registered Scrollables that contain the provided element.
* @param {?} elementRef
* @return {?}
*/
ScrollDispatcher.prototype.getScrollContainers = function (elementRef) {
var _this = this;
var /** @type {?} */ scrollingContainers = [];
this.scrollableReferences.forEach(function (_subscription, scrollable) {
if (_this.scrollableContainsElement(scrollable, elementRef)) {
scrollingContainers.push(scrollable);
}
});
return scrollingContainers;
};
/**
* Returns true if the element is contained within the provided Scrollable.
* @param {?} scrollable
* @param {?} elementRef
* @return {?}
*/
ScrollDispatcher.prototype.scrollableContainsElement = function (scrollable, elementRef) {
var /** @type {?} */ element = elementRef.nativeElement;
var /** @type {?} */ scrollableElement = scrollable.getElementRef().nativeElement;
// Traverse through the element parents until we reach null, checking if any of the elements
// are the scrollable's element.
do {
if (element == scrollableElement) {
return true;
}
} while (element = element.parentElement);
};
/**
* Sends a notification that a scroll event has been fired.
* @return {?}
*/
ScrollDispatcher.prototype._notify = function () {
this._scrolled.next();
};
return ScrollDispatcher;
}());
ScrollDispatcher.decorators = [
{ type: _angular_core.Injectable },
];
/**
* @nocollapse
*/
ScrollDispatcher.ctorParameters = function () { return [
{ type: _angular_core.NgZone, },
{ type: Platform, },
]; };
/**
* @param {?} parentDispatcher
* @param {?} ngZone
* @param {?} platform
* @return {?}
*/
function SCROLL_DISPATCHER_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
return parentDispatcher || new ScrollDispatcher(ngZone, platform);
}
var SCROLL_DISPATCHER_PROVIDER = {
// If there is already a ScrollDispatcher available, use that. Otherwise, provide a new one.
provide: ScrollDispatcher,
deps: [[new _angular_core.Optional(), new _angular_core.SkipSelf(), ScrollDispatcher], _angular_core.NgZone, Platform],
useFactory: SCROLL_DISPATCHER_PROVIDER_FACTORY
};
/**
* Simple utility for getting the bounds of the browser viewport.
* \@docs-private
*/
var ViewportRuler = (function () {
/**
* @param {?} scrollDispatcher
*/
function ViewportRuler(scrollDispatcher) {
var _this = this;
// Subscribe to scroll and resize events and update the document rectangle on changes.
scrollDispatcher.scrolled(null, function () { return _this._cacheViewportGeometry(); });
}
/**
* Gets a ClientRect for the viewport's bounds.
* @param {?=} documentRect
* @return {?}
*/
ViewportRuler.prototype.getViewportRect = function (documentRect) {
if (documentRect === void 0) { documentRect = this._documentRect; }
// Cache the document bounding rect so that we don't recompute it for multiple calls.
if (!documentRect) {
this._cacheViewportGeometry();
documentRect = this._documentRect;
}
// Use the document element's bounding rect rather than the window scroll properties
// (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll
// properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different
// conceptual viewports. Under most circumstances these viewports are equivalent, but they
// can disagree when the page is pinch-zoomed (on devices that support touch).
// See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4
// We use the documentElement instead of the body because, by default (without a css reset)
// browsers typically give the document body an 8px margin, which is not included in
// getBoundingClientRect().
var /** @type {?} */ scrollPosition = this.getViewportScrollPosition(documentRect);
var /** @type {?} */ height = window.innerHeight;
var /** @type {?} */ width = window.innerWidth;
return {
top: scrollPosition.top,
left: scrollPosition.left,
bottom: scrollPosition.top + height,
right: scrollPosition.left + width,
height: height,
width: width,
};
};
/**
* Gets the (top, left) scroll position of the viewport.
* @param {?=} documentRect
* @return {?}
*/
ViewportRuler.prototype.getViewportScrollPosition = function (documentRect) {
if (documentRect === void 0) { documentRect = this._documentRect; }
// Cache the document bounding rect so that we don't recompute it for multiple calls.
if (!documentRect) {
this._cacheViewportGeometry();
documentRect = this._documentRect;
}
// The top-left-corner of the viewport is determined by the scroll position of the document
// body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about
// whether `document.body` or `document.documentElement` is the scrolled element, so reading
// `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of
// `document.documentElement` works consistently, where the `top` and `left` values will
// equal negative the scroll position.
var /** @type {?} */ top = -documentRect.top || document.body.scrollTop || window.scrollY ||
document.documentElement.scrollTop || 0;
var /** @type {?} */ left = -documentRect.left || document.body.scrollLeft || window.scrollX ||
document.documentElement.scrollLeft || 0;
return { top: top, left: left };
};
/**
* Caches the latest client rectangle of the document element.
* @return {?}
*/
ViewportRuler.prototype._cacheViewportGeometry = function () {
this._documentRect = document.documentElement.getBoundingClientRect();
};
return ViewportRuler;
}());
ViewportRuler.decorators = [
{ type: _angular_core.Injectable },
];
/**
* @nocollapse
*/
ViewportRuler.ctorParameters = function () { return [
{ type: ScrollDispatcher, },
]; };
/**
* @param {?} parentRuler
* @param {?} scrollDispatcher
* @return {?}
*/
function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler, scrollDispatcher) {
return parentRuler || new ViewportRuler(scrollDispatcher);
}
var VIEWPORT_RULER_PROVIDER = {
// If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
provide: ViewportRuler,
deps: [[new _angular_core.Optional(), new _angular_core.SkipSelf(), ViewportRuler], ScrollDispatcher],
useFactory: VIEWPORT_RULER_PROVIDER_FACTORY
};
/**
* Injection token that can be used to specify the global ripple options.
*/
var MD_RIPPLE_GLOBAL_OPTIONS = new _angular_core.InjectionToken('md-ripple-global-options');
var MdRipple = (function () {
/**
* @param {?} elementRef
* @param {?} ngZone
* @param {?} ruler
* @param {?} platform
* @param {?} globalOptions
*/
function MdRipple(elementRef, ngZone, ruler, platform, globalOptions) {
/**
* If set, the radius in pixels of foreground ripples when fully expanded. If unset, the radius
* will be the distance from the center of the ripple to the furthest corner of the host element's
* bounding rectangle.
*/
this.radius = 0;
/**
* If set, the normal duration of ripple animations is divided by this value. For example,
* setting it to 0.5 will cause the animations to take twice as long.
* A changed speedFactor will not modify the fade-out duration of the ripples.
*/
this.speedFactor = 1;
this._rippleRenderer = new RippleRenderer(elementRef, ngZone, ruler, platform);
this._globalOptions = globalOptions ? globalOptions : {};
this._updateRippleRenderer();
}
/**
* @param {?} changes
* @return {?}
*/
MdRipple.prototype.ngOnChanges = function (changes) {
if (changes['trigger'] && this.trigger) {
this._rippleRenderer.setTriggerElement(this.trigger);
}
this._updateRippleRenderer();
};
/**
* @return {?}
*/
MdRipple.prototype.ngOnDestroy = function () {
// Set the trigger element to null to cleanup all listeners.
this._rippleRenderer.setTriggerElement(null);
};
/**
* Launches a manual ripple at the specified position.
* @param {?} pageX
* @param {?} pageY
* @param {?=} config
* @return {?}
*/
MdRipple.prototype.launch = function (pageX, pageY, config) {
if (config === void 0) { config = this.rippleConfig; }
return this._rippleRenderer.fadeInRipple(pageX, pageY, config);
};
/**
* Fades out all currently showing ripple elements.
* @return {?}
*/
MdRipple.prototype.fadeOutAll = function () {
this._rippleRenderer.fadeOutAll();
};
Object.defineProperty(MdRipple.prototype, "rippleConfig", {
/**
* Ripple configuration from the directive's input values.
* @return {?}
*/
get: function () {
return {
centered: this.centered,
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor |