arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-material/modules/closure/fabSpeedDial/fabSpeedDial.js
www/bower_components/angular-material/modules/closure/fabSpeedDial/fabSpeedDial.jsRaw
  1/*!
  2 * Angular Material Design
  3 * https://github.com/angular/material
  4 * @license MIT
  5 * v1.1.3
  6 */
  7goog.provide('ngmaterial.components.fabShared');
  8goog.require('ngmaterial.core');
  9(function() {
 10  'use strict';
 11
 12  MdFabController['$inject'] = ["$scope", "$element", "$animate", "$mdUtil", "$mdConstant", "$timeout"];
 13  angular.module('material.components.fabShared', ['material.core'])
 14    .controller('MdFabController', MdFabController);
 15
 16  function MdFabController($scope, $element, $animate, $mdUtil, $mdConstant, $timeout) {
 17    var vm = this;
 18    var initialAnimationAttempts = 0;
 19
 20    // NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops
 21
 22    vm.open = function() {
 23      $scope.$evalAsync("vm.isOpen = true");
 24    };
 25
 26    vm.close = function() {
 27      // Async eval to avoid conflicts with existing digest loops
 28      $scope.$evalAsync("vm.isOpen = false");
 29
 30      // Focus the trigger when the element closes so users can still tab to the next item
 31      $element.find('md-fab-trigger')[0].focus();
 32    };
 33
 34    // Toggle the open/close state when the trigger is clicked
 35    vm.toggle = function() {
 36      $scope.$evalAsync("vm.isOpen = !vm.isOpen");
 37    };
 38
 39    /*
 40     * Angular Lifecycle hook for newer Angular versions.
 41     * Bindings are not guaranteed to have been assigned in the controller, but they are in the $onInit hook.
 42     */
 43    vm.$onInit = function() {
 44      setupDefaults();
 45      setupListeners();
 46      setupWatchers();
 47
 48      fireInitialAnimations();
 49    };
 50
 51    // For Angular 1.4 and older, where there are no lifecycle hooks but bindings are pre-assigned,
 52    // manually call the $onInit hook.
 53    if (angular.version.major === 1 && angular.version.minor <= 4) {
 54      this.$onInit();
 55    }
 56
 57    function setupDefaults() {
 58      // Set the default direction to 'down' if none is specified
 59      vm.direction = vm.direction || 'down';
 60
 61      // Set the default to be closed
 62      vm.isOpen = vm.isOpen || false;
 63
 64      // Start the keyboard interaction at the first action
 65      resetActionIndex();
 66
 67      // Add an animations waiting class so we know not to run
 68      $element.addClass('md-animations-waiting');
 69    }
 70
 71    function setupListeners() {
 72      var eventTypes = [
 73        'click', 'focusin', 'focusout'
 74      ];
 75
 76      // Add our listeners
 77      angular.forEach(eventTypes, function(eventType) {
 78        $element.on(eventType, parseEvents);
 79      });
 80
 81      // Remove our listeners when destroyed
 82      $scope.$on('$destroy', function() {
 83        angular.forEach(eventTypes, function(eventType) {
 84          $element.off(eventType, parseEvents);
 85        });
 86
 87        // remove any attached keyboard handlers in case element is removed while
 88        // speed dial is open
 89        disableKeyboard();
 90      });
 91    }
 92
 93    var closeTimeout;
 94    function parseEvents(event) {
 95      // If the event is a click, just handle it
 96      if (event.type == 'click') {
 97        handleItemClick(event);
 98      }
 99
100      // If we focusout, set a timeout to close the element
101      if (event.type == 'focusout' && !closeTimeout) {
102        closeTimeout = $timeout(function() {
103          vm.close();
104        }, 100, false);
105      }
106
107      // If we see a focusin and there is a timeout about to run, cancel it so we stay open
108      if (event.type == 'focusin' && closeTimeout) {
109        $timeout.cancel(closeTimeout);
110        closeTimeout = null;
111      }
112    }
113
114    function resetActionIndex() {
115      vm.currentActionIndex = -1;
116    }
117
118    function setupWatchers() {
119      // Watch for changes to the direction and update classes/attributes
120      $scope.$watch('vm.direction', function(newDir, oldDir) {
121        // Add the appropriate classes so we can target the direction in the CSS
122        $animate.removeClass($element, 'md-' + oldDir);
123        $animate.addClass($element, 'md-' + newDir);
124
125        // Reset the action index since it may have changed
126        resetActionIndex();
127      });
128
129      var trigger, actions;
130
131      // Watch for changes to md-open
132      $scope.$watch('vm.isOpen', function(isOpen) {
133        // Reset the action index since it may have changed
134        resetActionIndex();
135
136        // We can't get the trigger/actions outside of the watch because the component hasn't been
137        // linked yet, so we wait until the first watch fires to cache them.
138        if (!trigger || !actions) {
139          trigger = getTriggerElement();
140          actions = getActionsElement();
141        }
142
143        if (isOpen) {
144          enableKeyboard();
145        } else {
146          disableKeyboard();
147        }
148
149        var toAdd = isOpen ? 'md-is-open' : '';
150        var toRemove = isOpen ? '' : 'md-is-open';
151
152        // Set the proper ARIA attributes
153        trigger.attr('aria-haspopup', true);
154        trigger.attr('aria-expanded', isOpen);
155        actions.attr('aria-hidden', !isOpen);
156
157        // Animate the CSS classes
158        $animate.setClass($element, toAdd, toRemove);
159      });
160    }
161
162    function fireInitialAnimations() {
163      // If the element is actually visible on the screen
164      if ($element[0].scrollHeight > 0) {
165        // Fire our animation
166        $animate.addClass($element, '_md-animations-ready').then(function() {
167          // Remove the waiting class
168          $element.removeClass('md-animations-waiting');
169        });
170      }
171
172      // Otherwise, try for up to 1 second before giving up
173      else if (initialAnimationAttempts < 10) {
174        $timeout(fireInitialAnimations, 100);
175
176        // Increment our counter
177        initialAnimationAttempts = initialAnimationAttempts + 1;
178      }
179    }
180
181    function enableKeyboard() {
182      $element.on('keydown', keyPressed);
183
184      // On the next tick, setup a check for outside clicks; we do this on the next tick to avoid
185      // clicks/touches that result in the isOpen attribute changing (e.g. a bound radio button)
186      $mdUtil.nextTick(function() {
187        angular.element(document).on('click touchend', checkForOutsideClick);
188      });
189
190      // TODO: On desktop, we should be able to reset the indexes so you cannot tab through, but
191      // this breaks accessibility, especially on mobile, since you have no arrow keys to press
192      //resetActionTabIndexes();
193    }
194
195    function disableKeyboard() {
196      $element.off('keydown', keyPressed);
197      angular.element(document).off('click touchend', checkForOutsideClick);
198    }
199
200    function checkForOutsideClick(event) {
201      if (event.target) {
202        var closestTrigger = $mdUtil.getClosest(event.target, 'md-fab-trigger');
203        var closestActions = $mdUtil.getClosest(event.target, 'md-fab-actions');
204
205        if (!closestTrigger && !closestActions) {
206          vm.close();
207        }
208      }
209    }
210
211    function keyPressed(event) {
212      switch (event.which) {
213        case $mdConstant.KEY_CODE.ESCAPE: vm.close(); event.preventDefault(); return false;
214        case $mdConstant.KEY_CODE.LEFT_ARROW: doKeyLeft(event); return false;
215        case $mdConstant.KEY_CODE.UP_ARROW: doKeyUp(event); return false;
216        case $mdConstant.KEY_CODE.RIGHT_ARROW: doKeyRight(event); return false;
217        case $mdConstant.KEY_CODE.DOWN_ARROW: doKeyDown(event); return false;
218      }
219    }
220
221    function doActionPrev(event) {
222      focusAction(event, -1);
223    }
224
225    function doActionNext(event) {
226      focusAction(event, 1);
227    }
228
229    function focusAction(event, direction) {
230      var actions = resetActionTabIndexes();
231
232      // Increment/decrement the counter with restrictions
233      vm.currentActionIndex = vm.currentActionIndex + direction;
234      vm.currentActionIndex = Math.min(actions.length - 1, vm.currentActionIndex);
235      vm.currentActionIndex = Math.max(0, vm.currentActionIndex);
236
237      // Focus the element
238      var focusElement =  angular.element(actions[vm.currentActionIndex]).children()[0];
239      angular.element(focusElement).attr('tabindex', 0);
240      focusElement.focus();
241
242      // Make sure the event doesn't bubble and cause something else
243      event.preventDefault();
244      event.stopImmediatePropagation();
245    }
246
247    function resetActionTabIndexes() {
248      // Grab all of the actions
249      var actions = getActionsElement()[0].querySelectorAll('.md-fab-action-item');
250
251      // Disable all other actions for tabbing
252      angular.forEach(actions, function(action) {
253        angular.element(angular.element(action).children()[0]).attr('tabindex', -1);
254      });
255
256      return actions;
257    }
258
259    function doKeyLeft(event) {
260      if (vm.direction === 'left') {
261        doActionNext(event);
262      } else {
263        doActionPrev(event);
264      }
265    }
266
267    function doKeyUp(event) {
268      if (vm.direction === 'down') {
269        doActionPrev(event);
270      } else {
271        doActionNext(event);
272      }
273    }
274
275    function doKeyRight(event) {
276      if (vm.direction === 'left') {
277        doActionPrev(event);
278      } else {
279        doActionNext(event);
280      }
281    }
282
283    function doKeyDown(event) {
284      if (vm.direction === 'up') {
285        doActionPrev(event);
286      } else {
287        doActionNext(event);
288      }
289    }
290
291    function isTrigger(element) {
292      return $mdUtil.getClosest(element, 'md-fab-trigger');
293    }
294
295    function isAction(element) {
296      return $mdUtil.getClosest(element, 'md-fab-actions');
297    }
298
299    function handleItemClick(event) {
300      if (isTrigger(event.target)) {
301        vm.toggle();
302      }
303
304      if (isAction(event.target)) {
305        vm.close();
306      }
307    }
308
309    function getTriggerElement() {
310      return $element.find('md-fab-trigger');
311    }
312
313    function getActionsElement() {
314      return $element.find('md-fab-actions');
315    }
316  }
317})();
318
319(function() {
320  'use strict';
321
322  /**
323   * The duration of the CSS animation in milliseconds.
324   *
325   * @type {number}
326   */
327  MdFabSpeedDialFlingAnimation['$inject'] = ["$timeout"];
328  MdFabSpeedDialScaleAnimation['$inject'] = ["$timeout"];
329  var cssAnimationDuration = 300;
330
331  /**
332   * @ngdoc module
333   * @name material.components.fabSpeedDial
334   */
335  angular
336    // Declare our module
337    .module('material.components.fabSpeedDial', [
338      'material.core',
339      'material.components.fabShared',
340      'material.components.fabActions'
341    ])
342
343    // Register our directive
344    .directive('mdFabSpeedDial', MdFabSpeedDialDirective)
345
346    // Register our custom animations
347    .animation('.md-fling', MdFabSpeedDialFlingAnimation)
348    .animation('.md-scale', MdFabSpeedDialScaleAnimation)
349
350    // Register a service for each animation so that we can easily inject them into unit tests
351    .service('mdFabSpeedDialFlingAnimation', MdFabSpeedDialFlingAnimation)
352    .service('mdFabSpeedDialScaleAnimation', MdFabSpeedDialScaleAnimation);
353
354  /**
355   * @ngdoc directive
356   * @name mdFabSpeedDial
357   * @module material.components.fabSpeedDial
358   *
359   * @restrict E
360   *
361   * @description
362   * The `<md-fab-speed-dial>` directive is used to present a series of popup elements (usually
363   * `<md-button>`s) for quick access to common actions.
364   *
365   * There are currently two animations available by applying one of the following classes to
366   * the component:
367   *
368   *  - `md-fling` - The speed dial items appear from underneath the trigger and move into their
369   *    appropriate positions.
370   *  - `md-scale` - The speed dial items appear in their proper places by scaling from 0% to 100%.
371   *
372   * You may also easily position the trigger by applying one one of the following classes to the
373   * `<md-fab-speed-dial>` element:
374   *  - `md-fab-top-left`
375   *  - `md-fab-top-right`
376   *  - `md-fab-bottom-left`
377   *  - `md-fab-bottom-right`
378   *
379   * These CSS classes use `position: absolute`, so you need to ensure that the container element
380   * also uses `position: absolute` or `position: relative` in order for them to work.
381   *
382   * Additionally, you may use the standard `ng-mouseenter` and `ng-mouseleave` directives to
383   * open or close the speed dial. However, if you wish to allow users to hover over the empty
384   * space where the actions will appear, you must also add the `md-hover-full` class to the speed
385   * dial element. Without this, the hover effect will only occur on top of the trigger.
386   *
387   * See the demos for more information.
388   *
389   * ## Troubleshooting
390   *
391   * If your speed dial shows the closing animation upon launch, you may need to use `ng-cloak` on
392   * the parent container to ensure that it is only visible once ready. We have plans to remove this
393   * necessity in the future.
394   *
395   * @usage
396   * <hljs lang="html">
397   * <md-fab-speed-dial md-direction="up" class="md-fling">
398   *   <md-fab-trigger>
399   *     <md-button aria-label="Add..."><md-icon md-svg-src="/img/icons/plus.svg"></md-icon></md-button>
400   *   </md-fab-trigger>
401   *
402   *   <md-fab-actions>
403   *     <md-button aria-label="Add User">
404   *       <md-icon md-svg-src="/img/icons/user.svg"></md-icon>
405   *     </md-button>
406   *
407   *     <md-button aria-label="Add Group">
408   *       <md-icon md-svg-src="/img/icons/group.svg"></md-icon>
409   *     </md-button>
410   *   </md-fab-actions>
411   * </md-fab-speed-dial>
412   * </hljs>
413   *
414   * @param {string} md-direction From which direction you would like the speed dial to appear
415   * relative to the trigger element.
416   * @param {expression=} md-open Programmatically control whether or not the speed-dial is visible.
417   */
418  function MdFabSpeedDialDirective() {
419    return {
420      restrict: 'E',
421
422      scope: {
423        direction: '@?mdDirection',
424        isOpen: '=?mdOpen'
425      },
426
427      bindToController: true,
428      controller: 'MdFabController',
429      controllerAs: 'vm',
430
431      link: FabSpeedDialLink
432    };
433
434    function FabSpeedDialLink(scope, element) {
435      // Prepend an element to hold our CSS variables so we can use them in the animations below
436      element.prepend('<div class="_md-css-variables"></div>');
437    }
438  }
439
440  function MdFabSpeedDialFlingAnimation($timeout) {
441    function delayDone(done) { $timeout(done, cssAnimationDuration, false); }
442
443    function runAnimation(element) {
444      // Don't run if we are still waiting and we are not ready
445      if (element.hasClass('md-animations-waiting') && !element.hasClass('_md-animations-ready')) {
446        return;
447      }
448
449      var el = element[0];
450      var ctrl = element.controller('mdFabSpeedDial');
451      var items = el.querySelectorAll('.md-fab-action-item');
452
453      // Grab our trigger element
454      var triggerElement = el.querySelector('md-fab-trigger');
455
456      // Grab our element which stores CSS variables
457      var variablesElement = el.querySelector('._md-css-variables');
458
459      // Setup JS variables based on our CSS variables
460      var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex);
461
462      // Always reset the items to their natural position/state
463      angular.forEach(items, function(item, index) {
464        var styles = item.style;
465
466        styles.transform = styles.webkitTransform = '';
467        styles.transitionDelay = '';
468        styles.opacity = 1;
469
470        // Make the items closest to the trigger have the highest z-index
471        styles.zIndex = (items.length - index) + startZIndex;
472      });
473
474      // Set the trigger to be above all of the actions so they disappear behind it.
475      triggerElement.style.zIndex = startZIndex + items.length + 1;
476
477      // If the control is closed, hide the items behind the trigger
478      if (!ctrl.isOpen) {
479        angular.forEach(items, function(item, index) {
480          var newPosition, axis;
481          var styles = item.style;
482
483          // Make sure to account for differences in the dimensions of the trigger verses the items
484          // so that we can properly center everything; this helps hide the item's shadows behind
485          // the trigger.
486          var triggerItemHeightOffset = (triggerElement.clientHeight - item.clientHeight) / 2;
487          var triggerItemWidthOffset = (triggerElement.clientWidth - item.clientWidth) / 2;
488
489          switch (ctrl.direction) {
490            case 'up':
491              newPosition = (item.scrollHeight * (index + 1) + triggerItemHeightOffset);
492              axis = 'Y';
493              break;
494            case 'down':
495              newPosition = -(item.scrollHeight * (index + 1) + triggerItemHeightOffset);
496              axis = 'Y';
497              break;
498            case 'left':
499              newPosition = (item.scrollWidth * (index + 1) + triggerItemWidthOffset);
500              axis = 'X';
501              break;
502            case 'right':
503              newPosition = -(item.scrollWidth * (index + 1) + triggerItemWidthOffset);
504              axis = 'X';
505              break;
506          }
507
508          var newTranslate = 'translate' + axis + '(' + newPosition + 'px)';
509
510          styles.transform = styles.webkitTransform = newTranslate;
511        });
512      }
513    }
514
515    return {
516      addClass: function(element, className, done) {
517        if (element.hasClass('md-fling')) {
518          runAnimation(element);
519          delayDone(done);
520        } else {
521          done();
522        }
523      },
524      removeClass: function(element, className, done) {
525        runAnimation(element);
526        delayDone(done);
527      }
528    };
529  }
530
531  function MdFabSpeedDialScaleAnimation($timeout) {
532    function delayDone(done) { $timeout(done, cssAnimationDuration, false); }
533
534    var delay = 65;
535
536    function runAnimation(element) {
537      var el = element[0];
538      var ctrl = element.controller('mdFabSpeedDial');
539      var items = el.querySelectorAll('.md-fab-action-item');
540
541      // Grab our element which stores CSS variables
542      var variablesElement = el.querySelector('._md-css-variables');
543
544      // Setup JS variables based on our CSS variables
545      var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex);
546
547      // Always reset the items to their natural position/state
548      angular.forEach(items, function(item, index) {
549        var styles = item.style,
550          offsetDelay = index * delay;
551
552        styles.opacity = ctrl.isOpen ? 1 : 0;
553        styles.transform = styles.webkitTransform = ctrl.isOpen ? 'scale(1)' : 'scale(0)';
554        styles.transitionDelay = (ctrl.isOpen ? offsetDelay : (items.length - offsetDelay)) + 'ms';
555
556        // Make the items closest to the trigger have the highest z-index
557        styles.zIndex = (items.length - index) + startZIndex;
558      });
559    }
560
561    return {
562      addClass: function(element, className, done) {
563        runAnimation(element);
564        delayDone(done);
565      },
566
567      removeClass: function(element, className, done) {
568        runAnimation(element);
569        delayDone(done);
570      }
571    };
572  }
573})();
574
575ngmaterial.components.fabShared = angular.module("material.components.fabShared");