/*!
|
|
* Angular Material Design
|
|
* https://github.com/angular/material
|
|
* @license MIT
|
|
* v1.1.3
|
|
*/
|
|
goog.provide('ngmaterial.components.dialog');
|
|
goog.require('ngmaterial.components.backdrop');
|
|
goog.require('ngmaterial.core');
|
|
/**
|
|
* @ngdoc module
|
|
* @name material.components.dialog
|
|
*/
|
|
MdDialogDirective['$inject'] = ["$$rAF", "$mdTheming", "$mdDialog"];
|
|
MdDialogProvider['$inject'] = ["$$interimElementProvider"];
|
|
angular
|
|
.module('material.components.dialog', [
|
|
'material.core',
|
|
'material.components.backdrop'
|
|
])
|
|
.directive('mdDialog', MdDialogDirective)
|
|
.provider('$mdDialog', MdDialogProvider);
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name mdDialog
|
|
* @module material.components.dialog
|
|
*
|
|
* @restrict E
|
|
*
|
|
* @description
|
|
* `<md-dialog>` - The dialog's template must be inside this element.
|
|
*
|
|
* Inside, use an `<md-dialog-content>` element for the dialog's content, and use
|
|
* an `<md-dialog-actions>` element for the dialog's actions.
|
|
*
|
|
* ## CSS
|
|
* - `.md-dialog-content` - class that sets the padding on the content as the spec file
|
|
*
|
|
* ## Notes
|
|
* - If you specify an `id` for the `<md-dialog>`, the `<md-dialog-content>` will have the same `id`
|
|
* prefixed with `dialogContent_`.
|
|
*
|
|
* @usage
|
|
* ### Dialog template
|
|
* <hljs lang="html">
|
|
* <md-dialog aria-label="List dialog">
|
|
* <md-dialog-content>
|
|
* <md-list>
|
|
* <md-list-item ng-repeat="item in items">
|
|
* <p>Number {{item}}</p>
|
|
* </md-list-item>
|
|
* </md-list>
|
|
* </md-dialog-content>
|
|
* <md-dialog-actions>
|
|
* <md-button ng-click="closeDialog()" class="md-primary">Close Dialog</md-button>
|
|
* </md-dialog-actions>
|
|
* </md-dialog>
|
|
* </hljs>
|
|
*/
|
|
function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
|
|
return {
|
|
restrict: 'E',
|
|
link: function(scope, element) {
|
|
element.addClass('_md'); // private md component indicator for styling
|
|
|
|
$mdTheming(element);
|
|
$$rAF(function() {
|
|
var images;
|
|
var content = element[0].querySelector('md-dialog-content');
|
|
|
|
if (content) {
|
|
images = content.getElementsByTagName('img');
|
|
addOverflowClass();
|
|
//-- delayed image loading may impact scroll height, check after images are loaded
|
|
angular.element(images).on('load', addOverflowClass);
|
|
}
|
|
|
|
scope.$on('$destroy', function() {
|
|
$mdDialog.destroy(element);
|
|
});
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function addOverflowClass() {
|
|
element.toggleClass('md-content-overflow', content.scrollHeight > content.clientHeight);
|
|
}
|
|
|
|
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @ngdoc service
|
|
* @name $mdDialog
|
|
* @module material.components.dialog
|
|
*
|
|
* @description
|
|
* `$mdDialog` opens a dialog over the app to inform users about critical information or require
|
|
* them to make decisions. There are two approaches for setup: a simple promise API
|
|
* and regular object syntax.
|
|
*
|
|
* ## Restrictions
|
|
*
|
|
* - The dialog is always given an isolate scope.
|
|
* - The dialog's template must have an outer `<md-dialog>` element.
|
|
* Inside, use an `<md-dialog-content>` element for the dialog's content, and use
|
|
* an `<md-dialog-actions>` element for the dialog's actions.
|
|
* - Dialogs must cover the entire application to keep interactions inside of them.
|
|
* Use the `parent` option to change where dialogs are appended.
|
|
*
|
|
* ## Sizing
|
|
* - Complex dialogs can be sized with `flex="percentage"`, i.e. `flex="66"`.
|
|
* - Default max-width is 80% of the `rootElement` or `parent`.
|
|
*
|
|
* ## CSS
|
|
* - `.md-dialog-content` - class that sets the padding on the content as the spec file
|
|
*
|
|
* @usage
|
|
* <hljs lang="html">
|
|
* <div ng-app="demoApp" ng-controller="EmployeeController">
|
|
* <div>
|
|
* <md-button ng-click="showAlert()" class="md-raised md-warn">
|
|
* Employee Alert!
|
|
* </md-button>
|
|
* </div>
|
|
* <div>
|
|
* <md-button ng-click="showDialog($event)" class="md-raised">
|
|
* Custom Dialog
|
|
* </md-button>
|
|
* </div>
|
|
* <div>
|
|
* <md-button ng-click="closeAlert()" ng-disabled="!hasAlert()" class="md-raised">
|
|
* Close Alert
|
|
* </md-button>
|
|
* </div>
|
|
* <div>
|
|
* <md-button ng-click="showGreeting($event)" class="md-raised md-primary" >
|
|
* Greet Employee
|
|
* </md-button>
|
|
* </div>
|
|
* </div>
|
|
* </hljs>
|
|
*
|
|
* ### JavaScript: object syntax
|
|
* <hljs lang="js">
|
|
* (function(angular, undefined){
|
|
* "use strict";
|
|
*
|
|
* angular
|
|
* .module('demoApp', ['ngMaterial'])
|
|
* .controller('AppCtrl', AppController);
|
|
*
|
|
* function AppController($scope, $mdDialog) {
|
|
* var alert;
|
|
* $scope.showAlert = showAlert;
|
|
* $scope.showDialog = showDialog;
|
|
* $scope.items = [1, 2, 3];
|
|
*
|
|
* // Internal method
|
|
* function showAlert() {
|
|
* alert = $mdDialog.alert({
|
|
* title: 'Attention',
|
|
* textContent: 'This is an example of how easy dialogs can be!',
|
|
* ok: 'Close'
|
|
* });
|
|
*
|
|
* $mdDialog
|
|
* .show( alert )
|
|
* .finally(function() {
|
|
* alert = undefined;
|
|
* });
|
|
* }
|
|
*
|
|
* function showDialog($event) {
|
|
* var parentEl = angular.element(document.body);
|
|
* $mdDialog.show({
|
|
* parent: parentEl,
|
|
* targetEvent: $event,
|
|
* template:
|
|
* '<md-dialog aria-label="List dialog">' +
|
|
* ' <md-dialog-content>'+
|
|
* ' <md-list>'+
|
|
* ' <md-list-item ng-repeat="item in items">'+
|
|
* ' <p>Number {{item}}</p>' +
|
|
* ' </md-item>'+
|
|
* ' </md-list>'+
|
|
* ' </md-dialog-content>' +
|
|
* ' <md-dialog-actions>' +
|
|
* ' <md-button ng-click="closeDialog()" class="md-primary">' +
|
|
* ' Close Dialog' +
|
|
* ' </md-button>' +
|
|
* ' </md-dialog-actions>' +
|
|
* '</md-dialog>',
|
|
* locals: {
|
|
* items: $scope.items
|
|
* },
|
|
* controller: DialogController
|
|
* });
|
|
* function DialogController($scope, $mdDialog, items) {
|
|
* $scope.items = items;
|
|
* $scope.closeDialog = function() {
|
|
* $mdDialog.hide();
|
|
* }
|
|
* }
|
|
* }
|
|
* }
|
|
* })(angular);
|
|
* </hljs>
|
|
*
|
|
* ### Multiple Dialogs
|
|
* Using the `multiple` option for the `$mdDialog` service allows developers to show multiple dialogs
|
|
* at the same time.
|
|
*
|
|
* <hljs lang="js">
|
|
* // From plain options
|
|
* $mdDialog.show({
|
|
* multiple: true
|
|
* });
|
|
*
|
|
* // From a dialog preset
|
|
* $mdDialog.show(
|
|
* $mdDialog
|
|
* .alert()
|
|
* .multiple(true)
|
|
* );
|
|
*
|
|
* </hljs>
|
|
*
|
|
* ### Pre-Rendered Dialogs
|
|
* By using the `contentElement` option, it is possible to use an already existing element in the DOM.
|
|
*
|
|
* > Pre-rendered dialogs will be not linked to any scope and will not instantiate any new controller.<br/>
|
|
* > You can manually link the elements to a scope or instantiate a controller from the template (`ng-controller`)
|
|
*
|
|
* <hljs lang="js">
|
|
* $scope.showPrerenderedDialog = function() {
|
|
* $mdDialog.show({
|
|
* contentElement: '#myStaticDialog',
|
|
* parent: angular.element(document.body)
|
|
* });
|
|
* };
|
|
* </hljs>
|
|
*
|
|
* When using a string as value, `$mdDialog` will automatically query the DOM for the specified CSS selector.
|
|
*
|
|
* <hljs lang="html">
|
|
* <div style="visibility: hidden">
|
|
* <div class="md-dialog-container" id="myStaticDialog">
|
|
* <md-dialog>
|
|
* This is a pre-rendered dialog.
|
|
* </md-dialog>
|
|
* </div>
|
|
* </div>
|
|
* </hljs>
|
|
*
|
|
* **Notice**: It is important, to use the `.md-dialog-container` as the content element, otherwise the dialog
|
|
* will not show up.
|
|
*
|
|
* It also possible to use a DOM element for the `contentElement` option.
|
|
* - `contentElement: document.querySelector('#myStaticDialog')`
|
|
* - `contentElement: angular.element(TEMPLATE)`
|
|
*
|
|
* When using a `template` as content element, it will be not compiled upon open.
|
|
* This allows you to compile the element yourself and use it each time the dialog opens.
|
|
*
|
|
* ### Custom Presets
|
|
* Developers are also able to create their own preset, which can be easily used without repeating
|
|
* their options each time.
|
|
*
|
|
* <hljs lang="js">
|
|
* $mdDialogProvider.addPreset('testPreset', {
|
|
* options: function() {
|
|
* return {
|
|
* template:
|
|
* '<md-dialog>' +
|
|
* 'This is a custom preset' +
|
|
* '</md-dialog>',
|
|
* controllerAs: 'dialog',
|
|
* bindToController: true,
|
|
* clickOutsideToClose: true,
|
|
* escapeToClose: true
|
|
* };
|
|
* }
|
|
* });
|
|
* </hljs>
|
|
*
|
|
* After you created your preset at config phase, you can easily access it.
|
|
*
|
|
* <hljs lang="js">
|
|
* $mdDialog.show(
|
|
* $mdDialog.testPreset()
|
|
* );
|
|
* </hljs>
|
|
*
|
|
* ### JavaScript: promise API syntax, custom dialog template
|
|
* <hljs lang="js">
|
|
* (function(angular, undefined){
|
|
* "use strict";
|
|
*
|
|
* angular
|
|
* .module('demoApp', ['ngMaterial'])
|
|
* .controller('EmployeeController', EmployeeEditor)
|
|
* .controller('GreetingController', GreetingController);
|
|
*
|
|
* // Fictitious Employee Editor to show how to use simple and complex dialogs.
|
|
*
|
|
* function EmployeeEditor($scope, $mdDialog) {
|
|
* var alert;
|
|
*
|
|
* $scope.showAlert = showAlert;
|
|
* $scope.closeAlert = closeAlert;
|
|
* $scope.showGreeting = showCustomGreeting;
|
|
*
|
|
* $scope.hasAlert = function() { return !!alert };
|
|
* $scope.userName = $scope.userName || 'Bobby';
|
|
*
|
|
* // Dialog #1 - Show simple alert dialog and cache
|
|
* // reference to dialog instance
|
|
*
|
|
* function showAlert() {
|
|
* alert = $mdDialog.alert()
|
|
* .title('Attention, ' + $scope.userName)
|
|
* .textContent('This is an example of how easy dialogs can be!')
|
|
* .ok('Close');
|
|
*
|
|
* $mdDialog
|
|
* .show( alert )
|
|
* .finally(function() {
|
|
* alert = undefined;
|
|
* });
|
|
* }
|
|
*
|
|
* // Close the specified dialog instance and resolve with 'finished' flag
|
|
* // Normally this is not needed, just use '$mdDialog.hide()' to close
|
|
* // the most recent dialog popup.
|
|
*
|
|
* function closeAlert() {
|
|
* $mdDialog.hide( alert, "finished" );
|
|
* alert = undefined;
|
|
* }
|
|
*
|
|
* // Dialog #2 - Demonstrate more complex dialogs construction and popup.
|
|
*
|
|
* function showCustomGreeting($event) {
|
|
* $mdDialog.show({
|
|
* targetEvent: $event,
|
|
* template:
|
|
* '<md-dialog>' +
|
|
*
|
|
* ' <md-dialog-content>Hello {{ employee }}!</md-dialog-content>' +
|
|
*
|
|
* ' <md-dialog-actions>' +
|
|
* ' <md-button ng-click="closeDialog()" class="md-primary">' +
|
|
* ' Close Greeting' +
|
|
* ' </md-button>' +
|
|
* ' </md-dialog-actions>' +
|
|
* '</md-dialog>',
|
|
* controller: 'GreetingController',
|
|
* onComplete: afterShowAnimation,
|
|
* locals: { employee: $scope.userName }
|
|
* });
|
|
*
|
|
* // When the 'enter' animation finishes...
|
|
*
|
|
* function afterShowAnimation(scope, element, options) {
|
|
* // post-show code here: DOM element focus, etc.
|
|
* }
|
|
* }
|
|
*
|
|
* // Dialog #3 - Demonstrate use of ControllerAs and passing $scope to dialog
|
|
* // Here we used ng-controller="GreetingController as vm" and
|
|
* // $scope.vm === <controller instance>
|
|
*
|
|
* function showCustomGreeting() {
|
|
*
|
|
* $mdDialog.show({
|
|
* clickOutsideToClose: true,
|
|
*
|
|
* scope: $scope, // use parent scope in template
|
|
* preserveScope: true, // do not forget this if use parent scope
|
|
|
|
* // Since GreetingController is instantiated with ControllerAs syntax
|
|
* // AND we are passing the parent '$scope' to the dialog, we MUST
|
|
* // use 'vm.<xxx>' in the template markup
|
|
*
|
|
* template: '<md-dialog>' +
|
|
* ' <md-dialog-content>' +
|
|
* ' Hi There {{vm.employee}}' +
|
|
* ' </md-dialog-content>' +
|
|
* '</md-dialog>',
|
|
*
|
|
* controller: function DialogController($scope, $mdDialog) {
|
|
* $scope.closeDialog = function() {
|
|
* $mdDialog.hide();
|
|
* }
|
|
* }
|
|
* });
|
|
* }
|
|
*
|
|
* }
|
|
*
|
|
* // Greeting controller used with the more complex 'showCustomGreeting()' custom dialog
|
|
*
|
|
* function GreetingController($scope, $mdDialog, employee) {
|
|
* // Assigned from construction <code>locals</code> options...
|
|
* $scope.employee = employee;
|
|
*
|
|
* $scope.closeDialog = function() {
|
|
* // Easily hides most recent dialog shown...
|
|
* // no specific instance reference is needed.
|
|
* $mdDialog.hide();
|
|
* };
|
|
* }
|
|
*
|
|
* })(angular);
|
|
* </hljs>
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#alert
|
|
*
|
|
* @description
|
|
* Builds a preconfigured dialog with the specified message.
|
|
*
|
|
* @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
|
|
*
|
|
* - $mdDialogPreset#title(string) - Sets the alert title.
|
|
* - $mdDialogPreset#textContent(string) - Sets the alert message.
|
|
* - $mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize
|
|
* module to be loaded. HTML is not run through Angular's compiler.
|
|
* - $mdDialogPreset#ok(string) - Sets the alert "Okay" button text.
|
|
* - $mdDialogPreset#theme(string) - Sets the theme of the alert dialog.
|
|
* - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
|
|
* the location of the click will be used as the starting point for the opening animation
|
|
* of the the dialog.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#confirm
|
|
*
|
|
* @description
|
|
* Builds a preconfigured dialog with the specified message. You can call show and the promise returned
|
|
* will be resolved only if the user clicks the confirm action on the dialog.
|
|
*
|
|
* @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
|
|
*
|
|
* Additionally, it supports the following methods:
|
|
*
|
|
* - $mdDialogPreset#title(string) - Sets the confirm title.
|
|
* - $mdDialogPreset#textContent(string) - Sets the confirm message.
|
|
* - $mdDialogPreset#htmlContent(string) - Sets the confirm message as HTML. Requires ngSanitize
|
|
* module to be loaded. HTML is not run through Angular's compiler.
|
|
* - $mdDialogPreset#ok(string) - Sets the confirm "Okay" button text.
|
|
* - $mdDialogPreset#cancel(string) - Sets the confirm "Cancel" button text.
|
|
* - $mdDialogPreset#theme(string) - Sets the theme of the confirm dialog.
|
|
* - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
|
|
* the location of the click will be used as the starting point for the opening animation
|
|
* of the the dialog.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#prompt
|
|
*
|
|
* @description
|
|
* Builds a preconfigured dialog with the specified message and input box. You can call show and the promise returned
|
|
* will be resolved only if the user clicks the prompt action on the dialog, passing the input value as the first argument.
|
|
*
|
|
* @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
|
|
*
|
|
* Additionally, it supports the following methods:
|
|
*
|
|
* - $mdDialogPreset#title(string) - Sets the prompt title.
|
|
* - $mdDialogPreset#textContent(string) - Sets the prompt message.
|
|
* - $mdDialogPreset#htmlContent(string) - Sets the prompt message as HTML. Requires ngSanitize
|
|
* module to be loaded. HTML is not run through Angular's compiler.
|
|
* - $mdDialogPreset#placeholder(string) - Sets the placeholder text for the input.
|
|
* - $mdDialogPreset#initialValue(string) - Sets the initial value for the prompt input.
|
|
* - $mdDialogPreset#ok(string) - Sets the prompt "Okay" button text.
|
|
* - $mdDialogPreset#cancel(string) - Sets the prompt "Cancel" button text.
|
|
* - $mdDialogPreset#theme(string) - Sets the theme of the prompt dialog.
|
|
* - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
|
|
* the location of the click will be used as the starting point for the opening animation
|
|
* of the the dialog.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#show
|
|
*
|
|
* @description
|
|
* Show a dialog with the specified options.
|
|
*
|
|
* @param {object} optionsOrPreset Either provide an `$mdDialogPreset` returned from `alert()`, and
|
|
* `confirm()`, or an options object with the following properties:
|
|
* - `templateUrl` - `{string=}`: The url of a template that will be used as the content
|
|
* of the dialog.
|
|
* - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML
|
|
* with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce).
|
|
* This template should **never** be constructed with any kind of user input or user data.
|
|
* - `contentElement` - `{string|Element}`: Instead of using a template, which will be compiled each time a
|
|
* dialog opens, you can also use a DOM element.<br/>
|
|
* * When specifying an element, which is present on the DOM, `$mdDialog` will temporary fetch the element into
|
|
* the dialog and restores it at the old DOM position upon close.
|
|
* * When specifying a string, the string be used as a CSS selector, to lookup for the element in the DOM.
|
|
* - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a
|
|
* `<md-dialog>` tag if one is not provided. Defaults to true. Can be disabled if you provide a
|
|
* custom dialog directive.
|
|
* - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
|
|
* the location of the click will be used as the starting point for the opening animation
|
|
* of the the dialog.
|
|
* - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object
|
|
* that is used to determine the bounds (top, left, height, width) from which the Dialog will
|
|
* originate.
|
|
* - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object
|
|
* that is used to determine the bounds (top, left, height, width) to which the Dialog will
|
|
* target.
|
|
* - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
|
|
* it will create a new isolate scope.
|
|
* This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
|
|
* - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false
|
|
* - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the dialog is open.
|
|
* Default true.
|
|
* - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop behind the dialog.
|
|
* Default true.
|
|
* - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the dialog to
|
|
* close it. Default false.
|
|
* - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the dialog.
|
|
* Default true.
|
|
* - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on open. Only disable if
|
|
* focusing some other way, as focus management is required for dialogs to be accessible.
|
|
* Defaults to true.
|
|
* - `controller` - `{function|string=}`: The controller to associate with the dialog. The controller
|
|
* will be injected with the local `$mdDialog`, which passes along a scope for the dialog.
|
|
* - `locals` - `{object=}`: An object containing key/value pairs. The keys will be used as names
|
|
* of values to inject into the controller. For example, `locals: {three: 3}` would inject
|
|
* `three` into the controller, with the value 3. If `bindToController` is true, they will be
|
|
* copied to the controller instead.
|
|
* - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
|
|
* - `resolve` - `{object=}`: Similar to locals, except it takes promises as values, and the
|
|
* dialog will not open until all of the promises resolve.
|
|
* - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
|
|
* - `parent` - `{element=}`: The element to append the dialog to. Defaults to appending
|
|
* to the root element of the application.
|
|
* - `onShowing` - `function(scope, element)`: Callback function used to announce the show() action is
|
|
* starting.
|
|
* - `onComplete` - `function(scope, element)`: Callback function used to announce when the show() action is
|
|
* finished.
|
|
* - `onRemoving` - `function(element, removePromise)`: Callback function used to announce the
|
|
* close/hide() action is starting. This allows developers to run custom animations
|
|
* in parallel the close animations.
|
|
* - `fullscreen` `{boolean=}`: An option to toggle whether the dialog should show in fullscreen
|
|
* or not. Defaults to `false`.
|
|
* @returns {promise} A promise that can be resolved with `$mdDialog.hide()` or
|
|
* rejected with `$mdDialog.cancel()`.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#hide
|
|
*
|
|
* @description
|
|
* Hide an existing dialog and resolve the promise returned from `$mdDialog.show()`.
|
|
*
|
|
* @param {*=} response An argument for the resolved promise.
|
|
*
|
|
* @returns {promise} A promise that is resolved when the dialog has been closed.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name $mdDialog#cancel
|
|
*
|
|
* @description
|
|
* Hide an existing dialog and reject the promise returned from `$mdDialog.show()`.
|
|
*
|
|
* @param {*=} response An argument for the rejected promise.
|
|
*
|
|
* @returns {promise} A promise that is resolved when the dialog has been closed.
|
|
*/
|
|
|
|
function MdDialogProvider($$interimElementProvider) {
|
|
// Elements to capture and redirect focus when the user presses tab at the dialog boundary.
|
|
advancedDialogOptions['$inject'] = ["$mdDialog", "$mdConstant"];
|
|
dialogDefaultOptions['$inject'] = ["$mdDialog", "$mdAria", "$mdUtil", "$mdConstant", "$animate", "$document", "$window", "$rootElement", "$log", "$injector", "$mdTheming", "$interpolate", "$mdInteraction"];
|
|
var topFocusTrap, bottomFocusTrap;
|
|
|
|
return $$interimElementProvider('$mdDialog')
|
|
.setDefaults({
|
|
methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose',
|
|
'targetEvent', 'closeTo', 'openFrom', 'parent', 'fullscreen', 'multiple'],
|
|
options: dialogDefaultOptions
|
|
})
|
|
.addPreset('alert', {
|
|
methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'theme',
|
|
'css'],
|
|
options: advancedDialogOptions
|
|
})
|
|
.addPreset('confirm', {
|
|
methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'cancel',
|
|
'theme', 'css'],
|
|
options: advancedDialogOptions
|
|
})
|
|
.addPreset('prompt', {
|
|
methods: ['title', 'htmlContent', 'textContent', 'initialValue', 'content', 'placeholder', 'ariaLabel',
|
|
'ok', 'cancel', 'theme', 'css'],
|
|
options: advancedDialogOptions
|
|
});
|
|
|
|
/* ngInject */
|
|
function advancedDialogOptions($mdDialog, $mdConstant) {
|
|
return {
|
|
template: [
|
|
'<md-dialog md-theme="{{ dialog.theme || dialog.defaultTheme }}" aria-label="{{ dialog.ariaLabel }}" ng-class="dialog.css">',
|
|
' <md-dialog-content class="md-dialog-content" role="document" tabIndex="-1">',
|
|
' <h2 class="md-title">{{ dialog.title }}</h2>',
|
|
' <div ng-if="::dialog.mdHtmlContent" class="md-dialog-content-body" ',
|
|
' ng-bind-html="::dialog.mdHtmlContent"></div>',
|
|
' <div ng-if="::!dialog.mdHtmlContent" class="md-dialog-content-body">',
|
|
' <p>{{::dialog.mdTextContent}}</p>',
|
|
' </div>',
|
|
' <md-input-container md-no-float ng-if="::dialog.$type == \'prompt\'" class="md-prompt-input-container">',
|
|
' <input ng-keypress="dialog.keypress($event)" md-autofocus ng-model="dialog.result" ' +
|
|
' placeholder="{{::dialog.placeholder}}">',
|
|
' </md-input-container>',
|
|
' </md-dialog-content>',
|
|
' <md-dialog-actions>',
|
|
' <md-button ng-if="dialog.$type === \'confirm\' || dialog.$type === \'prompt\'"' +
|
|
' ng-click="dialog.abort()" class="md-primary md-cancel-button">',
|
|
' {{ dialog.cancel }}',
|
|
' </md-button>',
|
|
' <md-button ng-click="dialog.hide()" class="md-primary md-confirm-button" md-autofocus="dialog.$type===\'alert\'">',
|
|
' {{ dialog.ok }}',
|
|
' </md-button>',
|
|
' </md-dialog-actions>',
|
|
'</md-dialog>'
|
|
].join('').replace(/\s\s+/g, ''),
|
|
controller: function mdDialogCtrl() {
|
|
var isPrompt = this.$type == 'prompt';
|
|
|
|
if (isPrompt && this.initialValue) {
|
|
this.result = this.initialValue;
|
|
}
|
|
|
|
this.hide = function() {
|
|
$mdDialog.hide(isPrompt ? this.result : true);
|
|
};
|
|
this.abort = function() {
|
|
$mdDialog.cancel();
|
|
};
|
|
this.keypress = function($event) {
|
|
if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
|
|
$mdDialog.hide(this.result);
|
|
}
|
|
};
|
|
},
|
|
controllerAs: 'dialog',
|
|
bindToController: true,
|
|
};
|
|
}
|
|
|
|
/* ngInject */
|
|
function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document, $window, $rootElement,
|
|
$log, $injector, $mdTheming, $interpolate, $mdInteraction) {
|
|
|
|
return {
|
|
hasBackdrop: true,
|
|
isolateScope: true,
|
|
onCompiling: beforeCompile,
|
|
onShow: onShow,
|
|
onShowing: beforeShow,
|
|
onRemove: onRemove,
|
|
clickOutsideToClose: false,
|
|
escapeToClose: true,
|
|
targetEvent: null,
|
|
closeTo: null,
|
|
openFrom: null,
|
|
focusOnOpen: true,
|
|
disableParentScroll: true,
|
|
autoWrap: true,
|
|
fullscreen: false,
|
|
transformTemplate: function(template, options) {
|
|
// Make the dialog container focusable, because otherwise the focus will be always redirected to
|
|
// an element outside of the container, and the focus trap won't work probably..
|
|
// Also the tabindex is needed for the `escapeToClose` functionality, because
|
|
// the keyDown event can't be triggered when the focus is outside of the container.
|
|
var startSymbol = $interpolate.startSymbol();
|
|
var endSymbol = $interpolate.endSymbol();
|
|
var theme = startSymbol + (options.themeWatch ? '' : '::') + 'theme' + endSymbol;
|
|
return '<div class="md-dialog-container" tabindex="-1" md-theme="' + theme + '">' + validatedTemplate(template) + '</div>';
|
|
|
|
/**
|
|
* The specified template should contain a <md-dialog> wrapper element....
|
|
*/
|
|
function validatedTemplate(template) {
|
|
if (options.autoWrap && !/<\/md-dialog>/g.test(template)) {
|
|
return '<md-dialog>' + (template || '') + '</md-dialog>';
|
|
} else {
|
|
return template || '';
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function beforeCompile(options) {
|
|
// Automatically apply the theme, if the user didn't specify a theme explicitly.
|
|
// Those option changes need to be done, before the compilation has started, because otherwise
|
|
// the option changes will be not available in the $mdCompilers locales.
|
|
options.defaultTheme = $mdTheming.defaultTheme();
|
|
|
|
detectTheming(options);
|
|
}
|
|
|
|
function beforeShow(scope, element, options, controller) {
|
|
|
|
if (controller) {
|
|
var mdHtmlContent = controller.htmlContent || options.htmlContent || '';
|
|
var mdTextContent = controller.textContent || options.textContent ||
|
|
controller.content || options.content || '';
|
|
|
|
if (mdHtmlContent && !$injector.has('$sanitize')) {
|
|
throw Error('The ngSanitize module must be loaded in order to use htmlContent.');
|
|
}
|
|
|
|
if (mdHtmlContent && mdTextContent) {
|
|
throw Error('md-dialog cannot have both `htmlContent` and `textContent`');
|
|
}
|
|
|
|
// Only assign the content if nothing throws, otherwise it'll still be compiled.
|
|
controller.mdHtmlContent = mdHtmlContent;
|
|
controller.mdTextContent = mdTextContent;
|
|
}
|
|
}
|
|
|
|
/** Show method for dialogs */
|
|
function onShow(scope, element, options, controller) {
|
|
angular.element($document[0].body).addClass('md-dialog-is-showing');
|
|
|
|
var dialogElement = element.find('md-dialog');
|
|
|
|
// Once a dialog has `ng-cloak` applied on his template the dialog animation will not work properly.
|
|
// This is a very common problem, so we have to notify the developer about this.
|
|
if (dialogElement.hasClass('ng-cloak')) {
|
|
var message = '$mdDialog: using `<md-dialog ng-cloak>` will affect the dialog opening animations.';
|
|
$log.warn( message, element[0] );
|
|
}
|
|
|
|
captureParentAndFromToElements(options);
|
|
configureAria(dialogElement, options);
|
|
showBackdrop(scope, element, options);
|
|
activateListeners(element, options);
|
|
|
|
return dialogPopIn(element, options)
|
|
.then(function() {
|
|
lockScreenReader(element, options);
|
|
warnDeprecatedActions();
|
|
focusOnOpen();
|
|
});
|
|
|
|
/**
|
|
* Check to see if they used the deprecated .md-actions class and log a warning
|
|
*/
|
|
function warnDeprecatedActions() {
|
|
if (element[0].querySelector('.md-actions')) {
|
|
$log.warn('Using a class of md-actions is deprecated, please use <md-dialog-actions>.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For alerts, focus on content... otherwise focus on
|
|
* the close button (or equivalent)
|
|
*/
|
|
function focusOnOpen() {
|
|
if (options.focusOnOpen) {
|
|
var target = $mdUtil.findFocusTarget(element) || findCloseButton() || dialogElement;
|
|
target.focus();
|
|
}
|
|
|
|
/**
|
|
* If no element with class dialog-close, try to find the last
|
|
* button child in md-actions and assume it is a close button.
|
|
*
|
|
* If we find no actions at all, log a warning to the console.
|
|
*/
|
|
function findCloseButton() {
|
|
return element[0].querySelector('.dialog-close, md-dialog-actions button:last-child');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove function for all dialogs
|
|
*/
|
|
function onRemove(scope, element, options) {
|
|
options.deactivateListeners();
|
|
options.unlockScreenReader();
|
|
options.hideBackdrop(options.$destroy);
|
|
|
|
// Remove the focus traps that we added earlier for keeping focus within the dialog.
|
|
if (topFocusTrap && topFocusTrap.parentNode) {
|
|
topFocusTrap.parentNode.removeChild(topFocusTrap);
|
|
}
|
|
|
|
if (bottomFocusTrap && bottomFocusTrap.parentNode) {
|
|
bottomFocusTrap.parentNode.removeChild(bottomFocusTrap);
|
|
}
|
|
|
|
// For navigation $destroy events, do a quick, non-animated removal,
|
|
// but for normal closes (from clicks, etc) animate the removal
|
|
return !!options.$destroy ? detachAndClean() : animateRemoval().then( detachAndClean );
|
|
|
|
/**
|
|
* For normal closes, animate the removal.
|
|
* For forced closes (like $destroy events), skip the animations
|
|
*/
|
|
function animateRemoval() {
|
|
return dialogPopOut(element, options);
|
|
}
|
|
|
|
/**
|
|
* Detach the element
|
|
*/
|
|
function detachAndClean() {
|
|
angular.element($document[0].body).removeClass('md-dialog-is-showing');
|
|
|
|
// Reverse the container stretch if using a content element.
|
|
if (options.contentElement) {
|
|
options.reverseContainerStretch();
|
|
}
|
|
|
|
// Exposed cleanup function from the $mdCompiler.
|
|
options.cleanupElement();
|
|
|
|
// Restores the focus to the origin element if the last interaction upon opening was a keyboard.
|
|
if (!options.$destroy && options.originInteraction === 'keyboard') {
|
|
options.origin.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
function detectTheming(options) {
|
|
// Once the user specifies a targetEvent, we will automatically try to find the correct
|
|
// nested theme.
|
|
var targetEl;
|
|
if (options.targetEvent && options.targetEvent.target) {
|
|
targetEl = angular.element(options.targetEvent.target);
|
|
}
|
|
|
|
var themeCtrl = targetEl && targetEl.controller('mdTheme');
|
|
|
|
if (!themeCtrl) {
|
|
return;
|
|
}
|
|
|
|
options.themeWatch = themeCtrl.$shouldWatch;
|
|
|
|
var theme = options.theme || themeCtrl.$mdTheme;
|
|
|
|
if (theme) {
|
|
options.scope.theme = theme;
|
|
}
|
|
|
|
var unwatch = themeCtrl.registerChanges(function (newTheme) {
|
|
options.scope.theme = newTheme;
|
|
|
|
if (!options.themeWatch) {
|
|
unwatch();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Capture originator/trigger/from/to element information (if available)
|
|
* and the parent container for the dialog; defaults to the $rootElement
|
|
* unless overridden in the options.parent
|
|
*/
|
|
function captureParentAndFromToElements(options) {
|
|
options.origin = angular.extend({
|
|
element: null,
|
|
bounds: null,
|
|
focus: angular.noop
|
|
}, options.origin || {});
|
|
|
|
options.parent = getDomElement(options.parent, $rootElement);
|
|
options.closeTo = getBoundingClientRect(getDomElement(options.closeTo));
|
|
options.openFrom = getBoundingClientRect(getDomElement(options.openFrom));
|
|
|
|
if ( options.targetEvent ) {
|
|
options.origin = getBoundingClientRect(options.targetEvent.target, options.origin);
|
|
options.originInteraction = $mdInteraction.getLastInteractionType();
|
|
}
|
|
|
|
|
|
/**
|
|
* Identify the bounding RECT for the target element
|
|
*
|
|
*/
|
|
function getBoundingClientRect (element, orig) {
|
|
var source = angular.element((element || {}));
|
|
if (source && source.length) {
|
|
// Compute and save the target element's bounding rect, so that if the
|
|
// element is hidden when the dialog closes, we can shrink the dialog
|
|
// back to the same position it expanded from.
|
|
//
|
|
// Checking if the source is a rect object or a DOM element
|
|
var bounds = {top:0,left:0,height:0,width:0};
|
|
var hasFn = angular.isFunction(source[0].getBoundingClientRect);
|
|
|
|
return angular.extend(orig || {}, {
|
|
element : hasFn ? source : undefined,
|
|
bounds : hasFn ? source[0].getBoundingClientRect() : angular.extend({}, bounds, source[0]),
|
|
focus : angular.bind(source, source.focus),
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the specifier is a simple string selector, then query for
|
|
* the DOM element.
|
|
*/
|
|
function getDomElement(element, defaultElement) {
|
|
if (angular.isString(element)) {
|
|
element = $document[0].querySelector(element);
|
|
}
|
|
|
|
// If we have a reference to a raw dom element, always wrap it in jqLite
|
|
return angular.element(element || defaultElement);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Listen for escape keys and outside clicks to auto close
|
|
*/
|
|
function activateListeners(element, options) {
|
|
var window = angular.element($window);
|
|
var onWindowResize = $mdUtil.debounce(function() {
|
|
stretchDialogContainerToViewport(element, options);
|
|
}, 60);
|
|
|
|
var removeListeners = [];
|
|
var smartClose = function() {
|
|
// Only 'confirm' dialogs have a cancel button... escape/clickOutside will
|
|
// cancel or fallback to hide.
|
|
var closeFn = ( options.$type == 'alert' ) ? $mdDialog.hide : $mdDialog.cancel;
|
|
$mdUtil.nextTick(closeFn, true);
|
|
};
|
|
|
|
if (options.escapeToClose) {
|
|
var parentTarget = options.parent;
|
|
var keyHandlerFn = function(ev) {
|
|
if (ev.keyCode === $mdConstant.KEY_CODE.ESCAPE) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
|
|
smartClose();
|
|
}
|
|
};
|
|
|
|
// Add keydown listeners
|
|
element.on('keydown', keyHandlerFn);
|
|
parentTarget.on('keydown', keyHandlerFn);
|
|
|
|
// Queue remove listeners function
|
|
removeListeners.push(function() {
|
|
|
|
element.off('keydown', keyHandlerFn);
|
|
parentTarget.off('keydown', keyHandlerFn);
|
|
|
|
});
|
|
}
|
|
|
|
// Register listener to update dialog on window resize
|
|
window.on('resize', onWindowResize);
|
|
|
|
removeListeners.push(function() {
|
|
window.off('resize', onWindowResize);
|
|
});
|
|
|
|
if (options.clickOutsideToClose) {
|
|
var target = element;
|
|
var sourceElem;
|
|
|
|
// Keep track of the element on which the mouse originally went down
|
|
// so that we can only close the backdrop when the 'click' started on it.
|
|
// A simple 'click' handler does not work,
|
|
// it sets the target object as the element the mouse went down on.
|
|
var mousedownHandler = function(ev) {
|
|
sourceElem = ev.target;
|
|
};
|
|
|
|
// We check if our original element and the target is the backdrop
|
|
// because if the original was the backdrop and the target was inside the dialog
|
|
// we don't want to dialog to close.
|
|
var mouseupHandler = function(ev) {
|
|
if (sourceElem === target[0] && ev.target === target[0]) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
|
|
smartClose();
|
|
}
|
|
};
|
|
|
|
// Add listeners
|
|
target.on('mousedown', mousedownHandler);
|
|
target.on('mouseup', mouseupHandler);
|
|
|
|
// Queue remove listeners function
|
|
removeListeners.push(function() {
|
|
target.off('mousedown', mousedownHandler);
|
|
target.off('mouseup', mouseupHandler);
|
|
});
|
|
}
|
|
|
|
// Attach specific `remove` listener handler
|
|
options.deactivateListeners = function() {
|
|
removeListeners.forEach(function(removeFn) {
|
|
removeFn();
|
|
});
|
|
options.deactivateListeners = null;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Show modal backdrop element...
|
|
*/
|
|
function showBackdrop(scope, element, options) {
|
|
|
|
if (options.disableParentScroll) {
|
|
// !! DO this before creating the backdrop; since disableScrollAround()
|
|
// configures the scroll offset; which is used by mdBackDrop postLink()
|
|
options.restoreScroll = $mdUtil.disableScrollAround(element, options.parent);
|
|
}
|
|
|
|
if (options.hasBackdrop) {
|
|
options.backdrop = $mdUtil.createBackdrop(scope, "md-dialog-backdrop md-opaque");
|
|
$animate.enter(options.backdrop, options.parent);
|
|
}
|
|
|
|
/**
|
|
* Hide modal backdrop element...
|
|
*/
|
|
options.hideBackdrop = function hideBackdrop($destroy) {
|
|
if (options.backdrop) {
|
|
if ( !!$destroy ) options.backdrop.remove();
|
|
else $animate.leave(options.backdrop);
|
|
}
|
|
|
|
|
|
if (options.disableParentScroll) {
|
|
options.restoreScroll && options.restoreScroll();
|
|
delete options.restoreScroll;
|
|
}
|
|
|
|
options.hideBackdrop = null;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Inject ARIA-specific attributes appropriate for Dialogs
|
|
*/
|
|
function configureAria(element, options) {
|
|
|
|
var role = (options.$type === 'alert') ? 'alertdialog' : 'dialog';
|
|
var dialogContent = element.find('md-dialog-content');
|
|
var existingDialogId = element.attr('id');
|
|
var dialogContentId = 'dialogContent_' + (existingDialogId || $mdUtil.nextUid());
|
|
|
|
element.attr({
|
|
'role': role,
|
|
'tabIndex': '-1'
|
|
});
|
|
|
|
if (dialogContent.length === 0) {
|
|
dialogContent = element;
|
|
// If the dialog element already had an ID, don't clobber it.
|
|
if (existingDialogId) {
|
|
dialogContentId = existingDialogId;
|
|
}
|
|
}
|
|
|
|
dialogContent.attr('id', dialogContentId);
|
|
element.attr('aria-describedby', dialogContentId);
|
|
|
|
if (options.ariaLabel) {
|
|
$mdAria.expect(element, 'aria-label', options.ariaLabel);
|
|
}
|
|
else {
|
|
$mdAria.expectAsync(element, 'aria-label', function() {
|
|
var words = dialogContent.text().split(/\s+/);
|
|
if (words.length > 3) words = words.slice(0, 3).concat('...');
|
|
return words.join(' ');
|
|
});
|
|
}
|
|
|
|
// Set up elements before and after the dialog content to capture focus and
|
|
// redirect back into the dialog.
|
|
topFocusTrap = document.createElement('div');
|
|
topFocusTrap.classList.add('md-dialog-focus-trap');
|
|
topFocusTrap.tabIndex = 0;
|
|
|
|
bottomFocusTrap = topFocusTrap.cloneNode(false);
|
|
|
|
// When focus is about to move out of the dialog, we want to intercept it and redirect it
|
|
// back to the dialog element.
|
|
var focusHandler = function() {
|
|
element.focus();
|
|
};
|
|
topFocusTrap.addEventListener('focus', focusHandler);
|
|
bottomFocusTrap.addEventListener('focus', focusHandler);
|
|
|
|
// The top focus trap inserted immeidately before the md-dialog element (as a sibling).
|
|
// The bottom focus trap is inserted at the very end of the md-dialog element (as a child).
|
|
element[0].parentNode.insertBefore(topFocusTrap, element[0]);
|
|
element.after(bottomFocusTrap);
|
|
}
|
|
|
|
/**
|
|
* Prevents screen reader interaction behind modal window
|
|
* on swipe interfaces
|
|
*/
|
|
function lockScreenReader(element, options) {
|
|
var isHidden = true;
|
|
|
|
// get raw DOM node
|
|
walkDOM(element[0]);
|
|
|
|
options.unlockScreenReader = function() {
|
|
isHidden = false;
|
|
walkDOM(element[0]);
|
|
|
|
options.unlockScreenReader = null;
|
|
};
|
|
|
|
/**
|
|
* Walk DOM to apply or remove aria-hidden on sibling nodes
|
|
* and parent sibling nodes
|
|
*
|
|
*/
|
|
function walkDOM(element) {
|
|
while (element.parentNode) {
|
|
if (element === document.body) {
|
|
return;
|
|
}
|
|
var children = element.parentNode.children;
|
|
for (var i = 0; i < children.length; i++) {
|
|
// skip over child if it is an ascendant of the dialog
|
|
// or a script or style tag
|
|
if (element !== children[i] && !isNodeOneOf(children[i], ['SCRIPT', 'STYLE'])) {
|
|
children[i].setAttribute('aria-hidden', isHidden);
|
|
}
|
|
}
|
|
|
|
walkDOM(element = element.parentNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure the dialog container fill-stretches to the viewport
|
|
*/
|
|
function stretchDialogContainerToViewport(container, options) {
|
|
var isFixed = $window.getComputedStyle($document[0].body).position == 'fixed';
|
|
var backdrop = options.backdrop ? $window.getComputedStyle(options.backdrop[0]) : null;
|
|
var height = backdrop ? Math.min($document[0].body.clientHeight, Math.ceil(Math.abs(parseInt(backdrop.height, 10)))) : 0;
|
|
|
|
var previousStyles = {
|
|
top: container.css('top'),
|
|
height: container.css('height')
|
|
};
|
|
|
|
// If the body is fixed, determine the distance to the viewport in relative from the parent.
|
|
var parentTop = Math.abs(options.parent[0].getBoundingClientRect().top);
|
|
|
|
container.css({
|
|
top: (isFixed ? parentTop : 0) + 'px',
|
|
height: height ? height + 'px' : '100%'
|
|
});
|
|
|
|
return function() {
|
|
// Reverts the modified styles back to the previous values.
|
|
// This is needed for contentElements, which should have the same styles after close
|
|
// as before.
|
|
container.css(previousStyles);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Dialog open and pop-in animation
|
|
*/
|
|
function dialogPopIn(container, options) {
|
|
// Add the `md-dialog-container` to the DOM
|
|
options.parent.append(container);
|
|
options.reverseContainerStretch = stretchDialogContainerToViewport(container, options);
|
|
|
|
var dialogEl = container.find('md-dialog');
|
|
var animator = $mdUtil.dom.animator;
|
|
var buildTranslateToOrigin = animator.calculateZoomToOrigin;
|
|
var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'};
|
|
var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin));
|
|
var to = animator.toTransformCss(""); // defaults to center display (or parent or $rootElement)
|
|
|
|
dialogEl.toggleClass('md-dialog-fullscreen', !!options.fullscreen);
|
|
|
|
return animator
|
|
.translate3d(dialogEl, from, to, translateOptions)
|
|
.then(function(animateReversal) {
|
|
|
|
// Build a reversal translate function synced to this translation...
|
|
options.reverseAnimate = function() {
|
|
delete options.reverseAnimate;
|
|
|
|
if (options.closeTo) {
|
|
// Using the opposite classes to create a close animation to the closeTo element
|
|
translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'};
|
|
from = to;
|
|
to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo));
|
|
|
|
return animator
|
|
.translate3d(dialogEl, from, to,translateOptions);
|
|
}
|
|
|
|
return animateReversal(
|
|
to = animator.toTransformCss(
|
|
// in case the origin element has moved or is hidden,
|
|
// let's recalculate the translateCSS
|
|
buildTranslateToOrigin(dialogEl, options.origin)
|
|
)
|
|
);
|
|
|
|
};
|
|
|
|
// Function to revert the generated animation styles on the dialog element.
|
|
// Useful when using a contentElement instead of a template.
|
|
options.clearAnimate = function() {
|
|
delete options.clearAnimate;
|
|
|
|
// Remove the transition classes, added from $animateCSS, since those can't be removed
|
|
// by reversely running the animator.
|
|
dialogEl.removeClass([
|
|
translateOptions.transitionOutClass,
|
|
translateOptions.transitionInClass
|
|
].join(' '));
|
|
|
|
// Run the animation reversely to remove the previous added animation styles.
|
|
return animator.translate3d(dialogEl, to, animator.toTransformCss(''), {});
|
|
};
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Dialog close and pop-out animation
|
|
*/
|
|
function dialogPopOut(container, options) {
|
|
return options.reverseAnimate().then(function() {
|
|
if (options.contentElement) {
|
|
// When we use a contentElement, we want the element to be the same as before.
|
|
// That means, that we have to clear all the animation properties, like transform.
|
|
options.clearAnimate();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Utility function to filter out raw DOM nodes
|
|
*/
|
|
function isNodeOneOf(elem, nodeTypeArray) {
|
|
if (nodeTypeArray.indexOf(elem.nodeName) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
ngmaterial.components.dialog = angular.module("material.components.dialog");
|