b3aeb7764e733a85ec67e715b7d9e2b3aaf77703
[vnfsdk/refrepo.git] /
1 /*!
2  * Angular Material Design
3  * https://github.com/angular/material
4  * @license MIT
5  * v1.1.3
6  */
7 goog.provide('ngmaterial.components.dialog');
8 goog.require('ngmaterial.components.backdrop');
9 goog.require('ngmaterial.core');
10 /**
11  * @ngdoc module
12  * @name material.components.dialog
13  */
14 MdDialogDirective['$inject'] = ["$$rAF", "$mdTheming", "$mdDialog"];
15 MdDialogProvider['$inject'] = ["$$interimElementProvider"];
16 angular
17   .module('material.components.dialog', [
18     'material.core',
19     'material.components.backdrop'
20   ])
21   .directive('mdDialog', MdDialogDirective)
22   .provider('$mdDialog', MdDialogProvider);
23
24 /**
25  * @ngdoc directive
26  * @name mdDialog
27  * @module material.components.dialog
28  *
29  * @restrict E
30  *
31  * @description
32  * `<md-dialog>` - The dialog's template must be inside this element.
33  *
34  * Inside, use an `<md-dialog-content>` element for the dialog's content, and use
35  * an `<md-dialog-actions>` element for the dialog's actions.
36  *
37  * ## CSS
38  * - `.md-dialog-content` - class that sets the padding on the content as the spec file
39  *
40  * ## Notes
41  * - If you specify an `id` for the `<md-dialog>`, the `<md-dialog-content>` will have the same `id`
42  * prefixed with `dialogContent_`.
43  *
44  * @usage
45  * ### Dialog template
46  * <hljs lang="html">
47  * <md-dialog aria-label="List dialog">
48  *   <md-dialog-content>
49  *     <md-list>
50  *       <md-list-item ng-repeat="item in items">
51  *         <p>Number {{item}}</p>
52  *       </md-list-item>
53  *     </md-list>
54  *   </md-dialog-content>
55  *   <md-dialog-actions>
56  *     <md-button ng-click="closeDialog()" class="md-primary">Close Dialog</md-button>
57  *   </md-dialog-actions>
58  * </md-dialog>
59  * </hljs>
60  */
61 function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
62   return {
63     restrict: 'E',
64     link: function(scope, element) {
65       element.addClass('_md');     // private md component indicator for styling
66
67       $mdTheming(element);
68       $$rAF(function() {
69         var images;
70         var content = element[0].querySelector('md-dialog-content');
71
72         if (content) {
73           images = content.getElementsByTagName('img');
74           addOverflowClass();
75           //-- delayed image loading may impact scroll height, check after images are loaded
76           angular.element(images).on('load', addOverflowClass);
77         }
78
79         scope.$on('$destroy', function() {
80           $mdDialog.destroy(element);
81         });
82
83         /**
84          *
85          */
86         function addOverflowClass() {
87           element.toggleClass('md-content-overflow', content.scrollHeight > content.clientHeight);
88         }
89
90
91       });
92     }
93   };
94 }
95
96 /**
97  * @ngdoc service
98  * @name $mdDialog
99  * @module material.components.dialog
100  *
101  * @description
102  * `$mdDialog` opens a dialog over the app to inform users about critical information or require
103  *  them to make decisions. There are two approaches for setup: a simple promise API
104  *  and regular object syntax.
105  *
106  * ## Restrictions
107  *
108  * - The dialog is always given an isolate scope.
109  * - The dialog's template must have an outer `<md-dialog>` element.
110  *   Inside, use an `<md-dialog-content>` element for the dialog's content, and use
111  *   an `<md-dialog-actions>` element for the dialog's actions.
112  * - Dialogs must cover the entire application to keep interactions inside of them.
113  * Use the `parent` option to change where dialogs are appended.
114  *
115  * ## Sizing
116  * - Complex dialogs can be sized with `flex="percentage"`, i.e. `flex="66"`.
117  * - Default max-width is 80% of the `rootElement` or `parent`.
118  *
119  * ## CSS
120  * - `.md-dialog-content` - class that sets the padding on the content as the spec file
121  *
122  * @usage
123  * <hljs lang="html">
124  * <div  ng-app="demoApp" ng-controller="EmployeeController">
125  *   <div>
126  *     <md-button ng-click="showAlert()" class="md-raised md-warn">
127  *       Employee Alert!
128  *       </md-button>
129  *   </div>
130  *   <div>
131  *     <md-button ng-click="showDialog($event)" class="md-raised">
132  *       Custom Dialog
133  *       </md-button>
134  *   </div>
135  *   <div>
136  *     <md-button ng-click="closeAlert()" ng-disabled="!hasAlert()" class="md-raised">
137  *       Close Alert
138  *     </md-button>
139  *   </div>
140  *   <div>
141  *     <md-button ng-click="showGreeting($event)" class="md-raised md-primary" >
142  *       Greet Employee
143  *       </md-button>
144  *   </div>
145  * </div>
146  * </hljs>
147  *
148  * ### JavaScript: object syntax
149  * <hljs lang="js">
150  * (function(angular, undefined){
151  *   "use strict";
152  *
153  *   angular
154  *    .module('demoApp', ['ngMaterial'])
155  *    .controller('AppCtrl', AppController);
156  *
157  *   function AppController($scope, $mdDialog) {
158  *     var alert;
159  *     $scope.showAlert = showAlert;
160  *     $scope.showDialog = showDialog;
161  *     $scope.items = [1, 2, 3];
162  *
163  *     // Internal method
164  *     function showAlert() {
165  *       alert = $mdDialog.alert({
166  *         title: 'Attention',
167  *         textContent: 'This is an example of how easy dialogs can be!',
168  *         ok: 'Close'
169  *       });
170  *
171  *       $mdDialog
172  *         .show( alert )
173  *         .finally(function() {
174  *           alert = undefined;
175  *         });
176  *     }
177  *
178  *     function showDialog($event) {
179  *        var parentEl = angular.element(document.body);
180  *        $mdDialog.show({
181  *          parent: parentEl,
182  *          targetEvent: $event,
183  *          template:
184  *            '<md-dialog aria-label="List dialog">' +
185  *            '  <md-dialog-content>'+
186  *            '    <md-list>'+
187  *            '      <md-list-item ng-repeat="item in items">'+
188  *            '       <p>Number {{item}}</p>' +
189  *            '      </md-item>'+
190  *            '    </md-list>'+
191  *            '  </md-dialog-content>' +
192  *            '  <md-dialog-actions>' +
193  *            '    <md-button ng-click="closeDialog()" class="md-primary">' +
194  *            '      Close Dialog' +
195  *            '    </md-button>' +
196  *            '  </md-dialog-actions>' +
197  *            '</md-dialog>',
198  *          locals: {
199  *            items: $scope.items
200  *          },
201  *          controller: DialogController
202  *       });
203  *       function DialogController($scope, $mdDialog, items) {
204  *         $scope.items = items;
205  *         $scope.closeDialog = function() {
206  *           $mdDialog.hide();
207  *         }
208  *       }
209  *     }
210  *   }
211  * })(angular);
212  * </hljs>
213  *
214  * ### Multiple Dialogs
215  * Using the `multiple` option for the `$mdDialog` service allows developers to show multiple dialogs
216  * at the same time.
217  *
218  * <hljs lang="js">
219  *   // From plain options
220  *   $mdDialog.show({
221  *     multiple: true
222  *   });
223  *
224  *   // From a dialog preset
225  *   $mdDialog.show(
226  *     $mdDialog
227  *       .alert()
228  *       .multiple(true)
229  *   );
230  *
231  * </hljs>
232  *
233  * ### Pre-Rendered Dialogs
234  * By using the `contentElement` option, it is possible to use an already existing element in the DOM.
235  *
236  * > Pre-rendered dialogs will be not linked to any scope and will not instantiate any new controller.<br/>
237  * > You can manually link the elements to a scope or instantiate a controller from the template (`ng-controller`)
238  *
239  * <hljs lang="js">
240  *   $scope.showPrerenderedDialog = function() {
241  *     $mdDialog.show({
242  *       contentElement: '#myStaticDialog',
243  *       parent: angular.element(document.body)
244  *     });
245  *   };
246  * </hljs>
247  *
248  * When using a string as value, `$mdDialog` will automatically query the DOM for the specified CSS selector.
249  *
250  * <hljs lang="html">
251  *   <div style="visibility: hidden">
252  *     <div class="md-dialog-container" id="myStaticDialog">
253  *       <md-dialog>
254  *         This is a pre-rendered dialog.
255  *       </md-dialog>
256  *     </div>
257  *   </div>
258  * </hljs>
259  *
260  * **Notice**: It is important, to use the `.md-dialog-container` as the content element, otherwise the dialog
261  * will not show up.
262  *
263  * It also possible to use a DOM element for the `contentElement` option.
264  * - `contentElement: document.querySelector('#myStaticDialog')`
265  * - `contentElement: angular.element(TEMPLATE)`
266  *
267  * When using a `template` as content element, it will be not compiled upon open.
268  * This allows you to compile the element yourself and use it each time the dialog opens.
269  *
270  * ### Custom Presets
271  * Developers are also able to create their own preset, which can be easily used without repeating
272  * their options each time.
273  *
274  * <hljs lang="js">
275  *   $mdDialogProvider.addPreset('testPreset', {
276  *     options: function() {
277  *       return {
278  *         template:
279  *           '<md-dialog>' +
280  *             'This is a custom preset' +
281  *           '</md-dialog>',
282  *         controllerAs: 'dialog',
283  *         bindToController: true,
284  *         clickOutsideToClose: true,
285  *         escapeToClose: true
286  *       };
287  *     }
288  *   });
289  * </hljs>
290  *
291  * After you created your preset at config phase, you can easily access it.
292  *
293  * <hljs lang="js">
294  *   $mdDialog.show(
295  *     $mdDialog.testPreset()
296  *   );
297  * </hljs>
298  *
299  * ### JavaScript: promise API syntax, custom dialog template
300  * <hljs lang="js">
301  * (function(angular, undefined){
302  *   "use strict";
303  *
304  *   angular
305  *     .module('demoApp', ['ngMaterial'])
306  *     .controller('EmployeeController', EmployeeEditor)
307  *     .controller('GreetingController', GreetingController);
308  *
309  *   // Fictitious Employee Editor to show how to use simple and complex dialogs.
310  *
311  *   function EmployeeEditor($scope, $mdDialog) {
312  *     var alert;
313  *
314  *     $scope.showAlert = showAlert;
315  *     $scope.closeAlert = closeAlert;
316  *     $scope.showGreeting = showCustomGreeting;
317  *
318  *     $scope.hasAlert = function() { return !!alert };
319  *     $scope.userName = $scope.userName || 'Bobby';
320  *
321  *     // Dialog #1 - Show simple alert dialog and cache
322  *     // reference to dialog instance
323  *
324  *     function showAlert() {
325  *       alert = $mdDialog.alert()
326  *         .title('Attention, ' + $scope.userName)
327  *         .textContent('This is an example of how easy dialogs can be!')
328  *         .ok('Close');
329  *
330  *       $mdDialog
331  *           .show( alert )
332  *           .finally(function() {
333  *             alert = undefined;
334  *           });
335  *     }
336  *
337  *     // Close the specified dialog instance and resolve with 'finished' flag
338  *     // Normally this is not needed, just use '$mdDialog.hide()' to close
339  *     // the most recent dialog popup.
340  *
341  *     function closeAlert() {
342  *       $mdDialog.hide( alert, "finished" );
343  *       alert = undefined;
344  *     }
345  *
346  *     // Dialog #2 - Demonstrate more complex dialogs construction and popup.
347  *
348  *     function showCustomGreeting($event) {
349  *         $mdDialog.show({
350  *           targetEvent: $event,
351  *           template:
352  *             '<md-dialog>' +
353  *
354  *             '  <md-dialog-content>Hello {{ employee }}!</md-dialog-content>' +
355  *
356  *             '  <md-dialog-actions>' +
357  *             '    <md-button ng-click="closeDialog()" class="md-primary">' +
358  *             '      Close Greeting' +
359  *             '    </md-button>' +
360  *             '  </md-dialog-actions>' +
361  *             '</md-dialog>',
362  *           controller: 'GreetingController',
363  *           onComplete: afterShowAnimation,
364  *           locals: { employee: $scope.userName }
365  *         });
366  *
367  *         // When the 'enter' animation finishes...
368  *
369  *         function afterShowAnimation(scope, element, options) {
370  *            // post-show code here: DOM element focus, etc.
371  *         }
372  *     }
373  *
374  *     // Dialog #3 - Demonstrate use of ControllerAs and passing $scope to dialog
375  *     //             Here we used ng-controller="GreetingController as vm" and
376  *     //             $scope.vm === <controller instance>
377  *
378  *     function showCustomGreeting() {
379  *
380  *        $mdDialog.show({
381  *           clickOutsideToClose: true,
382  *
383  *           scope: $scope,        // use parent scope in template
384  *           preserveScope: true,  // do not forget this if use parent scope
385
386  *           // Since GreetingController is instantiated with ControllerAs syntax
387  *           // AND we are passing the parent '$scope' to the dialog, we MUST
388  *           // use 'vm.<xxx>' in the template markup
389  *
390  *           template: '<md-dialog>' +
391  *                     '  <md-dialog-content>' +
392  *                     '     Hi There {{vm.employee}}' +
393  *                     '  </md-dialog-content>' +
394  *                     '</md-dialog>',
395  *
396  *           controller: function DialogController($scope, $mdDialog) {
397  *             $scope.closeDialog = function() {
398  *               $mdDialog.hide();
399  *             }
400  *           }
401  *        });
402  *     }
403  *
404  *   }
405  *
406  *   // Greeting controller used with the more complex 'showCustomGreeting()' custom dialog
407  *
408  *   function GreetingController($scope, $mdDialog, employee) {
409  *     // Assigned from construction <code>locals</code> options...
410  *     $scope.employee = employee;
411  *
412  *     $scope.closeDialog = function() {
413  *       // Easily hides most recent dialog shown...
414  *       // no specific instance reference is needed.
415  *       $mdDialog.hide();
416  *     };
417  *   }
418  *
419  * })(angular);
420  * </hljs>
421  */
422
423 /**
424  * @ngdoc method
425  * @name $mdDialog#alert
426  *
427  * @description
428  * Builds a preconfigured dialog with the specified message.
429  *
430  * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
431  *
432  * - $mdDialogPreset#title(string) - Sets the alert title.
433  * - $mdDialogPreset#textContent(string) - Sets the alert message.
434  * - $mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize
435  *     module to be loaded. HTML is not run through Angular's compiler.
436  * - $mdDialogPreset#ok(string) - Sets the alert "Okay" button text.
437  * - $mdDialogPreset#theme(string) - Sets the theme of the alert dialog.
438  * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
439  *     the location of the click will be used as the starting point for the opening animation
440  *     of the the dialog.
441  *
442  */
443
444 /**
445  * @ngdoc method
446  * @name $mdDialog#confirm
447  *
448  * @description
449  * Builds a preconfigured dialog with the specified message. You can call show and the promise returned
450  * will be resolved only if the user clicks the confirm action on the dialog.
451  *
452  * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
453  *
454  * Additionally, it supports the following methods:
455  *
456  * - $mdDialogPreset#title(string) - Sets the confirm title.
457  * - $mdDialogPreset#textContent(string) - Sets the confirm message.
458  * - $mdDialogPreset#htmlContent(string) - Sets the confirm message as HTML. Requires ngSanitize
459  *     module to be loaded. HTML is not run through Angular's compiler.
460  * - $mdDialogPreset#ok(string) - Sets the confirm "Okay" button text.
461  * - $mdDialogPreset#cancel(string) - Sets the confirm "Cancel" button text.
462  * - $mdDialogPreset#theme(string) - Sets the theme of the confirm dialog.
463  * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
464  *     the location of the click will be used as the starting point for the opening animation
465  *     of the the dialog.
466  *
467  */
468
469 /**
470  * @ngdoc method
471  * @name $mdDialog#prompt
472  *
473  * @description
474  * Builds a preconfigured dialog with the specified message and input box. You can call show and the promise returned
475  * will be resolved only if the user clicks the prompt action on the dialog, passing the input value as the first argument.
476  *
477  * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
478  *
479  * Additionally, it supports the following methods:
480  *
481  * - $mdDialogPreset#title(string) - Sets the prompt title.
482  * - $mdDialogPreset#textContent(string) - Sets the prompt message.
483  * - $mdDialogPreset#htmlContent(string) - Sets the prompt message as HTML. Requires ngSanitize
484  *     module to be loaded. HTML is not run through Angular's compiler.
485  * - $mdDialogPreset#placeholder(string) - Sets the placeholder text for the input.
486  * - $mdDialogPreset#initialValue(string) - Sets the initial value for the prompt input.
487  * - $mdDialogPreset#ok(string) - Sets the prompt "Okay" button text.
488  * - $mdDialogPreset#cancel(string) - Sets the prompt "Cancel" button text.
489  * - $mdDialogPreset#theme(string) - Sets the theme of the prompt dialog.
490  * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option,
491  *     the location of the click will be used as the starting point for the opening animation
492  *     of the the dialog.
493  *
494  */
495
496 /**
497  * @ngdoc method
498  * @name $mdDialog#show
499  *
500  * @description
501  * Show a dialog with the specified options.
502  *
503  * @param {object} optionsOrPreset Either provide an `$mdDialogPreset` returned from `alert()`, and
504  * `confirm()`, or an options object with the following properties:
505  *   - `templateUrl` - `{string=}`: The url of a template that will be used as the content
506  *   of the dialog.
507  *   - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML
508  *      with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce).
509  *      This template should **never** be constructed with any kind of user input or user data.
510  *   - `contentElement` - `{string|Element}`: Instead of using a template, which will be compiled each time a
511  *     dialog opens, you can also use a DOM element.<br/>
512  *     * When specifying an element, which is present on the DOM, `$mdDialog` will temporary fetch the element into
513  *       the dialog and restores it at the old DOM position upon close.
514  *     * When specifying a string, the string be used as a CSS selector, to lookup for the element in the DOM.
515  *   - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a
516  *     `<md-dialog>` tag if one is not provided. Defaults to true. Can be disabled if you provide a
517  *     custom dialog directive.
518  *   - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
519  *     the location of the click will be used as the starting point for the opening animation
520  *     of the the dialog.
521  *   - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object
522  *     that is used to determine the bounds (top, left, height, width) from which the Dialog will
523  *     originate.
524  *   - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object
525  *     that is used to determine the bounds (top, left, height, width) to which the Dialog will
526  *     target.
527  *   - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
528  *     it will create a new isolate scope.
529  *     This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
530  *   - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false
531  *   - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the dialog is open.
532  *     Default true.
533  *   - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop behind the dialog.
534  *     Default true.
535  *   - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the dialog to
536  *     close it. Default false.
537  *   - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the dialog.
538  *     Default true.
539  *   - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on open. Only disable if
540  *     focusing some other way, as focus management is required for dialogs to be accessible.
541  *     Defaults to true.
542  *   - `controller` - `{function|string=}`: The controller to associate with the dialog. The controller
543  *     will be injected with the local `$mdDialog`, which passes along a scope for the dialog.
544  *   - `locals` - `{object=}`: An object containing key/value pairs. The keys will be used as names
545  *     of values to inject into the controller. For example, `locals: {three: 3}` would inject
546  *     `three` into the controller, with the value 3. If `bindToController` is true, they will be
547  *     copied to the controller instead.
548  *   - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
549  *   - `resolve` - `{object=}`: Similar to locals, except it takes promises as values, and the
550  *     dialog will not open until all of the promises resolve.
551  *   - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
552  *   - `parent` - `{element=}`: The element to append the dialog to. Defaults to appending
553  *     to the root element of the application.
554  *   - `onShowing` - `function(scope, element)`: Callback function used to announce the show() action is
555  *     starting.
556  *   - `onComplete` - `function(scope, element)`: Callback function used to announce when the show() action is
557  *     finished.
558  *   - `onRemoving` - `function(element, removePromise)`: Callback function used to announce the
559  *      close/hide() action is starting. This allows developers to run custom animations
560  *      in parallel the close animations.
561  *   - `fullscreen` `{boolean=}`: An option to toggle whether the dialog should show in fullscreen
562  *      or not. Defaults to `false`.
563  * @returns {promise} A promise that can be resolved with `$mdDialog.hide()` or
564  * rejected with `$mdDialog.cancel()`.
565  */
566
567 /**
568  * @ngdoc method
569  * @name $mdDialog#hide
570  *
571  * @description
572  * Hide an existing dialog and resolve the promise returned from `$mdDialog.show()`.
573  *
574  * @param {*=} response An argument for the resolved promise.
575  *
576  * @returns {promise} A promise that is resolved when the dialog has been closed.
577  */
578
579 /**
580  * @ngdoc method
581  * @name $mdDialog#cancel
582  *
583  * @description
584  * Hide an existing dialog and reject the promise returned from `$mdDialog.show()`.
585  *
586  * @param {*=} response An argument for the rejected promise.
587  *
588  * @returns {promise} A promise that is resolved when the dialog has been closed.
589  */
590
591 function MdDialogProvider($$interimElementProvider) {
592   // Elements to capture and redirect focus when the user presses tab at the dialog boundary.
593   advancedDialogOptions['$inject'] = ["$mdDialog", "$mdConstant"];
594   dialogDefaultOptions['$inject'] = ["$mdDialog", "$mdAria", "$mdUtil", "$mdConstant", "$animate", "$document", "$window", "$rootElement", "$log", "$injector", "$mdTheming", "$interpolate", "$mdInteraction"];
595   var topFocusTrap, bottomFocusTrap;
596
597   return $$interimElementProvider('$mdDialog')
598     .setDefaults({
599       methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose',
600           'targetEvent', 'closeTo', 'openFrom', 'parent', 'fullscreen', 'multiple'],
601       options: dialogDefaultOptions
602     })
603     .addPreset('alert', {
604       methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'theme',
605           'css'],
606       options: advancedDialogOptions
607     })
608     .addPreset('confirm', {
609       methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'cancel',
610           'theme', 'css'],
611       options: advancedDialogOptions
612     })
613     .addPreset('prompt', {
614       methods: ['title', 'htmlContent', 'textContent', 'initialValue', 'content', 'placeholder', 'ariaLabel',
615           'ok', 'cancel', 'theme', 'css'],
616       options: advancedDialogOptions
617     });
618
619   /* ngInject */
620   function advancedDialogOptions($mdDialog, $mdConstant) {
621     return {
622       template: [
623         '<md-dialog md-theme="{{ dialog.theme || dialog.defaultTheme }}" aria-label="{{ dialog.ariaLabel }}" ng-class="dialog.css">',
624         '  <md-dialog-content class="md-dialog-content" role="document" tabIndex="-1">',
625         '    <h2 class="md-title">{{ dialog.title }}</h2>',
626         '    <div ng-if="::dialog.mdHtmlContent" class="md-dialog-content-body" ',
627         '        ng-bind-html="::dialog.mdHtmlContent"></div>',
628         '    <div ng-if="::!dialog.mdHtmlContent" class="md-dialog-content-body">',
629         '      <p>{{::dialog.mdTextContent}}</p>',
630         '    </div>',
631         '    <md-input-container md-no-float ng-if="::dialog.$type == \'prompt\'" class="md-prompt-input-container">',
632         '      <input ng-keypress="dialog.keypress($event)" md-autofocus ng-model="dialog.result" ' +
633         '             placeholder="{{::dialog.placeholder}}">',
634         '    </md-input-container>',
635         '  </md-dialog-content>',
636         '  <md-dialog-actions>',
637         '    <md-button ng-if="dialog.$type === \'confirm\' || dialog.$type === \'prompt\'"' +
638         '               ng-click="dialog.abort()" class="md-primary md-cancel-button">',
639         '      {{ dialog.cancel }}',
640         '    </md-button>',
641         '    <md-button ng-click="dialog.hide()" class="md-primary md-confirm-button" md-autofocus="dialog.$type===\'alert\'">',
642         '      {{ dialog.ok }}',
643         '    </md-button>',
644         '  </md-dialog-actions>',
645         '</md-dialog>'
646       ].join('').replace(/\s\s+/g, ''),
647       controller: function mdDialogCtrl() {
648         var isPrompt = this.$type == 'prompt';
649
650         if (isPrompt && this.initialValue) {
651           this.result = this.initialValue;
652         }
653
654         this.hide = function() {
655           $mdDialog.hide(isPrompt ? this.result : true);
656         };
657         this.abort = function() {
658           $mdDialog.cancel();
659         };
660         this.keypress = function($event) {
661           if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
662             $mdDialog.hide(this.result);
663           }
664         };
665       },
666       controllerAs: 'dialog',
667       bindToController: true,
668     };
669   }
670
671   /* ngInject */
672   function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document, $window, $rootElement,
673                                 $log, $injector, $mdTheming, $interpolate, $mdInteraction) {
674
675     return {
676       hasBackdrop: true,
677       isolateScope: true,
678       onCompiling: beforeCompile,
679       onShow: onShow,
680       onShowing: beforeShow,
681       onRemove: onRemove,
682       clickOutsideToClose: false,
683       escapeToClose: true,
684       targetEvent: null,
685       closeTo: null,
686       openFrom: null,
687       focusOnOpen: true,
688       disableParentScroll: true,
689       autoWrap: true,
690       fullscreen: false,
691       transformTemplate: function(template, options) {
692         // Make the dialog container focusable, because otherwise the focus will be always redirected to
693         // an element outside of the container, and the focus trap won't work probably..
694         // Also the tabindex is needed for the `escapeToClose` functionality, because
695         // the keyDown event can't be triggered when the focus is outside of the container.
696         var startSymbol = $interpolate.startSymbol();
697         var endSymbol = $interpolate.endSymbol();
698         var theme = startSymbol + (options.themeWatch ? '' : '::') + 'theme' + endSymbol;
699         return '<div class="md-dialog-container" tabindex="-1" md-theme="' + theme + '">' + validatedTemplate(template) + '</div>';
700
701         /**
702          * The specified template should contain a <md-dialog> wrapper element....
703          */
704         function validatedTemplate(template) {
705           if (options.autoWrap && !/<\/md-dialog>/g.test(template)) {
706             return '<md-dialog>' + (template || '') + '</md-dialog>';
707           } else {
708             return template || '';
709           }
710         }
711       }
712     };
713
714     function beforeCompile(options) {
715       // Automatically apply the theme, if the user didn't specify a theme explicitly.
716       // Those option changes need to be done, before the compilation has started, because otherwise
717       // the option changes will be not available in the $mdCompilers locales.
718       options.defaultTheme = $mdTheming.defaultTheme();
719
720       detectTheming(options);
721     }
722
723     function beforeShow(scope, element, options, controller) {
724
725       if (controller) {
726         var mdHtmlContent = controller.htmlContent || options.htmlContent || '';
727         var mdTextContent = controller.textContent || options.textContent ||
728             controller.content || options.content || '';
729
730         if (mdHtmlContent && !$injector.has('$sanitize')) {
731           throw Error('The ngSanitize module must be loaded in order to use htmlContent.');
732         }
733
734         if (mdHtmlContent && mdTextContent) {
735           throw Error('md-dialog cannot have both `htmlContent` and `textContent`');
736         }
737
738         // Only assign the content if nothing throws, otherwise it'll still be compiled.
739         controller.mdHtmlContent = mdHtmlContent;
740         controller.mdTextContent = mdTextContent;
741       }
742     }
743
744     /** Show method for dialogs */
745     function onShow(scope, element, options, controller) {
746       angular.element($document[0].body).addClass('md-dialog-is-showing');
747
748       var dialogElement = element.find('md-dialog');
749
750       // Once a dialog has `ng-cloak` applied on his template the dialog animation will not work properly.
751       // This is a very common problem, so we have to notify the developer about this.
752       if (dialogElement.hasClass('ng-cloak')) {
753         var message = '$mdDialog: using `<md-dialog ng-cloak>` will affect the dialog opening animations.';
754         $log.warn( message, element[0] );
755       }
756
757       captureParentAndFromToElements(options);
758       configureAria(dialogElement, options);
759       showBackdrop(scope, element, options);
760       activateListeners(element, options);
761
762       return dialogPopIn(element, options)
763         .then(function() {
764           lockScreenReader(element, options);
765           warnDeprecatedActions();
766           focusOnOpen();
767         });
768
769       /**
770        * Check to see if they used the deprecated .md-actions class and log a warning
771        */
772       function warnDeprecatedActions() {
773         if (element[0].querySelector('.md-actions')) {
774           $log.warn('Using a class of md-actions is deprecated, please use <md-dialog-actions>.');
775         }
776       }
777
778       /**
779        * For alerts, focus on content... otherwise focus on
780        * the close button (or equivalent)
781        */
782       function focusOnOpen() {
783         if (options.focusOnOpen) {
784           var target = $mdUtil.findFocusTarget(element) || findCloseButton() || dialogElement;
785           target.focus();
786         }
787
788         /**
789          * If no element with class dialog-close, try to find the last
790          * button child in md-actions and assume it is a close button.
791          *
792          * If we find no actions at all, log a warning to the console.
793          */
794         function findCloseButton() {
795           return element[0].querySelector('.dialog-close, md-dialog-actions button:last-child');
796         }
797       }
798     }
799
800     /**
801      * Remove function for all dialogs
802      */
803     function onRemove(scope, element, options) {
804       options.deactivateListeners();
805       options.unlockScreenReader();
806       options.hideBackdrop(options.$destroy);
807
808       // Remove the focus traps that we added earlier for keeping focus within the dialog.
809       if (topFocusTrap && topFocusTrap.parentNode) {
810         topFocusTrap.parentNode.removeChild(topFocusTrap);
811       }
812
813       if (bottomFocusTrap && bottomFocusTrap.parentNode) {
814         bottomFocusTrap.parentNode.removeChild(bottomFocusTrap);
815       }
816
817       // For navigation $destroy events, do a quick, non-animated removal,
818       // but for normal closes (from clicks, etc) animate the removal
819       return !!options.$destroy ? detachAndClean() : animateRemoval().then( detachAndClean );
820
821       /**
822        * For normal closes, animate the removal.
823        * For forced closes (like $destroy events), skip the animations
824        */
825       function animateRemoval() {
826         return dialogPopOut(element, options);
827       }
828
829       /**
830        * Detach the element
831        */
832       function detachAndClean() {
833         angular.element($document[0].body).removeClass('md-dialog-is-showing');
834
835         // Reverse the container stretch if using a content element.
836         if (options.contentElement) {
837           options.reverseContainerStretch();
838         }
839
840         // Exposed cleanup function from the $mdCompiler.
841         options.cleanupElement();
842
843         // Restores the focus to the origin element if the last interaction upon opening was a keyboard.
844         if (!options.$destroy && options.originInteraction === 'keyboard') {
845           options.origin.focus();
846         }
847       }
848     }
849
850     function detectTheming(options) {
851       // Once the user specifies a targetEvent, we will automatically try to find the correct
852       // nested theme.
853       var targetEl;
854       if (options.targetEvent && options.targetEvent.target) {
855         targetEl = angular.element(options.targetEvent.target);
856       }
857
858       var themeCtrl = targetEl && targetEl.controller('mdTheme');
859
860       if (!themeCtrl) {
861         return;
862       }
863
864       options.themeWatch = themeCtrl.$shouldWatch;
865
866       var theme = options.theme || themeCtrl.$mdTheme;
867
868       if (theme) {
869         options.scope.theme = theme;
870       }
871
872       var unwatch = themeCtrl.registerChanges(function (newTheme) {
873         options.scope.theme = newTheme;
874
875         if (!options.themeWatch) {
876           unwatch();
877         }
878       });
879     }
880
881     /**
882      * Capture originator/trigger/from/to element information (if available)
883      * and the parent container for the dialog; defaults to the $rootElement
884      * unless overridden in the options.parent
885      */
886     function captureParentAndFromToElements(options) {
887           options.origin = angular.extend({
888             element: null,
889             bounds: null,
890             focus: angular.noop
891           }, options.origin || {});
892
893           options.parent   = getDomElement(options.parent, $rootElement);
894           options.closeTo  = getBoundingClientRect(getDomElement(options.closeTo));
895           options.openFrom = getBoundingClientRect(getDomElement(options.openFrom));
896
897           if ( options.targetEvent ) {
898             options.origin = getBoundingClientRect(options.targetEvent.target, options.origin);
899             options.originInteraction = $mdInteraction.getLastInteractionType();
900           }
901
902
903           /**
904            * Identify the bounding RECT for the target element
905            *
906            */
907           function getBoundingClientRect (element, orig) {
908             var source = angular.element((element || {}));
909             if (source && source.length) {
910               // Compute and save the target element's bounding rect, so that if the
911               // element is hidden when the dialog closes, we can shrink the dialog
912               // back to the same position it expanded from.
913               //
914               // Checking if the source is a rect object or a DOM element
915               var bounds = {top:0,left:0,height:0,width:0};
916               var hasFn = angular.isFunction(source[0].getBoundingClientRect);
917
918               return angular.extend(orig || {}, {
919                   element : hasFn ? source : undefined,
920                   bounds  : hasFn ? source[0].getBoundingClientRect() : angular.extend({}, bounds, source[0]),
921                   focus   : angular.bind(source, source.focus),
922               });
923             }
924           }
925
926           /**
927            * If the specifier is a simple string selector, then query for
928            * the DOM element.
929            */
930           function getDomElement(element, defaultElement) {
931             if (angular.isString(element)) {
932               element = $document[0].querySelector(element);
933             }
934
935             // If we have a reference to a raw dom element, always wrap it in jqLite
936             return angular.element(element || defaultElement);
937           }
938
939         }
940
941     /**
942      * Listen for escape keys and outside clicks to auto close
943      */
944     function activateListeners(element, options) {
945       var window = angular.element($window);
946       var onWindowResize = $mdUtil.debounce(function() {
947         stretchDialogContainerToViewport(element, options);
948       }, 60);
949
950       var removeListeners = [];
951       var smartClose = function() {
952         // Only 'confirm' dialogs have a cancel button... escape/clickOutside will
953         // cancel or fallback to hide.
954         var closeFn = ( options.$type == 'alert' ) ? $mdDialog.hide : $mdDialog.cancel;
955         $mdUtil.nextTick(closeFn, true);
956       };
957
958       if (options.escapeToClose) {
959         var parentTarget = options.parent;
960         var keyHandlerFn = function(ev) {
961           if (ev.keyCode === $mdConstant.KEY_CODE.ESCAPE) {
962             ev.stopPropagation();
963             ev.preventDefault();
964
965             smartClose();
966           }
967         };
968
969         // Add keydown listeners
970         element.on('keydown', keyHandlerFn);
971         parentTarget.on('keydown', keyHandlerFn);
972
973         // Queue remove listeners function
974         removeListeners.push(function() {
975
976           element.off('keydown', keyHandlerFn);
977           parentTarget.off('keydown', keyHandlerFn);
978
979         });
980       }
981
982       // Register listener to update dialog on window resize
983       window.on('resize', onWindowResize);
984
985       removeListeners.push(function() {
986         window.off('resize', onWindowResize);
987       });
988
989       if (options.clickOutsideToClose) {
990         var target = element;
991         var sourceElem;
992
993         // Keep track of the element on which the mouse originally went down
994         // so that we can only close the backdrop when the 'click' started on it.
995         // A simple 'click' handler does not work,
996         // it sets the target object as the element the mouse went down on.
997         var mousedownHandler = function(ev) {
998           sourceElem = ev.target;
999         };
1000
1001         // We check if our original element and the target is the backdrop
1002         // because if the original was the backdrop and the target was inside the dialog
1003         // we don't want to dialog to close.
1004         var mouseupHandler = function(ev) {
1005           if (sourceElem === target[0] && ev.target === target[0]) {
1006             ev.stopPropagation();
1007             ev.preventDefault();
1008
1009             smartClose();
1010           }
1011         };
1012
1013         // Add listeners
1014         target.on('mousedown', mousedownHandler);
1015         target.on('mouseup', mouseupHandler);
1016
1017         // Queue remove listeners function
1018         removeListeners.push(function() {
1019           target.off('mousedown', mousedownHandler);
1020           target.off('mouseup', mouseupHandler);
1021         });
1022       }
1023
1024       // Attach specific `remove` listener handler
1025       options.deactivateListeners = function() {
1026         removeListeners.forEach(function(removeFn) {
1027           removeFn();
1028         });
1029         options.deactivateListeners = null;
1030       };
1031     }
1032
1033     /**
1034      * Show modal backdrop element...
1035      */
1036     function showBackdrop(scope, element, options) {
1037
1038       if (options.disableParentScroll) {
1039         // !! DO this before creating the backdrop; since disableScrollAround()
1040         //    configures the scroll offset; which is used by mdBackDrop postLink()
1041         options.restoreScroll = $mdUtil.disableScrollAround(element, options.parent);
1042       }
1043
1044       if (options.hasBackdrop) {
1045         options.backdrop = $mdUtil.createBackdrop(scope, "md-dialog-backdrop md-opaque");
1046         $animate.enter(options.backdrop, options.parent);
1047       }
1048
1049       /**
1050        * Hide modal backdrop element...
1051        */
1052       options.hideBackdrop = function hideBackdrop($destroy) {
1053         if (options.backdrop) {
1054           if ( !!$destroy ) options.backdrop.remove();
1055           else              $animate.leave(options.backdrop);
1056         }
1057
1058
1059         if (options.disableParentScroll) {
1060           options.restoreScroll && options.restoreScroll();
1061           delete options.restoreScroll;
1062         }
1063
1064         options.hideBackdrop = null;
1065       };
1066     }
1067
1068     /**
1069      * Inject ARIA-specific attributes appropriate for Dialogs
1070      */
1071     function configureAria(element, options) {
1072
1073       var role = (options.$type === 'alert') ? 'alertdialog' : 'dialog';
1074       var dialogContent = element.find('md-dialog-content');
1075       var existingDialogId = element.attr('id');
1076       var dialogContentId = 'dialogContent_' + (existingDialogId || $mdUtil.nextUid());
1077
1078       element.attr({
1079         'role': role,
1080         'tabIndex': '-1'
1081       });
1082
1083       if (dialogContent.length === 0) {
1084         dialogContent = element;
1085         // If the dialog element already had an ID, don't clobber it.
1086         if (existingDialogId) {
1087           dialogContentId = existingDialogId;
1088         }
1089       }
1090
1091       dialogContent.attr('id', dialogContentId);
1092       element.attr('aria-describedby', dialogContentId);
1093
1094       if (options.ariaLabel) {
1095         $mdAria.expect(element, 'aria-label', options.ariaLabel);
1096       }
1097       else {
1098         $mdAria.expectAsync(element, 'aria-label', function() {
1099           var words = dialogContent.text().split(/\s+/);
1100           if (words.length > 3) words = words.slice(0, 3).concat('...');
1101           return words.join(' ');
1102         });
1103       }
1104
1105       // Set up elements before and after the dialog content to capture focus and
1106       // redirect back into the dialog.
1107       topFocusTrap = document.createElement('div');
1108       topFocusTrap.classList.add('md-dialog-focus-trap');
1109       topFocusTrap.tabIndex = 0;
1110
1111       bottomFocusTrap = topFocusTrap.cloneNode(false);
1112
1113       // When focus is about to move out of the dialog, we want to intercept it and redirect it
1114       // back to the dialog element.
1115       var focusHandler = function() {
1116         element.focus();
1117       };
1118       topFocusTrap.addEventListener('focus', focusHandler);
1119       bottomFocusTrap.addEventListener('focus', focusHandler);
1120
1121       // The top focus trap inserted immeidately before the md-dialog element (as a sibling).
1122       // The bottom focus trap is inserted at the very end of the md-dialog element (as a child).
1123       element[0].parentNode.insertBefore(topFocusTrap, element[0]);
1124       element.after(bottomFocusTrap);
1125     }
1126
1127     /**
1128      * Prevents screen reader interaction behind modal window
1129      * on swipe interfaces
1130      */
1131     function lockScreenReader(element, options) {
1132       var isHidden = true;
1133
1134       // get raw DOM node
1135       walkDOM(element[0]);
1136
1137       options.unlockScreenReader = function() {
1138         isHidden = false;
1139         walkDOM(element[0]);
1140
1141         options.unlockScreenReader = null;
1142       };
1143
1144       /**
1145        * Walk DOM to apply or remove aria-hidden on sibling nodes
1146        * and parent sibling nodes
1147        *
1148        */
1149       function walkDOM(element) {
1150         while (element.parentNode) {
1151           if (element === document.body) {
1152             return;
1153           }
1154           var children = element.parentNode.children;
1155           for (var i = 0; i < children.length; i++) {
1156             // skip over child if it is an ascendant of the dialog
1157             // or a script or style tag
1158             if (element !== children[i] && !isNodeOneOf(children[i], ['SCRIPT', 'STYLE'])) {
1159               children[i].setAttribute('aria-hidden', isHidden);
1160             }
1161           }
1162
1163           walkDOM(element = element.parentNode);
1164         }
1165       }
1166     }
1167
1168     /**
1169      * Ensure the dialog container fill-stretches to the viewport
1170      */
1171     function stretchDialogContainerToViewport(container, options) {
1172       var isFixed = $window.getComputedStyle($document[0].body).position == 'fixed';
1173       var backdrop = options.backdrop ? $window.getComputedStyle(options.backdrop[0]) : null;
1174       var height = backdrop ? Math.min($document[0].body.clientHeight, Math.ceil(Math.abs(parseInt(backdrop.height, 10)))) : 0;
1175
1176       var previousStyles = {
1177         top: container.css('top'),
1178         height: container.css('height')
1179       };
1180
1181       // If the body is fixed, determine the distance to the viewport in relative from the parent.
1182       var parentTop = Math.abs(options.parent[0].getBoundingClientRect().top);
1183
1184       container.css({
1185         top: (isFixed ? parentTop : 0) + 'px',
1186         height: height ? height + 'px' : '100%'
1187       });
1188
1189       return function() {
1190         // Reverts the modified styles back to the previous values.
1191         // This is needed for contentElements, which should have the same styles after close
1192         // as before.
1193         container.css(previousStyles);
1194       };
1195     }
1196
1197     /**
1198      *  Dialog open and pop-in animation
1199      */
1200     function dialogPopIn(container, options) {
1201       // Add the `md-dialog-container` to the DOM
1202       options.parent.append(container);
1203       options.reverseContainerStretch = stretchDialogContainerToViewport(container, options);
1204
1205       var dialogEl = container.find('md-dialog');
1206       var animator = $mdUtil.dom.animator;
1207       var buildTranslateToOrigin = animator.calculateZoomToOrigin;
1208       var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'};
1209       var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin));
1210       var to = animator.toTransformCss("");  // defaults to center display (or parent or $rootElement)
1211
1212       dialogEl.toggleClass('md-dialog-fullscreen', !!options.fullscreen);
1213
1214       return animator
1215         .translate3d(dialogEl, from, to, translateOptions)
1216         .then(function(animateReversal) {
1217
1218           // Build a reversal translate function synced to this translation...
1219           options.reverseAnimate = function() {
1220             delete options.reverseAnimate;
1221
1222             if (options.closeTo) {
1223               // Using the opposite classes to create a close animation to the closeTo element
1224               translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'};
1225               from = to;
1226               to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo));
1227
1228               return animator
1229                 .translate3d(dialogEl, from, to,translateOptions);
1230             }
1231
1232             return animateReversal(
1233               to = animator.toTransformCss(
1234                 // in case the origin element has moved or is hidden,
1235                 // let's recalculate the translateCSS
1236                 buildTranslateToOrigin(dialogEl, options.origin)
1237               )
1238             );
1239
1240           };
1241
1242           // Function to revert the generated animation styles on the dialog element.
1243           // Useful when using a contentElement instead of a template.
1244           options.clearAnimate = function() {
1245             delete options.clearAnimate;
1246
1247             // Remove the transition classes, added from $animateCSS, since those can't be removed
1248             // by reversely running the animator.
1249             dialogEl.removeClass([
1250               translateOptions.transitionOutClass,
1251               translateOptions.transitionInClass
1252             ].join(' '));
1253
1254             // Run the animation reversely to remove the previous added animation styles.
1255             return animator.translate3d(dialogEl, to, animator.toTransformCss(''), {});
1256           };
1257
1258           return true;
1259         });
1260     }
1261
1262     /**
1263      * Dialog close and pop-out animation
1264      */
1265     function dialogPopOut(container, options) {
1266       return options.reverseAnimate().then(function() {
1267         if (options.contentElement) {
1268           // When we use a contentElement, we want the element to be the same as before.
1269           // That means, that we have to clear all the animation properties, like transform.
1270           options.clearAnimate();
1271         }
1272       });
1273     }
1274
1275     /**
1276      * Utility function to filter out raw DOM nodes
1277      */
1278     function isNodeOneOf(elem, nodeTypeArray) {
1279       if (nodeTypeArray.indexOf(elem.nodeName) !== -1) {
1280         return true;
1281       }
1282     }
1283
1284   }
1285 }
1286
1287 ngmaterial.components.dialog = angular.module("material.components.dialog");