arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-material/modules/js/sidenav/sidenav.js
www/bower_components/angular-material/modules/js/sidenav/sidenav.jsRaw
  1/*!
  2 * Angular Material Design
  3 * https://github.com/angular/material
  4 * @license MIT
  5 * v1.1.3
  6 */
  7(function( window, angular, undefined ){
  8"use strict";
  9
 10/**
 11 * @ngdoc module
 12 * @name material.components.sidenav
 13 *
 14 * @description
 15 * A Sidenav QP component.
 16 */
 17SidenavService['$inject'] = ["$mdComponentRegistry", "$mdUtil", "$q", "$log"];
 18SidenavDirective['$inject'] = ["$mdMedia", "$mdUtil", "$mdConstant", "$mdTheming", "$mdInteraction", "$animate", "$compile", "$parse", "$log", "$q", "$document", "$window", "$$rAF"];
 19SidenavController['$inject'] = ["$scope", "$attrs", "$mdComponentRegistry", "$q", "$interpolate"];
 20angular
 21  .module('material.components.sidenav', [
 22    'material.core',
 23    'material.components.backdrop'
 24  ])
 25  .factory('$mdSidenav', SidenavService )
 26  .directive('mdSidenav', SidenavDirective)
 27  .directive('mdSidenavFocus', SidenavFocusDirective)
 28  .controller('$mdSidenavController', SidenavController);
 29
 30
 31/**
 32 * @ngdoc service
 33 * @name $mdSidenav
 34 * @module material.components.sidenav
 35 *
 36 * @description
 37 * `$mdSidenav` makes it easy to interact with multiple sidenavs
 38 * in an app. When looking up a sidenav instance, you can either look
 39 * it up synchronously or wait for it to be initializied asynchronously.
 40 * This is done by passing the second argument to `$mdSidenav`.
 41 *
 42 * @usage
 43 * <hljs lang="js">
 44 * // Async lookup for sidenav instance; will resolve when the instance is available
 45 * $mdSidenav(componentId, true).then(function(instance) {
 46 *   $log.debug( componentId + "is now ready" );
 47 * });
 48 * // Sync lookup for sidenav instance; this will resolve immediately.
 49 * $mdSidenav(componentId).then(function(instance) {
 50 *   $log.debug( componentId + "is now ready" );
 51 * });
 52 * // Async toggle the given sidenav;
 53 * // when instance is known ready and lazy lookup is not needed.
 54 * $mdSidenav(componentId)
 55 *    .toggle()
 56 *    .then(function(){
 57 *      $log.debug('toggled');
 58 *    });
 59 * // Async open the given sidenav
 60 * $mdSidenav(componentId)
 61 *    .open()
 62 *    .then(function(){
 63 *      $log.debug('opened');
 64 *    });
 65 * // Async close the given sidenav
 66 * $mdSidenav(componentId)
 67 *    .close()
 68 *    .then(function(){
 69 *      $log.debug('closed');
 70 *    });
 71 * // Sync check to see if the specified sidenav is set to be open
 72 * $mdSidenav(componentId).isOpen();
 73 * // Sync check to whether given sidenav is locked open
 74 * // If this is true, the sidenav will be open regardless of close()
 75 * $mdSidenav(componentId).isLockedOpen();
 76 * // On close callback to handle close, backdrop click or escape key pressed
 77 * // Callback happens BEFORE the close action occurs.
 78 * $mdSidenav(componentId).onClose(function () {
 79 *   $log.debug('closing');
 80 * });
 81 * </hljs>
 82 */
 83function SidenavService($mdComponentRegistry, $mdUtil, $q, $log) {
 84  var errorMsg = "SideNav '{0}' is not available! Did you use md-component-id='{0}'?";
 85  var service = {
 86        find    : findInstance,     //  sync  - returns proxy API
 87        waitFor : waitForInstance   //  async - returns promise
 88      };
 89
 90  /**
 91   * Service API that supports three (3) usages:
 92   *   $mdSidenav().find("left")                       // sync (must already exist) or returns undefined
 93   *   $mdSidenav("left").toggle();                    // sync (must already exist) or returns reject promise;
 94   *   $mdSidenav("left",true).then( function(left){   // async returns instance when available
 95   *    left.toggle();
 96   *   });
 97   */
 98  return function(handle, enableWait) {
 99    if ( angular.isUndefined(handle) ) return service;
100
101    var shouldWait = enableWait === true;
102    var instance = service.find(handle, shouldWait);
103    return  !instance && shouldWait ? service.waitFor(handle) :
104            !instance && angular.isUndefined(enableWait) ? addLegacyAPI(service, handle) : instance;
105  };
106
107  /**
108   * For failed instance/handle lookups, older-clients expect an response object with noops
109   * that include `rejected promise APIs`
110   */
111  function addLegacyAPI(service, handle) {
112      var falseFn  = function() { return false; };
113      var rejectFn = function() {
114            return $q.when($mdUtil.supplant(errorMsg, [handle || ""]));
115          };
116
117      return angular.extend({
118        isLockedOpen : falseFn,
119        isOpen       : falseFn,
120        toggle       : rejectFn,
121        open         : rejectFn,
122        close        : rejectFn,
123        onClose      : angular.noop,
124        then : function(callback) {
125          return waitForInstance(handle)
126            .then(callback || angular.noop);
127        }
128       }, service);
129    }
130    /**
131     * Synchronously lookup the controller instance for the specified sidNav instance which has been
132     * registered with the markup `md-component-id`
133     */
134    function findInstance(handle, shouldWait) {
135      var instance = $mdComponentRegistry.get(handle);
136
137      if (!instance && !shouldWait) {
138
139        // Report missing instance
140        $log.error( $mdUtil.supplant(errorMsg, [handle || ""]) );
141
142        // The component has not registered itself... most like NOT yet created
143        // return null to indicate that the Sidenav is not in the DOM
144        return undefined;
145      }
146      return instance;
147    }
148
149    /**
150     * Asynchronously wait for the component instantiation,
151     * Deferred lookup of component instance using $component registry
152     */
153    function waitForInstance(handle) {
154      return $mdComponentRegistry.when(handle).catch($log.error);
155    }
156}
157/**
158 * @ngdoc directive
159 * @name mdSidenavFocus
160 * @module material.components.sidenav
161 *
162 * @restrict A
163 *
164 * @description
165 * `mdSidenavFocus` provides a way to specify the focused element when a sidenav opens.
166 * This is completely optional, as the sidenav itself is focused by default.
167 *
168 * @usage
169 * <hljs lang="html">
170 * <md-sidenav>
171 *   <form>
172 *     <md-input-container>
173 *       <label for="testInput">Label</label>
174 *       <input id="testInput" type="text" md-sidenav-focus>
175 *     </md-input-container>
176 *   </form>
177 * </md-sidenav>
178 * </hljs>
179 **/
180function SidenavFocusDirective() {
181  return {
182    restrict: 'A',
183    require: '^mdSidenav',
184    link: function(scope, element, attr, sidenavCtrl) {
185      // @see $mdUtil.findFocusTarget(...)
186    }
187  };
188}
189/**
190 * @ngdoc directive
191 * @name mdSidenav
192 * @module material.components.sidenav
193 * @restrict E
194 *
195 * @description
196 *
197 * A Sidenav component that can be opened and closed programatically.
198 *
199 * By default, upon opening it will slide out on top of the main content area.
200 *
201 * For keyboard and screen reader accessibility, focus is sent to the sidenav wrapper by default.
202 * It can be overridden with the `md-autofocus` directive on the child element you want focused.
203 *
204 * @usage
205 * <hljs lang="html">
206 * <div layout="row" ng-controller="MyController">
207 *   <md-sidenav md-component-id="left" class="md-sidenav-left">
208 *     Left Nav!
209 *   </md-sidenav>
210 *
211 *   <md-content>
212 *     Center Content
213 *     <md-button ng-click="openLeftMenu()">
214 *       Open Left Menu
215 *     </md-button>
216 *   </md-content>
217 *
218 *   <md-sidenav md-component-id="right"
219 *     md-is-locked-open="$mdMedia('min-width: 333px')"
220 *     class="md-sidenav-right">
221 *     <form>
222 *       <md-input-container>
223 *         <label for="testInput">Test input</label>
224 *         <input id="testInput" type="text"
225 *                ng-model="data" md-autofocus>
226 *       </md-input-container>
227 *     </form>
228 *   </md-sidenav>
229 * </div>
230 * </hljs>
231 *
232 * <hljs lang="js">
233 * var app = angular.module('myApp', ['ngMaterial']);
234 * app.controller('MyController', function($scope, $mdSidenav) {
235 *   $scope.openLeftMenu = function() {
236 *     $mdSidenav('left').toggle();
237 *   };
238 * });
239 * </hljs>
240 *
241 * @param {expression=} md-is-open A model bound to whether the sidenav is opened.
242 * @param {boolean=} md-disable-backdrop When present in the markup, the sidenav will not show a backdrop.
243 * @param {string=} md-component-id componentId to use with $mdSidenav service.
244 * @param {expression=} md-is-locked-open When this expression evaluates to true,
245 * the sidenav 'locks open': it falls into the content's flow instead
246 * of appearing over it. This overrides the `md-is-open` attribute.
247 * @param {string=} md-disable-scroll-target Selector, pointing to an element, whose scrolling will
248 * be disabled when the sidenav is opened. By default this is the sidenav's direct parent.
249 *
250* The $mdMedia() service is exposed to the is-locked-open attribute, which
251 * can be given a media query or one of the `sm`, `gt-sm`, `md`, `gt-md`, `lg` or `gt-lg` presets.
252 * Examples:
253 *
254 *   - `<md-sidenav md-is-locked-open="shouldLockOpen"></md-sidenav>`
255 *   - `<md-sidenav md-is-locked-open="$mdMedia('min-width: 1000px')"></md-sidenav>`
256 *   - `<md-sidenav md-is-locked-open="$mdMedia('sm')"></md-sidenav>` (locks open on small screens)
257 */
258function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $mdInteraction, $animate,
259                          $compile, $parse, $log, $q, $document, $window, $$rAF) {
260  return {
261    restrict: 'E',
262    scope: {
263      isOpen: '=?mdIsOpen'
264    },
265    controller: '$mdSidenavController',
266    compile: function(element) {
267      element.addClass('md-closed').attr('tabIndex', '-1');
268      return postLink;
269    }
270  };
271
272  /**
273   * Directive Post Link function...
274   */
275  function postLink(scope, element, attr, sidenavCtrl) {
276    var lastParentOverFlow;
277    var backdrop;
278    var disableScrollTarget = null;
279    var triggeringInteractionType;
280    var triggeringElement = null;
281    var previousContainerStyles;
282    var promise = $q.when(true);
283    var isLockedOpenParsed = $parse(attr.mdIsLockedOpen);
284    var ngWindow = angular.element($window);
285    var isLocked = function() {
286      return isLockedOpenParsed(scope.$parent, {
287        $media: function(arg) {
288          $log.warn("$media is deprecated for is-locked-open. Use $mdMedia instead.");
289          return $mdMedia(arg);
290        },
291        $mdMedia: $mdMedia
292      });
293    };
294
295    if (attr.mdDisableScrollTarget) {
296      disableScrollTarget = $document[0].querySelector(attr.mdDisableScrollTarget);
297
298      if (disableScrollTarget) {
299        disableScrollTarget = angular.element(disableScrollTarget);
300      } else {
301        $log.warn($mdUtil.supplant('mdSidenav: couldn\'t find element matching ' +
302          'selector "{selector}". Falling back to parent.', { selector: attr.mdDisableScrollTarget }));
303      }
304    }
305
306    if (!disableScrollTarget) {
307      disableScrollTarget = element.parent();
308    }
309
310    // Only create the backdrop if the backdrop isn't disabled.
311    if (!attr.hasOwnProperty('mdDisableBackdrop')) {
312      backdrop = $mdUtil.createBackdrop(scope, "md-sidenav-backdrop md-opaque ng-enter");
313    }
314
315    element.addClass('_md');     // private md component indicator for styling
316    $mdTheming(element);
317
318    // The backdrop should inherit the sidenavs theme,
319    // because the backdrop will take its parent theme by default.
320    if ( backdrop ) $mdTheming.inherit(backdrop, element);
321
322    element.on('$destroy', function() {
323      backdrop && backdrop.remove();
324      sidenavCtrl.destroy();
325    });
326
327    scope.$on('$destroy', function(){
328      backdrop && backdrop.remove();
329    });
330
331    scope.$watch(isLocked, updateIsLocked);
332    scope.$watch('isOpen', updateIsOpen);
333
334
335    // Publish special accessor for the Controller instance
336    sidenavCtrl.$toggleOpen = toggleOpen;
337
338    /**
339     * Toggle the DOM classes to indicate `locked`
340     * @param isLocked
341     */
342    function updateIsLocked(isLocked, oldValue) {
343      scope.isLockedOpen = isLocked;
344      if (isLocked === oldValue) {
345        element.toggleClass('md-locked-open', !!isLocked);
346      } else {
347        $animate[isLocked ? 'addClass' : 'removeClass'](element, 'md-locked-open');
348      }
349      if (backdrop) {
350        backdrop.toggleClass('md-locked-open', !!isLocked);
351      }
352    }
353
354    /**
355     * Toggle the SideNav view and attach/detach listeners
356     * @param isOpen
357     */
358    function updateIsOpen(isOpen) {
359      // Support deprecated md-sidenav-focus attribute as fallback
360      var focusEl = $mdUtil.findFocusTarget(element) || $mdUtil.findFocusTarget(element,'[md-sidenav-focus]') || element;
361      var parent = element.parent();
362
363      parent[isOpen ? 'on' : 'off']('keydown', onKeyDown);
364      if (backdrop) backdrop[isOpen ? 'on' : 'off']('click', close);
365
366      var restorePositioning = updateContainerPositions(parent, isOpen);
367
368      if ( isOpen ) {
369        // Capture upon opening..
370        triggeringElement = $document[0].activeElement;
371        triggeringInteractionType = $mdInteraction.getLastInteractionType();
372      }
373
374      disableParentScroll(isOpen);
375
376      return promise = $q.all([
377        isOpen && backdrop ? $animate.enter(backdrop, parent) : backdrop ?
378                             $animate.leave(backdrop) : $q.when(true),
379        $animate[isOpen ? 'removeClass' : 'addClass'](element, 'md-closed')
380      ]).then(function() {
381        // Perform focus when animations are ALL done...
382        if (scope.isOpen) {
383          $$rAF(function() {
384            // Notifies child components that the sidenav was opened. Should wait
385            // a frame in order to allow for the element height to be computed.
386            ngWindow.triggerHandler('resize');
387          });
388
389          focusEl && focusEl.focus();
390        }
391
392        // Restores the positioning on the sidenav and backdrop.
393        restorePositioning && restorePositioning();
394      });
395    }
396
397    function updateContainerPositions(parent, willOpen) {
398      var drawerEl = element[0];
399      var scrollTop = parent[0].scrollTop;
400
401      if (willOpen && scrollTop) {
402        previousContainerStyles = {
403          top: drawerEl.style.top,
404          bottom: drawerEl.style.bottom,
405          height: drawerEl.style.height
406        };
407
408        // When the parent is scrolled down, then we want to be able to show the sidenav at the current scroll
409        // position. We're moving the sidenav down to the correct scroll position and apply the height of the
410        // parent, to increase the performance. Using 100% as height, will impact the performance heavily.
411        var positionStyle = {
412          top: scrollTop + 'px',
413          bottom: 'auto',
414          height: parent[0].clientHeight + 'px'
415        };
416
417        // Apply the new position styles to the sidenav and backdrop.
418        element.css(positionStyle);
419        backdrop.css(positionStyle);
420      }
421
422      // When the sidenav is closing and we have previous defined container styles,
423      // then we return a restore function, which resets the sidenav and backdrop.
424      if (!willOpen && previousContainerStyles) {
425        return function() {
426          drawerEl.style.top = previousContainerStyles.top;
427          drawerEl.style.bottom = previousContainerStyles.bottom;
428          drawerEl.style.height = previousContainerStyles.height;
429
430          backdrop[0].style.top = null;
431          backdrop[0].style.bottom = null;
432          backdrop[0].style.height = null;
433
434          previousContainerStyles = null;
435        };
436      }
437    }
438
439    /**
440     * Prevent parent scrolling (when the SideNav is open)
441     */
442    function disableParentScroll(disabled) {
443      if ( disabled && !lastParentOverFlow ) {
444        lastParentOverFlow = disableScrollTarget.css('overflow');
445        disableScrollTarget.css('overflow', 'hidden');
446      } else if (angular.isDefined(lastParentOverFlow)) {
447        disableScrollTarget.css('overflow', lastParentOverFlow);
448        lastParentOverFlow = undefined;
449      }
450    }
451
452    /**
453     * Toggle the sideNav view and publish a promise to be resolved when
454     * the view animation finishes.
455     *
456     * @param isOpen
457     * @returns {*}
458     */
459    function toggleOpen( isOpen ) {
460      if (scope.isOpen == isOpen ) {
461
462        return $q.when(true);
463
464      } else {
465        if (scope.isOpen && sidenavCtrl.onCloseCb) sidenavCtrl.onCloseCb();
466
467        return $q(function(resolve){
468          // Toggle value to force an async `updateIsOpen()` to run
469          scope.isOpen = isOpen;
470
471          $mdUtil.nextTick(function() {
472            // When the current `updateIsOpen()` animation finishes
473            promise.then(function(result) {
474
475              if ( !scope.isOpen && triggeringElement && triggeringInteractionType === 'keyboard') {
476                // reset focus to originating element (if available) upon close
477                triggeringElement.focus();
478                triggeringElement = null;
479              }
480
481              resolve(result);
482            });
483          });
484
485        });
486
487      }
488    }
489
490    /**
491     * Auto-close sideNav when the `escape` key is pressed.
492     * @param evt
493     */
494    function onKeyDown(ev) {
495      var isEscape = (ev.keyCode === $mdConstant.KEY_CODE.ESCAPE);
496      return isEscape ? close(ev) : $q.when(true);
497    }
498
499    /**
500     * With backdrop `clicks` or `escape` key-press, immediately
501     * apply the CSS close transition... Then notify the controller
502     * to close() and perform its own actions.
503     */
504    function close(ev) {
505      ev.preventDefault();
506
507      return sidenavCtrl.close();
508    }
509
510  }
511}
512
513/*
514 * @private
515 * @ngdoc controller
516 * @name SidenavController
517 * @module material.components.sidenav
518 */
519function SidenavController($scope, $attrs, $mdComponentRegistry, $q, $interpolate) {
520
521  var self = this;
522
523  // Use Default internal method until overridden by directive postLink
524
525  // Synchronous getters
526  self.isOpen = function() { return !!$scope.isOpen; };
527  self.isLockedOpen = function() { return !!$scope.isLockedOpen; };
528
529  // Synchronous setters
530  self.onClose = function (callback) {
531    self.onCloseCb = callback;
532    return self;
533  };
534
535  // Async actions
536  self.open   = function() { return self.$toggleOpen( true );  };
537  self.close  = function() { return self.$toggleOpen( false ); };
538  self.toggle = function() { return self.$toggleOpen( !$scope.isOpen );  };
539  self.$toggleOpen = function(value) { return $q.when($scope.isOpen = value); };
540
541  // Evaluate the component id.
542  var rawId = $attrs.mdComponentId;
543  var hasDataBinding = rawId && rawId.indexOf($interpolate.startSymbol()) > -1;
544  var componentId = hasDataBinding ? $interpolate(rawId)($scope.$parent) : rawId;
545
546  // Register the component.
547  self.destroy = $mdComponentRegistry.register(self, componentId);
548
549  // Watch and update the component, if the id has changed.
550  if (hasDataBinding) {
551    $attrs.$observe('mdComponentId', function(id) {
552      if (id && id !== self.$$mdHandle) {
553        self.destroy(); // `destroy` only deregisters the old component id so we can add the new one.
554        self.destroy = $mdComponentRegistry.register(self, id);
555      }
556    });
557  }
558}
559
560})(window, window.angular);