arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-material/modules/closure/slider/slider.js
www/bower_components/angular-material/modules/closure/slider/slider.jsRaw
  1/*!
  2 * Angular Material Design
  3 * https://github.com/angular/material
  4 * @license MIT
  5 * v1.1.3
  6 */
  7goog.provide('ngmaterial.components.slider');
  8goog.require('ngmaterial.core');
  9  /**
 10   * @ngdoc module
 11   * @name material.components.slider
 12   */
 13SliderDirective['$inject'] = ["$$rAF", "$window", "$mdAria", "$mdUtil", "$mdConstant", "$mdTheming", "$mdGesture", "$parse", "$log", "$timeout"];
 14  angular.module('material.components.slider', [
 15    'material.core'
 16  ])
 17  .directive('mdSlider', SliderDirective)
 18  .directive('mdSliderContainer', SliderContainerDirective);
 19
 20/**
 21 * @ngdoc directive
 22 * @name mdSliderContainer
 23 * @module material.components.slider
 24 * @restrict E
 25 * @description
 26 * The `<md-slider-container>` contains slider with two other elements.
 27 *
 28 *
 29 * @usage
 30 * <h4>Normal Mode</h4>
 31 * <hljs lang="html">
 32 * </hljs>
 33 */
 34function SliderContainerDirective() {
 35  return {
 36    controller: function () {},
 37    compile: function (elem) {
 38      var slider = elem.find('md-slider');
 39
 40      if (!slider) {
 41        return;
 42      }
 43
 44      var vertical = slider.attr('md-vertical');
 45
 46      if (vertical !== undefined) {
 47        elem.attr('md-vertical', '');
 48      }
 49
 50      if(!slider.attr('flex')) {
 51        slider.attr('flex', '');
 52      }
 53
 54      return function postLink(scope, element, attr, ctrl) {
 55        element.addClass('_md');     // private md component indicator for styling
 56
 57        // We have to manually stop the $watch on ngDisabled because it exists
 58        // on the parent scope, and won't be automatically destroyed when
 59        // the component is destroyed.
 60        function setDisable(value) {
 61          element.children().attr('disabled', value);
 62          element.find('input').attr('disabled', value);
 63        }
 64
 65        var stopDisabledWatch = angular.noop;
 66
 67        if (attr.disabled) {
 68          setDisable(true);
 69        }
 70        else if (attr.ngDisabled) {
 71          stopDisabledWatch = scope.$watch(attr.ngDisabled, function (value) {
 72            setDisable(value);
 73          });
 74        }
 75
 76        scope.$on('$destroy', function () {
 77          stopDisabledWatch();
 78        });
 79
 80        var initialMaxWidth;
 81
 82        ctrl.fitInputWidthToTextLength = function (length) {
 83          var input = element[0].querySelector('md-input-container');
 84
 85          if (input) {
 86            var computedStyle = getComputedStyle(input);
 87            var minWidth = parseInt(computedStyle.minWidth);
 88            var padding = parseInt(computedStyle.padding) * 2;
 89
 90            initialMaxWidth = initialMaxWidth || parseInt(computedStyle.maxWidth);
 91            var newMaxWidth = Math.max(initialMaxWidth, minWidth + padding + (minWidth / 2 * length));
 92
 93            input.style.maxWidth = newMaxWidth + 'px';
 94          }
 95        };
 96      };
 97    }
 98  };
 99}
