source: trip-planner-front/node_modules/angular-material/modules/js/progressCircular/progressCircular.js@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 16.6 KB
Line 
1/*!
2 * AngularJS Material Design
3 * https://github.com/angular/material
4 * @license MIT
5 * v1.2.3
6 */
7(function( window, angular, undefined ){
8"use strict";
9
10/**
11 * @ngdoc module
12 * @name material.components.progressCircular
13 * @description Module for a circular progressbar
14 */
15
16angular.module('material.components.progressCircular', ['material.core']);
17
18/**
19 * @ngdoc directive
20 * @name mdProgressCircular
21 * @module material.components.progressCircular
22 * @restrict E
23 *
24 * @description
25 * The circular progress directive is used to make loading content in your app as delightful and
26 * painless as possible by minimizing the amount of visual change a user sees before they can view
27 * and interact with content.
28 *
29 * For operations where the percentage of the operation completed can be determined, use a
30 * determinate indicator. They give users a quick sense of how long an operation will take.
31 *
32 * For operations where the user is asked to wait a moment while something finishes up, and it’s
33 * not necessary to expose what's happening behind the scenes and how long it will take, use an
34 * indeterminate indicator.
35 *
36 * @param {string} md-mode Select from one of two modes: **'determinate'** and **'indeterminate'**.
37 *
38 * Note: if the `md-mode` value is set as undefined or specified as not 1 of the two (2) valid modes, then **'indeterminate'**
39 * will be auto-applied as the mode.
40 *
41 * Note: if not configured, the `md-mode="indeterminate"` will be auto injected as an attribute.
42 * If `value=""` is also specified, however, then `md-mode="determinate"` would be auto-injected instead.
43 * @param {number=} value In determinate mode, this number represents the percentage of the
44 * circular progress. Default: 0
45 * @param {number=} md-diameter This specifies the diameter of the circular progress. The value
46 * should be a pixel-size value (eg '100'). If this attribute is
47 * not present then a default value of '50px' is assumed.
48 *
49 * @param {boolean=} ng-disabled Determines whether to disable the progress element.
50 *
51 * @usage
52 * <hljs lang="html">
53 * <md-progress-circular md-mode="determinate" value="..."></md-progress-circular>
54 *
55 * <md-progress-circular md-mode="determinate" ng-value="..."></md-progress-circular>
56 *
57 * <md-progress-circular md-mode="determinate" value="..." md-diameter="100"></md-progress-circular>
58 *
59 * <md-progress-circular md-mode="indeterminate"></md-progress-circular>
60 * </hljs>
61 */
62
63MdProgressCircularDirective['$inject'] = ["$window", "$mdProgressCircular", "$mdTheming", "$mdUtil", "$interval", "$log"];
64angular
65 .module('material.components.progressCircular')
66 .directive('mdProgressCircular', MdProgressCircularDirective);
67
68/* ngInject */
69function MdProgressCircularDirective($window, $mdProgressCircular, $mdTheming,
70 $mdUtil, $interval, $log) {
71
72 // Note that this shouldn't use use $$rAF, because it can cause an infinite loop
73 // in any tests that call $animate.flush.
74 var rAF = $window.requestAnimationFrame ||
75 $window.webkitRequestAnimationFrame ||
76 angular.noop;
77
78 var cAF = $window.cancelAnimationFrame ||
79 $window.webkitCancelAnimationFrame ||
80 $window.webkitCancelRequestAnimationFrame ||
81 angular.noop;
82
83 var MODE_DETERMINATE = 'determinate';
84 var MODE_INDETERMINATE = 'indeterminate';
85 var DISABLED_CLASS = '_md-progress-circular-disabled';
86 var INDETERMINATE_CLASS = 'md-mode-indeterminate';
87
88 return {
89 restrict: 'E',
90 scope: {
91 value: '@',
92 mdDiameter: '@',
93 mdMode: '@'
94 },
95 template:
96 '<svg xmlns="http://www.w3.org/2000/svg">' +
97 '<path fill="none"/>' +
98 '</svg>',
99 compile: function(element, attrs) {
100 element.attr({
101 'aria-valuemin': 0,
102 'aria-valuemax': 100,
103 'role': 'progressbar'
104 });
105
106 if (angular.isUndefined(attrs.mdMode)) {
107 var mode = attrs.hasOwnProperty('value') ? MODE_DETERMINATE : MODE_INDETERMINATE;
108 attrs.$set('mdMode', mode);
109 } else {
110 attrs.$set('mdMode', attrs.mdMode.trim());
111 }
112
113 return MdProgressCircularLink;
114 }
115 };
116
117 function MdProgressCircularLink(scope, element, attrs) {
118 var node = element[0];
119 var svg = angular.element(node.querySelector('svg'));
120 var path = angular.element(node.querySelector('path'));
121 var startIndeterminate = $mdProgressCircular.startIndeterminate;
122 var endIndeterminate = $mdProgressCircular.endIndeterminate;
123 var iterationCount = 0;
124 var lastAnimationId = 0;
125 var lastDrawFrame;
126 var interval;
127
128 $mdTheming(element);
129 element.toggleClass(DISABLED_CLASS, attrs.hasOwnProperty('disabled'));
130
131 // If the mode is indeterminate, it doesn't need to
132 // wait for the next digest. It can start right away.
133 if (scope.mdMode === MODE_INDETERMINATE){
134 startIndeterminateAnimation();
135 }
136
137 scope.$on('$destroy', function(){
138 cleanupIndeterminateAnimation();
139
140 if (lastDrawFrame) {
141 cAF(lastDrawFrame);
142 }
143 });
144
145 scope.$watchGroup(['value', 'mdMode', function() {
146 var isDisabled = node.disabled;
147
148 // Sometimes the browser doesn't return a boolean, in
149 // which case we should check whether the attribute is
150 // present.
151 if (isDisabled === true || isDisabled === false){
152 return isDisabled;
153 }
154
155 return angular.isDefined(element.attr('disabled'));
156 }], function(newValues, oldValues) {
157 var mode = newValues[1];
158 var isDisabled = newValues[2];
159 var wasDisabled = oldValues[2];
160 var diameter = 0;
161 var strokeWidth = 0;
162
163 if (isDisabled !== wasDisabled) {
164 element.toggleClass(DISABLED_CLASS, !!isDisabled);
165 }
166
167 if (isDisabled) {
168 cleanupIndeterminateAnimation();
169 } else {
170 if (mode !== MODE_DETERMINATE && mode !== MODE_INDETERMINATE) {
171 mode = MODE_INDETERMINATE;
172 attrs.$set('mdMode', mode);
173 }
174
175 if (mode === MODE_INDETERMINATE) {
176 if (oldValues[1] === MODE_DETERMINATE) {
177 diameter = getSize(scope.mdDiameter);
178 strokeWidth = getStroke(diameter);
179 path.attr('d', getSvgArc(diameter, strokeWidth, true));
180 path.attr('stroke-dasharray', getDashLength(diameter, strokeWidth, 75));
181 }
182 startIndeterminateAnimation();
183 } else {
184 var newValue = clamp(newValues[0]);
185 var oldValue = clamp(oldValues[0]);
186
187 cleanupIndeterminateAnimation();
188
189 if (oldValues[1] === MODE_INDETERMINATE) {
190 diameter = getSize(scope.mdDiameter);
191 strokeWidth = getStroke(diameter);
192 path.attr('d', getSvgArc(diameter, strokeWidth, false));
193 path.attr('stroke-dasharray', getDashLength(diameter, strokeWidth, 100));
194 }
195
196 element.attr('aria-valuenow', newValue);
197 renderCircle(oldValue, newValue);
198 }
199 }
200
201 });
202
203 // This is in a separate watch in order to avoid layout, unless
204 // the value has actually changed.
205 scope.$watch('mdDiameter', function(newValue) {
206 var diameter = getSize(newValue);
207 var strokeWidth = getStroke(diameter);
208 var value = clamp(scope.value);
209 var transformOrigin = (diameter / 2) + 'px';
210 var dimensions = {
211 width: diameter + 'px',
212 height: diameter + 'px'
213 };
214
215 // The viewBox has to be applied via setAttribute, because it is
216 // case-sensitive. If jQuery is included in the page, `.attr` lowercases
217 // all attribute names.
218 svg[0].setAttribute('viewBox', '0 0 ' + diameter + ' ' + diameter);
219
220 // Usually viewBox sets the dimensions for the SVG, however that doesn't
221 // seem to be the case on IE10.
222 // Important! The transform origin has to be set from here and it has to
223 // be in the format of "Ypx Ypx Ypx", otherwise the rotation wobbles in
224 // IE and Edge, because they don't account for the stroke width when
225 // rotating. Also "center" doesn't help in this case, it has to be a
226 // precise value.
227 svg
228 .css(dimensions)
229 .css('transform-origin', transformOrigin + ' ' + transformOrigin + ' ' + transformOrigin);
230
231 element.css(dimensions);
232
233 path.attr('stroke-width', strokeWidth);
234 path.attr('stroke-linecap', 'square');
235 if (scope.mdMode == MODE_INDETERMINATE) {
236 path.attr('d', getSvgArc(diameter, strokeWidth, true));
237 path.attr('stroke-dasharray', getDashLength(diameter, strokeWidth, 75));
238 path.attr('stroke-dashoffset', getDashOffset(diameter, strokeWidth, 1, 75));
239 } else {
240 path.attr('d', getSvgArc(diameter, strokeWidth, false));
241 path.attr('stroke-dasharray', getDashLength(diameter, strokeWidth, 100));
242 path.attr('stroke-dashoffset', getDashOffset(diameter, strokeWidth, 0, 100));
243 renderCircle(value, value);
244 }
245
246 });
247
248 function renderCircle(animateFrom, animateTo, easing, duration, iterationCount, maxValue) {
249 var id = ++lastAnimationId;
250 var startTime = $mdUtil.now();
251 var changeInValue = animateTo - animateFrom;
252 var diameter = getSize(scope.mdDiameter);
253 var strokeWidth = getStroke(diameter);
254 var ease = easing || $mdProgressCircular.easeFn;
255 var animationDuration = duration || $mdProgressCircular.duration;
256 var rotation = -90 * (iterationCount || 0);
257 var dashLimit = maxValue || 100;
258
259 // No need to animate it if the values are the same
260 if (animateTo === animateFrom) {
261 renderFrame(animateTo);
262 } else {
263 lastDrawFrame = rAF(function animation() {
264 var currentTime = $window.Math.max(0, $window.Math.min($mdUtil.now() - startTime, animationDuration));
265
266 renderFrame(ease(currentTime, animateFrom, changeInValue, animationDuration));
267
268 // Do not allow overlapping animations
269 if (id === lastAnimationId && currentTime < animationDuration) {
270 lastDrawFrame = rAF(animation);
271 }
272 });
273 }
274
275 function renderFrame(value) {
276 path.attr('stroke-dashoffset', getDashOffset(diameter, strokeWidth, value, dashLimit));
277 path.attr('transform','rotate(' + (rotation) + ' ' + diameter/2 + ' ' + diameter/2 + ')');
278 }
279 }
280
281 function animateIndeterminate() {
282 renderCircle(
283 startIndeterminate,
284 endIndeterminate,
285 $mdProgressCircular.easeFnIndeterminate,
286 $mdProgressCircular.durationIndeterminate,
287 iterationCount,
288 75
289 );
290
291 // The %4 technically isn't necessary, but it keeps the rotation
292 // under 360, instead of becoming a crazy large number.
293 iterationCount = ++iterationCount % 4;
294
295 }
296
297 function startIndeterminateAnimation() {
298 if (!interval) {
299 // Note that this interval isn't supposed to trigger a digest.
300 interval = $interval(
301 animateIndeterminate,
302 $mdProgressCircular.durationIndeterminate,
303 0,
304 false
305 );
306
307 animateIndeterminate();
308
309 element
310 .addClass(INDETERMINATE_CLASS)
311 .removeAttr('aria-valuenow');
312 }
313 }
314
315 function cleanupIndeterminateAnimation() {
316 if (interval) {
317 $interval.cancel(interval);
318 interval = null;
319 element.removeClass(INDETERMINATE_CLASS);
320 }
321 }
322 }
323
324 /**
325 * Returns SVG path data for progress circle
326 * Syntax spec: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
327 *
328 * @param {number} diameter Diameter of the container.
329 * @param {number} strokeWidth Stroke width to be used when drawing circle
330 * @param {boolean} indeterminate Use if progress circle will be used for indeterminate
331 *
332 * @returns {string} String representation of an SVG arc.
333 */
334 function getSvgArc(diameter, strokeWidth, indeterminate) {
335 var radius = diameter / 2;
336 var offset = strokeWidth / 2;
337 var start = radius + ',' + offset; // ie: (25, 2.5) or 12 o'clock
338 var end = offset + ',' + radius; // ie: (2.5, 25) or 9 o'clock
339 var arcRadius = radius - offset;
340 return 'M' + start
341 + 'A' + arcRadius + ',' + arcRadius + ' 0 1 1 ' + end // 75% circle
342 + (indeterminate ? '' : 'A' + arcRadius + ',' + arcRadius + ' 0 0 1 ' + start); // loop to start
343 }
344
345 /**
346 * Return stroke length for progress circle
347 *
348 * @param {number} diameter Diameter of the container.
349 * @param {number} strokeWidth Stroke width to be used when drawing circle
350 * @param {number} value Percentage of circle (between 0 and 100)
351 * @param {number} maxArcLength Maximum length of arc as a percentage of circle (between 0 and 100)
352 *
353 * @returns {number} Stroke length for progress circle
354 */
355 function getDashOffset(diameter, strokeWidth, value, maxArcLength) {
356 return getSpinnerCircumference(diameter, strokeWidth) * ((maxArcLength - value) / 100);
357 }
358
359 /**
360 * Limits a value between 0 and 100.
361 */
362 function clamp(value) {
363 return $window.Math.max(0, $window.Math.min(value || 0, 100));
364 }
365
366 /**
367 * Determines the size of a progress circle, based on the provided
368 * value in the following formats: `X`, `Ypx`, `Z%`.
369 */
370 function getSize(value) {
371 var defaultValue = $mdProgressCircular.progressSize;
372
373 if (value) {
374 var parsed = parseFloat(value);
375
376 if (value.lastIndexOf('%') === value.length - 1) {
377 parsed = (parsed / 100) * defaultValue;
378 }
379
380 return parsed;
381 }
382
383 return defaultValue;
384 }
385
386 /**
387 * Determines the circle's stroke width, based on
388 * the provided diameter.
389 */
390 function getStroke(diameter) {
391 return $mdProgressCircular.strokeWidth / 100 * diameter;
392 }
393
394 /**
395 * Return length of the dash
396 *
397 * @param {number} diameter Diameter of the container.
398 * @param {number} strokeWidth Stroke width to be used when drawing circle
399 * @param {number} value Percentage of circle (between 0 and 100)
400 *
401 * @returns {number} Length of the dash
402 */
403 function getDashLength(diameter, strokeWidth, value) {
404 return getSpinnerCircumference(diameter, strokeWidth) * (value / 100);
405 }
406
407 /**
408 * Return circumference of the spinner
409 *
410 * @param {number} diameter Diameter of the container.
411 * @param {number} strokeWidth Stroke width to be used when drawing circle
412 *
413 * @returns {number} Circumference of the spinner
414 */
415 function getSpinnerCircumference(diameter, strokeWidth) {
416 return ((diameter - strokeWidth) * $window.Math.PI);
417 }
418
419}
420
421/**
422 * @ngdoc service
423 * @name $mdProgressCircular
424 * @module material.components.progressCircular
425 *
426 * @description
427 * Allows the user to specify the default options for the `progressCircular` directive.
428 *
429 * @property {number} progressSize Diameter of the progress circle in pixels.
430 * @property {number} strokeWidth Width of the circle's stroke as a percentage of the circle's size.
431 * @property {number} duration Length of the circle animation in milliseconds.
432 * @property {function} easeFn Default easing animation function.
433 * @property {object} easingPresets Collection of pre-defined easing functions.
434 *
435 * @property {number} durationIndeterminate Duration of the indeterminate animation.
436 * @property {number} startIndeterminate Indeterminate animation start point.
437 * @property {number} endIndeterminate Indeterminate animation end point.
438 * @property {function} easeFnIndeterminate Easing function to be used when animating
439 * between the indeterminate values.
440 *
441 * @property {(function(object): object)} configure Used to modify the default options.
442 *
443 * @usage
444 * <hljs lang="js">
445 * myAppModule.config(function($mdProgressCircularProvider) {
446 *
447 * // Example of changing the default progress options.
448 * $mdProgressCircularProvider.configure({
449 * progressSize: 100,
450 * strokeWidth: 20,
451 * duration: 800
452 * });
453 * });
454 * </hljs>
455 *
456 */
457
458angular
459 .module('material.components.progressCircular')
460 .provider("$mdProgressCircular", MdProgressCircularProvider);
461
462function MdProgressCircularProvider() {
463 var progressConfig = {
464 progressSize: 50,
465 strokeWidth: 10,
466 duration: 100,
467 easeFn: linearEase,
468
469 durationIndeterminate: 1333,
470 startIndeterminate: 1,
471 endIndeterminate: 149,
472 easeFnIndeterminate: materialEase,
473
474 easingPresets: {
475 linearEase: linearEase,
476 materialEase: materialEase
477 }
478 };
479
480 return {
481 configure: function(options) {
482 progressConfig = angular.extend(progressConfig, options || {});
483 return progressConfig;
484 },
485 $get: function() { return progressConfig; }
486 };
487
488 function linearEase(t, b, c, d) {
489 return c * t / d + b;
490 }
491
492 function materialEase(t, b, c, d) {
493 // via http://www.timotheegroleau.com/Flash/experiments/easing_function_generator.htm
494 // with settings of [0, 0, 1, 1]
495 var ts = (t /= d) * t;
496 var tc = ts * t;
497 return b + c * (6 * tc * ts + -15 * ts * ts + 10 * tc);
498 }
499}
500
501})(window, window.angular);
Note: See TracBrowser for help on using the repository browser.