7fc8ff484576525e1c106a2c5efd5997977f8136
[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.toast');
8 goog.require('ngmaterial.components.button');
9 goog.require('ngmaterial.core');
10 /**
11   * @ngdoc module
12   * @name material.components.toast
13   * @description
14   * Toast
15   */
16 MdToastDirective['$inject'] = ["$mdToast"];
17 MdToastProvider['$inject'] = ["$$interimElementProvider"];
18 angular.module('material.components.toast', [
19   'material.core',
20   'material.components.button'
21 ])
22   .directive('mdToast', MdToastDirective)
23   .provider('$mdToast', MdToastProvider);
24
25 /* ngInject */
26 function MdToastDirective($mdToast) {
27   return {
28     restrict: 'E',
29     link: function postLink(scope, element) {
30       element.addClass('_md');     // private md component indicator for styling
31       
32       // When navigation force destroys an interimElement, then
33       // listen and $destroy() that interim instance...
34       scope.$on('$destroy', function() {
35         $mdToast.destroy();
36       });
37     }
38   };
39 }
40
41 /**
42   * @ngdoc service
43   * @name $mdToast
44   * @module material.components.toast
45   *
46   * @description
47   * `$mdToast` is a service to build a toast notification on any position
48   * on the screen with an optional duration, and provides a simple promise API.
49   *
50   * The toast will be always positioned at the `bottom`, when the screen size is
51   * between `600px` and `959px` (`sm` breakpoint)
52   *
53   * ## Restrictions on custom toasts
54   * - The toast's template must have an outer `<md-toast>` element.
55   * - For a toast action, use element with class `md-action`.
56   * - Add the class `md-capsule` for curved corners.
57   *
58   * ### Custom Presets
59   * Developers are also able to create their own preset, which can be easily used without repeating
60   * their options each time.
61   *
62   * <hljs lang="js">
63   *   $mdToastProvider.addPreset('testPreset', {
64   *     options: function() {
65   *       return {
66   *         template:
67   *           '<md-toast>' +
68   *             '<div class="md-toast-content">' +
69   *               'This is a custom preset' +
70   *             '</div>' +
71   *           '</md-toast>',
72   *         controllerAs: 'toast',
73   *         bindToController: true
74   *       };
75   *     }
76   *   });
77   * </hljs>
78   *
79   * After you created your preset at config phase, you can easily access it.
80   *
81   * <hljs lang="js">
82   *   $mdToast.show(
83   *     $mdToast.testPreset()
84   *   );
85   * </hljs>
86   *
87   * ## Parent container notes
88   *
89   * The toast is positioned using absolute positioning relative to its first non-static parent
90   * container. Thus, if the requested parent container uses static positioning, we will temporarily
91   * set its positioning to `relative` while the toast is visible and reset it when the toast is
92   * hidden.
93   *
94   * Because of this, it is usually best to ensure that the parent container has a fixed height and
95   * prevents scrolling by setting the `overflow: hidden;` style. Since the position is based off of
96   * the parent's height, the toast may be mispositioned if you allow the parent to scroll.
97   *
98   * You can, however, have a scrollable element inside of the container; just make sure the
99   * container itself does not scroll.
100   *
101   * <hljs lang="html">
102   * <div layout-fill id="toast-container">
103   *   <md-content>
104   *     I can have lots of content and scroll!
105   *   </md-content>
106   * </div>
107   * </hljs>
108   *
109   * @usage
110   * <hljs lang="html">
111   * <div ng-controller="MyController">
112   *   <md-button ng-click="openToast()">
113   *     Open a Toast!
114   *   </md-button>
115   * </div>
116   * </hljs>
117   *
118   * <hljs lang="js">
119   * var app = angular.module('app', ['ngMaterial']);
120   * app.controller('MyController', function($scope, $mdToast) {
121   *   $scope.openToast = function($event) {
122   *     $mdToast.show($mdToast.simple().textContent('Hello!'));
123   *     // Could also do $mdToast.showSimple('Hello');
124   *   };
125   * });
126   * </hljs>
127   */
128
129 /**
130  * @ngdoc method
131  * @name $mdToast#showSimple
132  * 
133  * @param {string} message The message to display inside the toast
134  * @description
135  * Convenience method which builds and shows a simple toast.
136  *
137  * @returns {promise} A promise that can be resolved with `$mdToast.hide()` or
138  * rejected with `$mdToast.cancel()`.
139  *
140  */
141
142  /**
143   * @ngdoc method
144   * @name $mdToast#simple
145   *
146   * @description
147   * Builds a preconfigured toast.
148   *
149   * @returns {obj} a `$mdToastPreset` with the following chainable configuration methods.
150   *
151   * _**Note:** These configuration methods are provided in addition to the methods provided by
152   * the `build()` and `show()` methods below._
153   *
154   * <table class="md-api-table methods">
155   *    <thead>
156   *      <tr>
157   *        <th>Method</th>
158   *        <th>Description</th>
159   *      </tr>
160   *    </thead>
161   *    <tbody>
162   *      <tr>
163   *        <td>`.textContent(string)`</td>
164   *        <td>Sets the toast content to the specified string</td>
165   *      </tr>
166   *      <tr>
167   *        <td>`.action(string)`</td>
168   *        <td>
169   *          Adds an action button. <br/>
170   *          If clicked, the promise (returned from `show()`)
171   *          will resolve with the value `'ok'`; otherwise, it is resolved with `true` after a `hideDelay`
172   *          timeout
173   *        </td>
174   *      </tr>
175   *      <tr>
176   *        <td>`.highlightAction(boolean)`</td>
177   *        <td>
178   *          Whether or not the action button will have an additional highlight class.<br/>
179   *          By default the `accent` color will be applied to the action button.
180   *        </td>
181   *      </tr>
182   *      <tr>
183   *        <td>`.highlightClass(string)`</td>
184   *        <td>
185   *          If set, the given class will be applied to the highlighted action button.<br/>
186   *          This allows you to specify the highlight color easily. Highlight classes are `md-primary`, `md-warn`
187   *          and `md-accent`
188   *        </td>
189   *      </tr>
190   *      <tr>
191   *        <td>`.capsule(boolean)`</td>
192   *        <td>Whether or not to add the `md-capsule` class to the toast to provide rounded corners</td>
193   *      </tr>
194   *      <tr>
195   *        <td>`.theme(string)`</td>
196   *        <td>Sets the theme on the toast to the requested theme. Default is `$mdThemingProvider`'s default.</td>
197   *      </tr>
198   *      <tr>
199   *        <td>`.toastClass(string)`</td>
200   *        <td>Sets a class on the toast element</td>
201   *      </tr>
202   *    </tbody>
203   * </table>
204   *
205   */
206
207 /**
208   * @ngdoc method
209   * @name $mdToast#updateTextContent
210   *
211   * @description
212   * Updates the content of an existing toast. Useful for updating things like counts, etc.
213   *
214   */
215
216  /**
217   * @ngdoc method
218   * @name $mdToast#build
219   *
220   * @description
221   * Creates a custom `$mdToastPreset` that you can configure.
222   *
223   * @returns {obj} a `$mdToastPreset` with the chainable configuration methods for shows' options (see below).
224   */
225
226  /**
227   * @ngdoc method
228   * @name $mdToast#show
229   *
230   * @description Shows the toast.
231   *
232   * @param {object} optionsOrPreset Either provide an `$mdToastPreset` returned from `simple()`
233   * and `build()`, or an options object with the following properties:
234   *
235   *   - `templateUrl` - `{string=}`: The url of an html template file that will
236   *     be used as the content of the toast. Restrictions: the template must
237   *     have an outer `md-toast` element.
238   *   - `template` - `{string=}`: Same as templateUrl, except this is an actual
239   *     template string.
240   *   - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template content with a
241   *     `<div class="md-toast-content">` if one is not provided. Defaults to true. Can be disabled if you provide a
242   *     custom toast directive.
243   *   - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified, it will create a new child scope.
244   *     This scope will be destroyed when the toast is removed unless `preserveScope` is set to true.
245   *   - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false
246   *   - `hideDelay` - `{number=}`: How many milliseconds the toast should stay
247   *     active before automatically closing.  Set to 0 or false to have the toast stay open until
248   *     closed manually. Default: 3000.
249   *   - `position` - `{string=}`: Sets the position of the toast. <br/>
250   *     Available: any combination of `'bottom'`, `'left'`, `'top'`, `'right'`, `'end'` and `'start'`.
251   *     The properties `'end'` and `'start'` are dynamic and can be used for RTL support.<br/>
252   *     Default combination: `'bottom left'`.
253   *   - `toastClass` - `{string=}`: A class to set on the toast element.
254   *   - `controller` - `{string=}`: The controller to associate with this toast.
255   *     The controller will be injected the local `$mdToast.hide( )`, which is a function
256   *     used to hide the toast.
257   *   - `locals` - `{string=}`: An object containing key/value pairs. The keys will
258   *     be used as names of values to inject into the controller. For example,
259   *     `locals: {three: 3}` would inject `three` into the controller with the value
260   *     of 3.
261   *   - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
262   *   - `resolve` - `{object=}`: Similar to locals, except it takes promises as values
263   *     and the toast will not open until the promises resolve.
264   *   - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
265   *   - `parent` - `{element=}`: The element to append the toast to. Defaults to appending
266   *     to the root element of the application.
267   *
268   * @returns {promise} A promise that can be resolved with `$mdToast.hide()` or
269   * rejected with `$mdToast.cancel()`. `$mdToast.hide()` will resolve either with a Boolean
270   * value == 'true' or the value passed as an argument to `$mdToast.hide()`.
271   * And `$mdToast.cancel()` will resolve the promise with a Boolean value == 'false'
272   */
273
274 /**
275   * @ngdoc method
276   * @name $mdToast#hide
277   *
278   * @description
279   * Hide an existing toast and resolve the promise returned from `$mdToast.show()`.
280   *
281   * @param {*=} response An argument for the resolved promise.
282   *
283   * @returns {promise} a promise that is called when the existing element is removed from the DOM.
284   * The promise is resolved with either a Boolean value == 'true' or the value passed as the
285   * argument to `.hide()`.
286   *
287   */
288
289 /**
290   * @ngdoc method
291   * @name $mdToast#cancel
292   *
293   * @description
294   * `DEPRECATED` - The promise returned from opening a toast is used only to notify about the closing of the toast.
295   * As such, there isn't any reason to also allow that promise to be rejected,
296   * since it's not clear what the difference between resolve and reject would be.
297   *
298   * Hide the existing toast and reject the promise returned from
299   * `$mdToast.show()`.
300   *
301   * @param {*=} response An argument for the rejected promise.
302   *
303   * @returns {promise} a promise that is called when the existing element is removed from the DOM
304   * The promise is resolved with a Boolean value == 'false'.
305   *
306   */
307
308 function MdToastProvider($$interimElementProvider) {
309   // Differentiate promise resolves: hide timeout (value == true) and hide action clicks (value == ok).
310   toastDefaultOptions['$inject'] = ["$animate", "$mdToast", "$mdUtil", "$mdMedia"];
311   var ACTION_RESOLVE = 'ok';
312
313   var activeToastContent;
314   var $mdToast = $$interimElementProvider('$mdToast')
315     .setDefaults({
316       methods: ['position', 'hideDelay', 'capsule', 'parent', 'position', 'toastClass'],
317       options: toastDefaultOptions
318     })
319     .addPreset('simple', {
320       argOption: 'textContent',
321       methods: ['textContent', 'content', 'action', 'highlightAction', 'highlightClass', 'theme', 'parent' ],
322       options: /* ngInject */ ["$mdToast", "$mdTheming", function($mdToast, $mdTheming) {
323         return {
324           template:
325             '<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}">' +
326             '  <div class="md-toast-content">' +
327             '    <span class="md-toast-text" role="alert" aria-relevant="all" aria-atomic="true">' +
328             '      {{ toast.content }}' +
329             '    </span>' +
330             '    <md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ' +
331             '        ng-class="highlightClasses">' +
332             '      {{ toast.action }}' +
333             '    </md-button>' +
334             '  </div>' +
335             '</md-toast>',
336           controller: /* ngInject */ ["$scope", function mdToastCtrl($scope) {
337             var self = this;
338
339             if (self.highlightAction) {
340               $scope.highlightClasses = [
341                 'md-highlight',
342                 self.highlightClass
343               ]
344             }
345
346             $scope.$watch(function() { return activeToastContent; }, function() {
347               self.content = activeToastContent;
348             });
349
350             this.resolve = function() {
351               $mdToast.hide( ACTION_RESOLVE );
352             };
353           }],
354           theme: $mdTheming.defaultTheme(),
355           controllerAs: 'toast',
356           bindToController: true
357         };
358       }]
359     })
360     .addMethod('updateTextContent', updateTextContent)
361     .addMethod('updateContent', updateTextContent);
362
363     function updateTextContent(newContent) {
364       activeToastContent = newContent;
365     }
366
367     return $mdToast;
368
369   /* ngInject */
370   function toastDefaultOptions($animate, $mdToast, $mdUtil, $mdMedia) {
371     var SWIPE_EVENTS = '$md.swipeleft $md.swiperight $md.swipeup $md.swipedown';
372     return {
373       onShow: onShow,
374       onRemove: onRemove,
375       toastClass: '',
376       position: 'bottom left',
377       themable: true,
378       hideDelay: 3000,
379       autoWrap: true,
380       transformTemplate: function(template, options) {
381         var shouldAddWrapper = options.autoWrap && template && !/md-toast-content/g.test(template);
382
383         if (shouldAddWrapper) {
384           // Root element of template will be <md-toast>. We need to wrap all of its content inside of
385           // of <div class="md-toast-content">. All templates provided here should be static, developer-controlled
386           // content (meaning we're not attempting to guard against XSS).
387           var templateRoot = document.createElement('md-template');
388           templateRoot.innerHTML = template;
389
390           // Iterate through all root children, to detect possible md-toast directives.
391           for (var i = 0; i < templateRoot.children.length; i++) {
392             if (templateRoot.children[i].nodeName === 'MD-TOAST') {
393               var wrapper = angular.element('<div class="md-toast-content">');
394
395               // Wrap the children of the `md-toast` directive in jqLite, to be able to append multiple
396               // nodes with the same execution.
397               wrapper.append(angular.element(templateRoot.children[i].childNodes));
398
399               // Append the new wrapped element to the `md-toast` directive.
400               templateRoot.children[i].appendChild(wrapper[0]);
401             }
402           }
403
404           // We have to return the innerHTMl, because we do not want to have the `md-template` element to be
405           // the root element of our interimElement.
406           return templateRoot.innerHTML;
407         }
408
409         return template || '';
410       }
411     };
412
413     function onShow(scope, element, options) {
414       activeToastContent = options.textContent || options.content; // support deprecated #content method
415
416       var isSmScreen = !$mdMedia('gt-sm');
417
418       element = $mdUtil.extractElementByName(element, 'md-toast', true);
419       options.element = element;
420
421       options.onSwipe = function(ev, gesture) {
422         //Add the relevant swipe class to the element so it can animate correctly
423         var swipe = ev.type.replace('$md.','');
424         var direction = swipe.replace('swipe', '');
425
426         // If the swipe direction is down/up but the toast came from top/bottom don't fade away
427         // Unless the screen is small, then the toast always on bottom
428         if ((direction === 'down' && options.position.indexOf('top') != -1 && !isSmScreen) ||
429             (direction === 'up' && (options.position.indexOf('bottom') != -1 || isSmScreen))) {
430           return;
431         }
432
433         if ((direction === 'left' || direction === 'right') && isSmScreen) {
434           return;
435         }
436
437         element.addClass('md-' + swipe);
438         $mdUtil.nextTick($mdToast.cancel);
439       };
440       options.openClass = toastOpenClass(options.position);
441
442       element.addClass(options.toastClass);
443
444       // 'top left' -> 'md-top md-left'
445       options.parent.addClass(options.openClass);
446
447       // static is the default position
448       if ($mdUtil.hasComputedStyle(options.parent, 'position', 'static')) {
449         options.parent.css('position', 'relative');
450       }
451
452       element.on(SWIPE_EVENTS, options.onSwipe);
453       element.addClass(isSmScreen ? 'md-bottom' : options.position.split(' ').map(function(pos) {
454         return 'md-' + pos;
455       }).join(' '));
456
457       if (options.parent) options.parent.addClass('md-toast-animating');
458       return $animate.enter(element, options.parent).then(function() {
459         if (options.parent) options.parent.removeClass('md-toast-animating');
460       });
461     }
462
463     function onRemove(scope, element, options) {
464       element.off(SWIPE_EVENTS, options.onSwipe);
465       if (options.parent) options.parent.addClass('md-toast-animating');
466       if (options.openClass) options.parent.removeClass(options.openClass);
467
468       return ((options.$destroy == true) ? element.remove() : $animate.leave(element))
469         .then(function () {
470           if (options.parent) options.parent.removeClass('md-toast-animating');
471           if ($mdUtil.hasComputedStyle(options.parent, 'position', 'static')) {
472             options.parent.css('position', '');
473           }
474         });
475     }
476
477     function toastOpenClass(position) {
478       // For mobile, always open full-width on bottom
479       if (!$mdMedia('gt-xs')) {
480         return 'md-toast-open-bottom';
481       }
482
483       return 'md-toast-open-' +
484         (position.indexOf('top') > -1 ? 'top' : 'bottom');
485     }
486   }
487
488 }
489
490 ngmaterial.components.toast = angular.module("material.components.toast");