100
101/**
102 * @ngdoc directive
103 * @name mdSlider
104 * @module material.components.slider
105 * @restrict E
106 * @description
107 * The `<md-slider>` component allows the user to choose from a range of
108 * values.
109 *
110 * As per the [material design spec](http://www.google.com/design/spec/style/color.html#color-ui-color-application)
111 * the slider is in the accent color by default. The primary color palette may be used with
112 * the `md-primary` class.
113 *
114 * It has two modes: 'normal' mode, where the user slides between a wide range
115 * of values, and 'discrete' mode, where the user slides between only a few
116 * select values.
117 *
118 * To enable discrete mode, add the `md-discrete` attribute to a slider,
119 * and use the `step` attribute to change the distance between
120 * values the user is allowed to pick.
121 *
122 * @usage
123 * <h4>Normal Mode</h4>
124 * <hljs lang="html">
125 * <md-slider ng-model="myValue" min="5" max="500">
126 * </md-slider>
127 * </hljs>
128 * <h4>Discrete Mode</h4>
129 * <hljs lang="html">
130 * <md-slider md-discrete ng-model="myDiscreteValue" step="10" min="10" max="130">
131 * </md-slider>
132 * </hljs>
133 * <h4>Invert Mode</h4>
134 * <hljs lang="html">
135 * <md-slider md-invert ng-model="myValue" step="10" min="10" max="130">
136 * </md-slider>
137 * </hljs>
138 *
139 * @param {boolean=} md-discrete Whether to enable discrete mode.
140 * @param {boolean=} md-invert Whether to enable invert mode.
141 * @param {number=} step The distance between values the user is allowed to pick. Default 1.
142 * @param {number=} min The minimum value the user is allowed to pick. Default 0.
143 * @param {number=} max The maximum value the user is allowed to pick. Default 100.
144 * @param {number=} round The amount of numbers after the decimal point, maximum is 6 to prevent scientific notation. Default 3.
145 */
146function SliderDirective($$rAF, $window, $mdAria, $mdUtil, $mdConstant, $mdTheming, $mdGesture, $parse, $log, $timeout) {
147  return {
148    scope: {},
149    require: ['?ngModel', '?^mdSliderContainer'],
150    template:
151      '<div class="md-slider-wrapper">' +
152        '<div class="md-slider-content">' +
153          '<div class="md-track-container">' +
154            '<div class="md-track"></div>' +
155            '<div class="md-track md-track-fill"></div>' +
156            '<div class="md-track-ticks"></div>' +
157          '</div>' +
158          '<div class="md-thumb-container">' +
159            '<div class="md-thumb"></div>' +
160            '<div class="md-focus-thumb"></div>' +
161            '<div class="md-focus-ring"></div>' +
162            '<div class="md-sign">' +
163              '<span class="md-thumb-text"></span>' +
164            '</div>' +
165            '<div class="md-disabled-thumb"></div>' +
166          '</div>' +
167        '</div>' +
168      '</div>',
169    compile: compile
170  };
171
172  // **********************************************************
173  // Private Methods
174  // **********************************************************
175
176  function compile (tElement, tAttrs) {
177    var wrapper = angular.element(tElement[0].getElementsByClassName('md-slider-wrapper'));
178
179    var tabIndex = tAttrs.tabindex || 0;
180    wrapper.attr('tabindex', tabIndex);
181
182    if (tAttrs.disabled || tAttrs.ngDisabled) wrapper.attr('tabindex', -1);
183
184    wrapper.attr('role', 'slider');
185
186    $mdAria.expect(tElement, 'aria-label');
187
188    return postLink;
189  }
190
191  function postLink(scope, element, attr, ctrls) {
192    $mdTheming(element);
193    var ngModelCtrl = ctrls[0] || {
194      // Mock ngModelController if it doesn't exist to give us
195      // the minimum functionality needed
196      $setViewValue: function(val) {
197        this.$viewValue = val;
198        this.$viewChangeListeners.forEach(function(cb) { cb(); });
199      },
200      $parsers: [],
201      $formatters: [],
202      $viewChangeListeners: []
203    };
204
205    var containerCtrl = ctrls[1];
206    var container = angular.element($mdUtil.getClosest(element, '_md-slider-container', true));
207    var isDisabled = attr.ngDisabled ? angular.bind(null, $parse(attr.ngDisabled), scope.$parent) : function () {
208          return element[0].hasAttribute('disabled');
209        };
210
211    var thumb = angular.element(element[0].querySelector('.md-thumb'));
212    var thumbText = angular.element(element[0].querySelector('.md-thumb-text'));
213    var thumbContainer = thumb.parent();
214    var trackContainer = angular.element(element[0].querySelector('.md-track-container'));
215    var activeTrack = angular.element(element[0].querySelector('.md-track-fill'));
216    var tickContainer = angular.element(element[0].querySelector('.md-track-ticks'));
217    var wrapper = angular.element(element[0].getElementsByClassName('md-slider-wrapper'));
218    var content = angular.element(element[0].getElementsByClassName('md-slider-content'));
219    var throttledRefreshDimensions = $mdUtil.throttle(refreshSliderDimensions, 5000);
220
221    // Default values, overridable by attrs
222    var DEFAULT_ROUND = 3;
223    var vertical = angular.isDefined(attr.mdVertical);
224    var discrete = angular.isDefined(attr.mdDiscrete);
225    var invert = angular.isDefined(attr.mdInvert);
226    angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(0);
227    angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(100);
228    angular.isDefined(attr.step)? attr.$observe('step', updateStep) : updateStep(1);
229    angular.isDefined(attr.round)? attr.$observe('round', updateRound) : updateRound(DEFAULT_ROUND);
230
231    // We have to manually stop the $watch on ngDisabled because it exists
232    // on the parent scope, and won't be automatically destroyed when
233    // the component is destroyed.
234    var stopDisabledWatch = angular.noop;
235    if (attr.ngDisabled) {
236      stopDisabledWatch = scope.$parent.$watch(attr.ngDisabled, updateAriaDisabled);
237    }
238
239    $mdGesture.register(wrapper, 'drag', { horizontal: !vertical });
240
241    scope.mouseActive = false;
242
243    wrapper
244      .on('keydown', keydownListener)
245      .on('mousedown', mouseDownListener)
246      .on('focus', focusListener)
247      .on('blur', blurListener)
248      .on('$md.pressdown', onPressDown)
249      .on('$md.pressup', onPressUp)
250      .on('$md.dragstart', onDragStart)
251      .on('$md.drag', onDrag)
252      .on('$md.dragend', onDragEnd);
253
254    // On resize, recalculate the slider's dimensions and re-render
255    function updateAll() {
256      refreshSliderDimensions();
257      ngModelRender();
258    }
259    setTimeout(updateAll, 0);
260
261    var debouncedUpdateAll = $$rAF.throttle(updateAll);
262    angular.element($window).on('resize', debouncedUpdateAll);
263
264    scope.$on('$destroy', function() {
265      angular.element($window).off('resize', debouncedUpdateAll);
266    });
267
268    ngModelCtrl.$render = ngModelRender;
269    ngModelCtrl.$viewChangeListeners.push(ngModelRender);
270    ngModelCtrl.$formatters.push(minMaxValidator);
271    ngModelCtrl.$formatters.push(stepValidator);
272
273    /**
274     * Attributes
275     */
276    var min;
277    var max;
278    var step;
279    var round;
280    function updateMin(value) {
281      min = parseFloat(value);
282      element.attr('aria-valuemin', value);
283      updateAll();
284    }
285    function updateMax(value) {
286      max = parseFloat(value);
287      element.attr('aria-valuemax', value);
288      updateAll();
289    }
290    function updateStep(value) {
291      step = parseFloat(value);
292    }
293    function updateRound(value) {
294      // Set max round digits to 6, after 6 the input uses scientific notation
295      round = minMaxValidator(parseInt(value), 0, 6);
296    }
297    function updateAriaDisabled() {
298      element.attr('aria-disabled', !!isDisabled());
299    }
300
301    // Draw the ticks with canvas.
302    // The alternative to drawing ticks with canvas is to draw one element for each tick,
303    // which could quickly become a performance bottleneck.
304    var tickCanvas, tickCtx;
305    function redrawTicks() {
306      if (!discrete || isDisabled()) return;
307      if ( angular.isUndefined(step) )         return;
308
309      if ( step <= 0 ) {
310        var msg = 'Slider step value must be greater than zero when in discrete mode';
311        $log.error(msg);
312        throw new Error(msg);
313      }
314
315      var numSteps = Math.floor( (max - min) / step );
316      if (!tickCanvas) {
317        tickCanvas = angular.element('<canvas>').css('position', 'absolute');
318        tickContainer.append(tickCanvas);
319
320        tickCtx = tickCanvas[0].getContext('2d');
321      }
322
323      var dimensions = getSliderDimensions();
324
325      // If `dimensions` doesn't have height and width it might be the first attempt so we will refresh dimensions
326      if (dimensions && !dimensions.height && !dimensions.width) {
327        refreshSliderDimensions();
328        dimensions = sliderDimensions;
329      }
330
331      tickCanvas[0].width = dimensions.width;
332      tickCanvas[0].height = dimensions.height;
333
334      var distance;
335      for (var i = 0; i <= numSteps; i++) {
336        var trackTicksStyle = $window.getComputedStyle(tickContainer[0]);
337        tickCtx.fillStyle = trackTicksStyle.color || 'black';
338
339        distance = Math.floor((vertical ? dimensions.height : dimensions.width) * (i / numSteps));
340
341        tickCtx.fillRect(vertical ? 0 : distance - 1,
342          vertical ? distance - 1 : 0,
343          vertical ? dimensions.width : 2,
344          vertical ? 2 : dimensions.height);
345      }
346    }
347
348    function clearTicks() {
349      if(tickCanvas && tickCtx) {
350        var dimensions = getSliderDimensions();
351        tickCtx.clearRect(0, 0, dimensions.width, dimensions.height);
352      }
353    }
354
355    /**
356     * Refreshing Dimensions
357     */
358    var sliderDimensions = {};
359    refreshSliderDimensions();
360    function refreshSliderDimensions() {
361      sliderDimensions = trackContainer[0].getBoundingClientRect();
362    }
363    function getSliderDimensions() {
364      throttledRefreshDimensions();
365      return sliderDimensions;
366    }
367
368    /**
369     * left/right/up/down arrow listener
370     */
371    function keydownListener(ev) {
372      if (isDisabled()) return;
373
374      var changeAmount;
375      if (vertical ? ev.keyCode === $mdConstant.KEY_CODE.DOWN_ARROW : ev.keyCode === $mdConstant.KEY_CODE.LEFT_ARROW) {
376        changeAmount = -step;
377      } else if (vertical ? ev.keyCode === $mdConstant.KEY_CODE.UP_ARROW : ev.keyCode === $mdConstant.KEY_CODE.RIGHT_ARROW) {
378        changeAmount = step;
379      }
380      changeAmount = invert ? -changeAmount : changeAmount;
381      if (changeAmount) {
382        if (ev.metaKey || ev.ctrlKey || ev.altKey) {
383          changeAmount *= 4;
384        }
385        ev.preventDefault();
386        ev.stopPropagation();
387        scope.$evalAsync(function() {
388          setModelValue(ngModelCtrl.$viewValue + changeAmount);
389        });
390      }
391    }
392
393    function mouseDownListener() {
394      redrawTicks();
395
396      scope.mouseActive = true;
397      wrapper.removeClass('md-focused');
398
399      $timeout(function() {
400        scope.mouseActive = false;
401      }, 100);
402    }
403
404    function focusListener() {
405      if (scope.mouseActive === false) {
406        wrapper.addClass('md-focused');
407      }
408    }
409
410    function blurListener() {
411      wrapper.removeClass('md-focused');
412      element.removeClass('md-active');
413      clearTicks();
414    }
415
416    /**
417     * ngModel setters and validators
418     */
419    function setModelValue(value) {
420      ngModelCtrl.$setViewValue( minMaxValidator(stepValidator(value)) );
421    }
422    function ngModelRender() {
423      if (isNaN(ngModelCtrl.$viewValue)) {
424        ngModelCtrl.$viewValue = ngModelCtrl.$modelValue;
425      }
426
427      ngModelCtrl.$viewValue = minMaxValidator(ngModelCtrl.$viewValue);
428
429      var percent = valueToPercent(ngModelCtrl.$viewValue);
430      scope.modelValue = ngModelCtrl.$viewValue;
431      element.attr('aria-valuenow', ngModelCtrl.$viewValue);
432      setSliderPercent(percent);
433      thumbText.text( ngModelCtrl.$viewValue );
434    }
435
436    function minMaxValidator(value, minValue, maxValue) {
437      if (angular.isNumber(value)) {
438        minValue = angular.isNumber(minValue) ? minValue : min;
439        maxValue = angular.isNumber(maxValue) ? maxValue : max;
440
441        return Math.max(minValue, Math.min(maxValue, value));
442      }
443    }
444
445    function stepValidator(value) {
446      if (angular.isNumber(value)) {
447        var formattedValue = (Math.round((value - min) / step) * step + min);
448        formattedValue = (Math.round(formattedValue * Math.pow(10, round)) / Math.pow(10, round));
449
450        if (containerCtrl && containerCtrl.fitInputWidthToTextLength){
451          $mdUtil.debounce(function () {
452            containerCtrl.fitInputWidthToTextLength(formattedValue.toString().length);
453          }, 100)();
454        }
455
456        return formattedValue;
457      }
458    }
459
460    /**
461     * @param percent 0-1
462     */
463    function setSliderPercent(percent) {
464
465      percent = clamp(percent);
466
467      var thumbPosition = (percent * 100) + '%';
468      var activeTrackPercent = invert ? (1 - percent) * 100 + '%' : thumbPosition;
469
470      if (vertical) {
471        thumbContainer.css('bottom', thumbPosition);
472      }
473      else {
474        $mdUtil.bidiProperty(thumbContainer, 'left', 'right', thumbPosition);
475      }
476
477      
478      activeTrack.css(vertical ? 'height' : 'width', activeTrackPercent);
479
480      element.toggleClass((invert ? 'md-max' : 'md-min'), percent === 0);
481      element.toggleClass((invert ? 'md-min' : 'md-max'), percent === 1);
482    }
483
484    /**
485     * Slide listeners
486     */
487    var isDragging = false;
488
489    function onPressDown(ev) {
490      if (isDisabled()) return;
491
492      element.addClass('md-active');
493      element[0].focus();
494      refreshSliderDimensions();
495
496      var exactVal = percentToValue( positionToPercent( vertical ? ev.pointer.y : ev.pointer.x ));
497      var closestVal = minMaxValidator( stepValidator(exactVal) );
498      scope.$apply(function() {
499        setModelValue( closestVal );
500        setSliderPercent( valueToPercent(closestVal));
501      });
502    }
503    function onPressUp(ev) {
504      if (isDisabled()) return;
505
506      element.removeClass('md-dragging');
507
508      var exactVal = percentToValue( positionToPercent( vertical ? ev.pointer.y : ev.pointer.x ));
509      var closestVal = minMaxValidator( stepValidator(exactVal) );
510      scope.$apply(function() {
511        setModelValue(closestVal);
512        ngModelRender();
513      });
514    }
515    function onDragStart(ev) {
516      if (isDisabled()) return;
517      isDragging = true;
518
519      ev.stopPropagation();
520
521      element.addClass('md-dragging');
522      setSliderFromEvent(ev);
523    }
524    function onDrag(ev) {
525      if (!isDragging) return;
526      ev.stopPropagation();
527      setSliderFromEvent(ev);
528    }
529    function onDragEnd(ev) {
530      if (!isDragging) return;
531      ev.stopPropagation();
532      isDragging = false;
533    }
534
535    function setSliderFromEvent(ev) {
536      // While panning discrete, update only the
537      // visual positioning but not the model value.
538      if ( discrete ) adjustThumbPosition( vertical ? ev.pointer.y : ev.pointer.x );
539      else            doSlide( vertical ? ev.pointer.y : ev.pointer.x );
540    }
541
542    /**
543     * Slide the UI by changing the model value
544     * @param x
545     */
546    function doSlide( x ) {
547      scope.$evalAsync( function() {
548        setModelValue( percentToValue( positionToPercent(x) ));
549      });
550    }
551
552    /**
553     * Slide the UI without changing the model (while dragging/panning)
554     * @param x
555     */
556    function adjustThumbPosition( x ) {
557      var exactVal = percentToValue( positionToPercent( x ));
558      var closestVal = minMaxValidator( stepValidator(exactVal) );
559      setSliderPercent( positionToPercent(x) );
560      thumbText.text( closestVal );
561    }
562
563    /**
564    * Clamps the value to be between 0 and 1.
565    * @param {number} value The value to clamp.
566    * @returns {number}
567    */
568    function clamp(value) {
569      return Math.max(0, Math.min(value || 0, 1));
570    }
571
572    /**
573     * Convert position on slider to percentage value of offset from beginning...
574     * @param position
575     * @returns {number}
576     */
577    function positionToPercent( position ) {
578      var offset = vertical ? sliderDimensions.top : sliderDimensions.left;
579      var size = vertical ? sliderDimensions.height : sliderDimensions.width;
580      var calc = (position - offset) / size;
581
582      if (!vertical && $mdUtil.bidi() === 'rtl') {
583        calc = 1 - calc;
584      }
585
586      return Math.max(0, Math.min(1, vertical ? 1 - calc : calc));
587    }
588
589    /**
590     * Convert percentage offset on slide to equivalent model value
591     * @param percent
592     * @returns {*}
593     */
594    function percentToValue( percent ) {
595      var adjustedPercent = invert ? (1 - percent) : percent;
596      return (min + adjustedPercent * (max - min));
597    }
598
599    function valueToPercent( val ) {
600      var percent = (val - min) / (max - min);
601      return invert ? (1 - percent) : percent;
602    }
603  }
604}
605
606ngmaterial.components.slider = angular.module("material.components.slider");