md2
Version:
Angular2 based Material Design components, directives and services are Accordion, Autocomplete, Chips(Tags), Collapse, Colorpicker, Data Table, Datepicker, Dialog(Modal), Menu, Multiselect, Select, Tabs, Tags(Chips), Toast and Tooltip.
1,182 lines (1,167 loc) • 581 kB
JavaScript
/**
* @license Md2 v0.0.33
* Copyright (c) 2017 Promact, Inc. http://code.promactinfo.com/md2/
* License: MIT
*/
import { ApplicationRef, Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ContentChildren, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Inject, Injectable, InjectionToken, Injector, Input, IterableDiffers, NgModule, NgZone, Optional, Output, Pipe, QueryList, Renderer, Renderer2, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation, forwardRef, isDevMode } from '@angular/core';
import { DOCUMENT, HammerGestureConfig } from '@angular/platform-browser';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/auditTime';
import 'rxjs/add/operator/first';
import 'rxjs/add/observable/of';
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
import { FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, NgForm, Validators } from '@angular/forms';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/filter';
var __decorate$3 = (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$1 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param$1 = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
const MATERIAL_COMPATIBILITY_MODE = new InjectionToken('md-compatibility-mode');
/**
* Returns an exception to be thrown if the consumer has used
* an invalid Material prefix on a component.
* @docs-private
*/
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. */
const MAT_ELEMENTS_SELECTOR = `
[mat-button],
[mat-fab],
[mat-icon-button],
[mat-mini-fab],
[mat-raised-button],
[matCardSubtitle],
[matCardTitle],
[matDialogActions],
[matDialogClose],
[matDialogContent],
[matDialogTitle],
[matLine],
[matTabLabel],
[matTabLink],
[matTabNav],
[matTooltip],
mat-autocomplete,
mat-button-toggle,
mat-button-toggle,
mat-button-toggle-group,
mat-card,
mat-card-actions,
mat-card-content,
mat-card-footer,
mat-card-header,
mat-card-subtitle,
mat-card-title,
mat-card-title-group,
mat-checkbox,
mat-chip,
mat-dialog-actions,
mat-dialog-container,
mat-dialog-content,
mat-divider,
mat-error,
mat-grid-list,
mat-grid-tile,
mat-grid-tile-footer,
mat-grid-tile-header,
mat-hint,
mat-icon,
mat-list,
mat-list-item,
mat-menu,
mat-nav-list,
mat-option,
mat-placeholder,
mat-progress-bar,
mat-pseudo-checkbox,
mat-radio-button,
mat-radio-group,
mat-select,
mat-sidenav,
mat-sidenav-container,
mat-slider,
mat-spinner,
mat-tab,
mat-tab-group,
mat-toolbar`;
/** Selector that matches all elements that may have style collisions with AngularJS Material. */
const MD_ELEMENTS_SELECTOR = `
[md-button],
[md-fab],
[md-icon-button],
[md-mini-fab],
[md-raised-button],
[mdCardSubtitle],
[mdCardTitle],
[mdDialogActions],
[mdDialogClose],
[mdDialogContent],
[mdDialogTitle],
[mdLine],
[mdTabLabel],
[mdTabLink],
[mdTabNav],
[mdTooltip],
md-autocomplete,
md-button-toggle,
md-button-toggle,
md-button-toggle-group,
md-card,
md-card-actions,
md-card-content,
md-card-footer,
md-card-header,
md-card-subtitle,
md-card-title,
md-card-title-group,
md-checkbox,
md-chip,
md-dialog-actions,
md-dialog-container,
md-dialog-content,
md-divider,
md-error,
md-grid-list,
md-grid-tile,
md-grid-tile-footer,
md-grid-tile-header,
md-hint,
md-icon,
md-list,
md-list-item,
md-menu,
md-nav-list,
md-option,
md-placeholder,
md-progress-bar,
md-pseudo-checkbox,
md-radio-button,
md-radio-group,
md-select,
md-sidenav,
md-sidenav-container,
md-slider,
md-spinner,
md-tab,
md-tab-group,
md-toolbar`;
/** Directive that enforces that the `mat-` prefix cannot be used. */
let MatPrefixRejector = class MatPrefixRejector {
constructor(isCompatibilityMode, elementRef) {
if (!isCompatibilityMode) {
throw getMdCompatibilityInvalidPrefixError('mat', elementRef.nativeElement.nodeName);
}
}
};
MatPrefixRejector = __decorate$3([
Directive({ selector: MAT_ELEMENTS_SELECTOR }),
__param$1(0, Optional()), __param$1(0, Inject(MATERIAL_COMPATIBILITY_MODE)),
__metadata$1("design:paramtypes", [Boolean, ElementRef])
], MatPrefixRejector);
/** Directive that enforces that the `md-` prefix cannot be used. */
let MdPrefixRejector = class MdPrefixRejector {
constructor(isCompatibilityMode, elementRef) {
if (isCompatibilityMode) {
throw getMdCompatibilityInvalidPrefixError('md', elementRef.nativeElement.nodeName);
}
}
};
MdPrefixRejector = __decorate$3([
Directive({ selector: MD_ELEMENTS_SELECTOR }),
__param$1(0, Optional()), __param$1(0, Inject(MATERIAL_COMPATIBILITY_MODE)),
__metadata$1("design:paramtypes", [Boolean, ElementRef])
], MdPrefixRejector);
/**
* 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.
*/
let CompatibilityModule = class CompatibilityModule {
};
CompatibilityModule = __decorate$3([
NgModule({
declarations: [MatPrefixRejector, MdPrefixRejector],
exports: [MatPrefixRejector, MdPrefixRejector],
})
], CompatibilityModule);
/**
* 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.
*/
let NoConflictStyleCompatibilityMode = class NoConflictStyleCompatibilityMode {
};
NoConflictStyleCompatibilityMode = __decorate$3([
NgModule({
providers: [{
provide: MATERIAL_COMPATIBILITY_MODE, useValue: true,
}],
})
], NoConflictStyleCompatibilityMode);
var __decorate$2 = (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); }
};
/** Injection token that configures whether the Material sanity checks are enabled. */
const MATERIAL_SANITY_CHECKS = new 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).
*/
let MdCommonModule = class MdCommonModule {
constructor(_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 && isDevMode()) {
this._checkDoctype();
this._checkTheme();
this._hasDoneGlobalChecks = true;
}
}
_checkDoctype() {
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.');
}
}
_checkTheme() {
if (typeof getComputedStyle === 'function') {
const 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);
}
}
};
MdCommonModule = __decorate$2([
NgModule({
imports: [CompatibilityModule],
exports: [CompatibilityModule],
providers: [{
provide: MATERIAL_SANITY_CHECKS, useValue: true,
}],
}),
__param(0, Optional()), __param(0, Inject(DOCUMENT)),
__param(1, Optional()), __param(1, Inject(MATERIAL_SANITY_CHECKS)),
__metadata("design:paramtypes", [Object, Boolean])
], MdCommonModule);
var __decorate$1 = (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;
};
/**
* 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.
*/
let MdLine = class MdLine {
};
MdLine = __decorate$1([
Directive({
selector: '[md-line], [mat-line], [mdLine], [matLine]',
host: { 'class': 'mat-line' }
})
], MdLine);
/**
* Helper that takes a query list of lines and sets the correct class on the host.
* @docs-private
*/
class MdLineSetter {
constructor(_lines, _renderer, _element) {
this._lines = _lines;
this._renderer = _renderer;
this._element = _element;
this._setLineClass(this._lines.length);
this._lines.changes.subscribe(() => {
this._setLineClass(this._lines.length);
});
}
_setLineClass(count) {
this._resetClasses();
if (count === 2 || count === 3) {
this._setClass(`mat-${count}-line`, true);
}
else if (count > 3) {
this._setClass(`mat-multi-line`, true);
}
}
_resetClasses() {
this._setClass('mat-2-line', false);
this._setClass('mat-3-line', false);
this._setClass('mat-multi-line', false);
}
_setClass(className, isAdd) {
if (isAdd) {
this._renderer.addClass(this._element.nativeElement, className);
}
else {
this._renderer.removeClass(this._element.nativeElement, className);
}
}
}
let MdLineModule = class MdLineModule {
};
MdLineModule = __decorate$1([
NgModule({
imports: [MdCommonModule],
exports: [MdLine, MdCommonModule],
declarations: [MdLine],
})
], MdLineModule);
var __decorate$4 = (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$2 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* 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.
*/
let Dir = class Dir {
/**
* 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.
*/
constructor() {
/** Layout direction of the element. */
this._dir = 'ltr';
/** Event emitted when the direction changes. */
this.dirChange = new EventEmitter();
}
/** @docs-private */
get dir() {
return this._dir;
}
set dir(v) {
let old = this._dir;
this._dir = v;
if (old != this._dir) {
this.dirChange.emit();
}
}
/** Current layout direction of the element. */
get value() { return this.dir; }
set value(v) { this.dir = v; }
};
__decorate$4([
Input('dir'),
__metadata$2("design:type", String)
], Dir.prototype, "_dir", void 0);
__decorate$4([
Output(),
__metadata$2("design:type", Object)
], Dir.prototype, "dirChange", void 0);
__decorate$4([
HostBinding('attr.dir'),
__metadata$2("design:type", String),
__metadata$2("design:paramtypes", [String])
], Dir.prototype, "dir", null);
Dir = __decorate$4([
Directive({
selector: '[dir]',
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got.
exportAs: '$implicit'
})
], Dir);
let RtlModule = class RtlModule {
};
RtlModule = __decorate$4([
NgModule({
exports: [Dir],
declarations: [Dir]
})
], RtlModule);
var __decorate$5 = (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$3 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Factory that creates a new MutationObserver and allows us to stub it out in unit tests.
* @docs-private
*/
let MdMutationObserverFactory = class MdMutationObserverFactory {
create(callback) {
return typeof MutationObserver === 'undefined' ? null : new MutationObserver(callback);
}
};
MdMutationObserverFactory = __decorate$5([
Injectable()
], MdMutationObserverFactory);
/**
* Directive that triggers a callback whenever the content of
* its associated element has changed.
*/
let ObserveContent = class ObserveContent {
constructor(_mutationObserverFactory, _elementRef) {
this._mutationObserverFactory = _mutationObserverFactory;
this._elementRef = _elementRef;
/** Event emitted for each change in the element's content. */
this.event = new EventEmitter();
/** Used for debouncing the emitted values to the observeContent event. */
this._debouncer = new Subject();
}
ngAfterContentInit() {
if (this.debounce > 0) {
this._debouncer
.debounceTime(this.debounce)
.subscribe(mutations => this.event.emit(mutations));
}
else {
this._debouncer.subscribe(mutations => this.event.emit(mutations));
}
this._observer = this._mutationObserverFactory.create((mutations) => {
this._debouncer.next(mutations);
});
if (this._observer) {
this._observer.observe(this._elementRef.nativeElement, {
characterData: true,
childList: true,
subtree: true
});
}
}
ngOnDestroy() {
if (this._observer) {
this._observer.disconnect();
this._debouncer.complete();
this._debouncer = this._observer = null;
}
}
};
__decorate$5([
Output('cdkObserveContent'),
__metadata$3("design:type", Object)
], ObserveContent.prototype, "event", void 0);
__decorate$5([
Input(),
__metadata$3("design:type", Number)
], ObserveContent.prototype, "debounce", void 0);
ObserveContent = __decorate$5([
Directive({
selector: '[cdkObserveContent]'
}),
__metadata$3("design:paramtypes", [MdMutationObserverFactory,
ElementRef])
], ObserveContent);
let ObserveContentModule = class ObserveContentModule {
};
ObserveContentModule = __decorate$5([
NgModule({
exports: [ObserveContent],
declarations: [ObserveContent],
providers: [MdMutationObserverFactory]
})
], ObserveContentModule);
/** Possible states for a ripple element. */
var RippleState;
(function (RippleState) {
RippleState[RippleState["FADING_IN"] = 0] = "FADING_IN";
RippleState[RippleState["VISIBLE"] = 1] = "VISIBLE";
RippleState[RippleState["FADING_OUT"] = 2] = "FADING_OUT";
RippleState[RippleState["HIDDEN"] = 3] = "HIDDEN";
})(RippleState || (RippleState = {}));
/**
* Reference to a previously launched ripple element.
*/
class RippleRef {
constructor(_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. */
fadeOut() {
this._renderer.fadeOutRipple(this);
}
}
/** Fade-in duration for the ripples. Can be modified with the speedFactor option. */
const RIPPLE_FADE_IN_DURATION = 450;
/** Fade-out duration for the ripples in milliseconds. This can't be modified by the speedFactor. */
const 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
*/
class RippleRenderer {
constructor(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. */
fadeInRipple(pageX, pageY, config = {}) {
let 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.
let scrollPosition = this._ruler.getViewportScrollPosition();
pageX -= scrollPosition.left;
pageY -= scrollPosition.top;
}
let radius = config.radius || distanceToFurthestCorner(pageX, pageY, containerRect);
let duration = RIPPLE_FADE_IN_DURATION * (1 / (config.speedFactor || 1));
let offsetX = pageX - containerRect.left;
let offsetY = pageY - containerRect.top;
let 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.
let 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(() => {
rippleRef.state = RippleState.VISIBLE;
if (!config.persistent && !this._isMousedown) {
rippleRef.fadeOut();
}
}, duration);
return rippleRef;
}
/** Fades out a ripple reference. */
fadeOutRipple(rippleRef) {
// For ripples that are not active anymore, don't re-un the fade-out animation.
if (!this._activeRipples.delete(rippleRef)) {
return;
}
let 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(() => {
rippleRef.state = RippleState.HIDDEN;
rippleEl.parentNode.removeChild(rippleEl);
}, RIPPLE_FADE_OUT_DURATION);
}
/** Fades out all currently active ripples. */
fadeOutAll() {
this._activeRipples.forEach(ripple => ripple.fadeOut());
}
/** Sets the trigger element and registers the mouse events. */
setTriggerElement(element) {
// Remove all previously register event listeners from the trigger element.
if (this._triggerElement) {
this._triggerEvents.forEach((fn, type) => this._triggerElement.removeEventListener(type, fn));
}
if (element) {
// If the element is not null, register all event listeners on the trigger element.
this._ngZone.runOutsideAngular(() => {
this._triggerEvents.forEach((fn, type) => element.addEventListener(type, fn));
});
}
this._triggerElement = element;
}
/** Listener being called on mousedown event. */
onMousedown(event) {
if (!this.rippleDisabled) {
this._isMousedown = true;
this.fadeInRipple(event.pageX, event.pageY, this.rippleConfig);
}
}
/** Listener being called on mouseup event. */
onMouseup() {
this._isMousedown = false;
// Fade-out all ripples that are completely visible and not persistent.
this._activeRipples.forEach(ripple => {
if (!ripple.config.persistent && ripple.state === RippleState.VISIBLE) {
ripple.fadeOut();
}
});
}
/** Listener being called on mouseleave event. */
onMouseLeave() {
if (this._isMousedown) {
this.onMouseup();
}
}
/** Runs a timeout outside of the Angular zone to avoid triggering the change detection. */
runTimeoutOutsideZone(fn, delay = 0) {
this._ngZone.runOutsideAngular(() => setTimeout(fn, delay));
}
}
/** Enforces a style recalculation of a DOM element by computing its styles. */
// TODO(devversion): Move into global utility function.
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.
*/
function distanceToFurthestCorner(x, y, rect) {
const distX = Math.max(Math.abs(x - rect.left), Math.abs(x - rect.right));
const distY = Math.max(Math.abs(y - rect.top), Math.abs(y - rect.bottom));
return Math.sqrt(distX * distX + distY * distY);
}
var __decorate$12 = (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;
};
// Whether the current platform supports the V8 Break Iterator. The V8 check
// is necessary to detect all Blink based browsers.
const 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
*/
let Platform = class Platform {
/**
* Service to detect the current platform by comparing the userAgent strings and
* checking browser-specific global properties.
* @docs-private
*/
constructor() {
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;
}
};
Platform = __decorate$12([
Injectable()
], Platform);
/** Cached result Set of input types support by the current browser. */
let supportedInputTypes;
/** Types of <input> that *might* be supported. */
const 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',
];
/** @returns 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;
}
let featureTestInput = document.createElement('input');
supportedInputTypes = new Set(candidateInputTypes.filter(value => {
featureTestInput.setAttribute('type', value);
return featureTestInput.type === value;
}));
return supportedInputTypes;
}
var __decorate$11 = (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;
};
let PlatformModule = class PlatformModule {
};
PlatformModule = __decorate$11([
NgModule({
providers: [Platform]
})
], PlatformModule);
var __decorate$10 = (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$6 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/** Time in ms to throttle the scrolling events by default. */
const 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.
*/
let ScrollDispatcher = class ScrollDispatcher {
constructor(_ngZone, _platform) {
this._ngZone = _ngZone;
this._platform = _platform;
/** Subject for notifying that a registered scrollable reference element has been scrolled. */
this._scrolled = new 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.
*/
register(scrollable) {
const scrollSubscription = scrollable.elementScrolled().subscribe(() => 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.
*/
deregister(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.
*/
scrolled(auditTimeInMs = DEFAULT_SCROLL_TIME, callback) {
// Scroll events can only happen on the browser, so do nothing if we're not on the browser.
if (!this._platform.isBrowser) {
return Subscription.EMPTY;
}
// In the case of a 0ms delay, use an observable without auditTime
// since it does add a perceptible delay in processing overhead.
let observable = auditTimeInMs > 0 ?
this._scrolled.asObservable().auditTime(auditTimeInMs) :
this._scrolled.asObservable();
this._scrolledCount++;
if (!this._globalSubscription) {
this._globalSubscription = this._ngZone.runOutsideAngular(() => {
return Observable.merge(Observable.fromEvent(window.document, 'scroll'), Observable.fromEvent(window, 'resize')).subscribe(() => 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.
let subscription = observable.subscribe(callback);
subscription.add(() => {
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. */
getScrollContainers(elementRef) {
const scrollingContainers = [];
this.scrollableReferences.forEach((_subscription, scrollable) => {
if (this.scrollableContainsElement(scrollable, elementRef)) {
scrollingContainers.push(scrollable);
}
});
return scrollingContainers;
}
/** Returns true if the element is contained within the provided Scrollable. */
scrollableContainsElement(scrollable, elementRef) {
let element = elementRef.nativeElement;
let 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. */
_notify() {
this._scrolled.next();
}
};
ScrollDispatcher = __decorate$10([
Injectable(),
__metadata$6("design:paramtypes", [NgZone, Platform])
], ScrollDispatcher);
function SCROLL_DISPATCHER_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
return parentDispatcher || new ScrollDispatcher(ngZone, platform);
}
const SCROLL_DISPATCHER_PROVIDER = {
// If there is already a ScrollDispatcher available, use that. Otherwise, provide a new one.
provide: ScrollDispatcher,
deps: [[new Optional(), new SkipSelf(), ScrollDispatcher], NgZone, Platform],
useFactory: SCROLL_DISPATCHER_PROVIDER_FACTORY
};
var __decorate$9 = (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$5 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Simple utility for getting the bounds of the browser viewport.
* @docs-private
*/
let ViewportRuler = class ViewportRuler {
constructor(scrollDispatcher) {
// Subscribe to scroll and resize events and update the document rectangle on changes.
scrollDispatcher.scrolled(null, () => this._cacheViewportGeometry());
}
/** Gets a ClientRect for the viewport's bounds. */
getViewportRect(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().
const scrollPosition = this.getViewportScrollPosition(documentRect);
const height = window.innerHeight;
const width = window.innerWidth;
return {
top: scrollPosition.top,
left: scrollPosition.left,
bottom: scrollPosition.top + height,
right: scrollPosition.left + width,
height,
width,
};
}
/**
* Gets the (top, left) scroll position of the viewport.
* @param documentRect
*/
getViewportScrollPosition(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.
const top = -documentRect.top || document.body.scrollTop || window.scrollY ||
document.documentElement.scrollTop || 0;
const left = -documentRect.left || document.body.scrollLeft || window.scrollX ||
document.documentElement.scrollLeft || 0;
return { top, left };
}
/** Caches the latest client rectangle of the document element. */
_cacheViewportGeometry() {
this._documentRect = document.documentElement.getBoundingClientRect();
}
};
ViewportRuler = __decorate$9([
Injectable(),
__metadata$5("design:paramtypes", [ScrollDispatcher])
], ViewportRuler);
function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler, scrollDispatcher) {
return parentRuler || new ViewportRuler(scrollDispatcher);
}
const VIEWPORT_RULER_PROVIDER = {
// If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
provide: ViewportRuler,
deps: [[new Optional(), new SkipSelf(), ViewportRuler], ScrollDispatcher],
useFactory: VIEWPORT_RULER_PROVIDER_FACTORY
};
var __decorate$8 = (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$4 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param$2 = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
/** Injection token that can be used to specify the global ripple options. */
const MD_RIPPLE_GLOBAL_OPTIONS = new InjectionToken('md-ripple-global-options');
let MdRipple = class MdRipple {
constructor(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();
}
ngOnChanges(changes) {
if (changes['trigger'] && this.trigger) {
this._rippleRenderer.setTriggerElement(this.trigger);
}
this._updateRippleRenderer();
}
ngOnDestroy() {
// Set the trigger element to null to cleanup all listeners.
this._rippleRenderer.setTriggerElement(null);
}
/** Launches a manual ripple at the specified position. */
launch(pageX, pageY, config = this.rippleConfig) {
return this._rippleRenderer.fadeInRipple(pageX, pageY, config);
}
/** Fades out all currently showing ripple elements. */
fadeOutAll() {
this._rippleRenderer.fadeOutAll();
}
/** Ripple configuration from the directive's input values. */
get rippleConfig() {
return {
centered: this.centered,
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
radius: this.radius,
color: this.color
};
}
/** Updates the ripple renderer with the latest ripple configuration. */
_updateRippleRenderer() {
this._rippleRenderer.rippleDisabled = this._globalOptions.disabled || this.disabled;
this._rippleRenderer.rippleConfig = this.rippleConfig;
}
};
__decorate$8([
Input('mdRippleTrigger'),
__metadata$4("design:type", HTMLElement)
], MdRipple.prototype, "trigger", void 0);
__decorate$8([
Input('mdRippleCentered'),
__metadata$4("design:type", Boolean)
], MdRipple.prototype, "centered", void 0);
__decorate$8([
Input('mdRippleDisabled'),
__metadata$4("design:type", Boolean)
], MdRipple.prototype, "disabled", void 0);
__decorate$8([
Input('mdRippleRadius'),
__metadata$4("design:type", Number)
], MdRipple.prototype, "radius", void 0);
__decorate$8([
Input('mdRippleSpeedFactor'),
__metadata$4("design:type", Number)
], MdRipple.prototype, "speedFactor", void 0);
__decorate$8([
Input('mdRippleColor'),
__metadata$4("design:type", String)
], MdRipple.prototype, "color", void 0);
__decorate$8([
Input('mdRippleUnbounded'),
__metadata$4("design:type", Boolean)
], MdRipple.prototype, "unbounded", void 0);
MdRipple = __decorate$8([
Directive({
selector: '[md-ripple], [mat-ripple], [mdRipple], [matRipple]',
exportAs: 'mdRipple',
host: {
'class': 'mat-ripple',
'[class.mat-ripple-unbounded]': 'unbounded'
}
}),
__param$2(4, Optional()), __param$2(4, Inject(MD_RIPPLE_GLOBAL_OPTIONS)),
__metadata$4("design:paramtypes", [ElementRef,
NgZone,
ViewportRuler,
Platform, Object])
], MdRipple);
var __decorate$14 = (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$7 = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Sends an event when the directive's element is scrolled. Registers itself with the
* ScrollDispatcher service to include itself as part of its collection of scrolling events that it
* can be listened to through the service.
*/
let Scrollable = class Scrollable {
constructor(_elementRef, _scroll, _ngZone, _renderer) {
this._elementRef = _elementRef;
this._scroll = _scroll;
this._ngZone = _ngZone;
this._renderer = _renderer;
this._elementScrolled = new Subject();
}
ngOnInit() {
this._scrollListener = this._ngZone.runOutsideAngular(() => {
return this._renderer.listen(this.getElementRef().nativeElement, 'scroll', (event) => {
this._elementScrolled.next(event);
});
});
this._scroll.register(this);
}
ngOnDestroy() {
this._scroll.deregister(this);
if (this._scrollListener) {
this._scrollListener();
this._scrollListener = null;
}
}
/**
* Returns observable that emits when a scroll event is fired on the host element.
*/
elementScrolled() {
return this._elementScrolled.asObservable();
}
getElementRef() {
return this._elementRef;
}
};
Scrollable = __decorate$14([
Directive({