PageSourceSearch

https://www.jilllowe.com/wp-content/plugins/trx_addons/components/shortcodes/icons/vivus.js

js jilllowe.com collected 2026-10-04 02:08:48 UTC 30,421 bytes, 1,015 lines download raw bytes

1/**
2 * vivus - JavaScript library to make drawing animation on SVG
3 * @version v0.3.1
4 * @link https://github.com/maxwellito/vivus
5 * @license MIT
6 */
7
8'use strict';
9
10(function (window, document) {
11
12  'use strict';
13
14/**
15 * Pathformer
16 * Beta version
17 *
18 * Take any SVG version 1.1 and transform
19 * child elements to 'path' elements
20 *
21 * This code is purely forked from
22 * https://github.com/Waest/SVGPathConverter
23 */
24
25/**
26 * Class constructor
27 *
28 * @param {DOM|String} element Dom element of the SVG or id of it
29 */
30function Pathformer(element) {
31  // Test params
32  if (typeof element === 'undefined') {
33    throw new Error('Pathformer [constructor]: "element" parameter is required');
34  }
35
36  // Set the element
37  if (element.constructor === String) {
38    element = document.getElementById(element);
39    if (!element) {
40      throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');
41    }
42  }
43  if (element.constructor instanceof window.SVGElement || /^svg$/i.test(element.nodeName)) {
44    this.el = element;
45  } else {
46    throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');
47  }
48
49  // Start
50  this.scan(element);
51}
52
53/**
54 * List of tags which can be transformed
55 * to path elements
56 *
57 * @type {Array}
58 */
59Pathformer.prototype.TYPES = ['line', 'ellipse', 'circle', 'polygon', 'polyline', 'rect'];
60
61/**
62 * List of attribute names which contain
63 * data. This array list them to check if
64 * they contain bad values, like percentage. 
65 *
66 * @type {Array}
67 */
68Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', '
68y1', 'y2'];
69
70/**
71 * Finds the elements compatible for transform
72 * and apply the liked method
73 *
74 * @param  {object} options Object from the constructor
75 */
76Pathformer.prototype.scan = function (svg) {
77  var fn, element, pathData, pathDom,
78    elements = svg.querySelectorAll(this.TYPES.join(','));
79  for (var i = 0; i < elements.length; i++) {
80    element = elements[i];
81    fn = this[element.tagName.toLowerCase() + 'ToPath'];
82    pathData = fn(this.parseAttr(element.attributes));
83    pathDom = this.pathMaker(element, pathData);
84    element.parentNode.replaceChild(pathDom, element);
85  }
86};
87
88
89/**
90 * Read `line` element to extract and transform
91 * data, to make it ready for a `path` object.
92 *
93 * @param  {DOMelement} element Line element to transform
94 * @return {object}             Data for a `path` element
95 */
96Pathformer.prototype.lineToPath = function (element) {
97  var newElement = {};
98  newElement.d = 'M' + element.x1 + ',' + element.y1 + 'L' + element.x2 + ',' + element.y2;
99  return newElement;
100};
101
102/**
103 * Read `rect` element to extract and transform
104 * data, to make it ready for a `path` object.
105 * The radius-border is not taken in charge yet.
106 * (your help is more than welcomed)
107 *
108 * @param  {DOMelement} element Rect element to transform
109 * @return {object}             Data for a `path` element
110 */
111Pathformer.prototype.rectToPath = function (element) {
112  var newElement = {},
113    x = parseFloat(element.x) || 0,
114    y = parseFloat(element.y) || 0,
115    width = parseFloat(element.width) || 0,
116    height = parseFloat(element.height) || 0;
117  newElement.d  = 'M' + x + ' ' + y + ' ';
118  newElement.d += 'L' + (x + width) + ' ' + y + ' ';
119  newElement.d += 'L' + (x + width) + ' ' + (y + height) + ' ';
120  newElement.d += 'L' + x + ' ' + (y + height) + ' Z';
121  return newElement;
122};
123
124/**
125 * Read `polyline` element to extract and transform
126 * data, to make it ready for a `path` object.
127 *
128 * @param  {DOMelement} element Polyline element to transform
129 * @return {object}             Data for a `path` element
130 */
131Pathformer.prototype.polylineToPath = function (element) {
132  var i, path;
133  var newElement = {};
134  var points = element.points.trim().split(' ');
135  
136  // Reformatting if points are defined without commas
137  if (element.points.indexOf(',') === -1) {
138    var formattedPoints = [];
139    for (i = 0; i < points.length; i+=2) {
140      formattedPoints.push(points[i] + ',' + points[i+1]);
141    }
142    points = formattedPoints;
143  }
144
145  // Generate the path.d value
146  path = 'M' + points[0];
147  for(i = 1; i < points.length; i++) {
148    if (points[i].indexOf(',') !== -1) {
149      path += 'L' + points[i];
150    }
151  }
152  newElement.d = path;
153  return newElement;
154};
155
156/**
157 * Read `polygon` element to extract and transform
158 * data, to make it ready for a `path` object.
159 * This method rely on polylineToPath, because the
160 * logic is similar. The path created is just closed,
161 * so it needs an 'Z' at the end.
162 *
163 * @param  {DOMelement} element Polygon element to transform
164 * @return {object}             Data for a `path` element
165 */
166Pathformer.prototype.polygonToPath = function (element) {
167  var newElement = Pathformer.prototype.polylineToPath(element);
168  newElement.d += 'Z';
169  return newElement;
170};
171
172/**
173 * Read `ellipse` element to extract and transform
174 * data, to make it ready for a `path` object.
175 *
176 * @param  {DOMelement} element ellipse element to transform
177 * @return {object}             Data for a `path` element
178 */
179Pathformer.prototype.ellipseToPath = function (element) {
180  var startX = element.cx - element.rx,
181      startY = element.cy;
182  var endX = parseFloat(element.cx) + parseFloat(element.rx),
183      endY = element.cy;
184
185  var newElement = {};
186  newElement.d = 'M' + startX + ',' + startY +
187                 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + endX + ',' + endY +
188                 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + startX + ',' + endY;
189  return newElement;
190};
191
192/**
193 * Read `circle` element to extract and transform
194 * data, to make it ready for a `path` object.
195 *
196 * @param  {DOMelement} element Circle element to transform
197 * @return {object}             Data for a `path` element
198 */
199Pathformer.prototype.circleToPath = function (element) {
200  var newElement = {};
201  var startX = element.cx - element.r,
202      startY = element.cy;
203  var endX = parseFloat(element.cx) + parseFloat(element.r),
204      endY = element.cy;
205  newElement.d =  'M' + startX + ',' + startY +
206                  'A' + element.r + ',' + element.r + ' 0,1,1 ' + endX + ',' + endY +
207                  'A' + element.r + ',' + element.r + ' 0,1,1 ' + startX + ',' + endY;
208  return newElement;
209};
210
211/**
212 * Create `path` elements form original element
213 * and prepared objects
214 *
215 * @param  {DOMelement} element  Original element to transform
216 * @param  {object} pathData     Path data (from `toPath` methods)
217 * @return {DOMelement}          Path element
218 */
219Pathformer.prototype.pathMaker = function (element, pathData) {
220  var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path');
221  for(i = 0; i < element.attributes.length; i++) {
222    attr = element.attributes[i];
vendor: 4,144 bytes, lines 223-346
223    if (this.ATTR_WATCH.indexOf(attr.name) === -1) {
224      pathTag.setAttribute(attr.name, attr.value);
225    }
226  }
227  for(i in pathData) {
228    pathTag.setAttribute(i, pathData[i]);
229  }
230  return pathTag;
231};
232
233/**
234 * Parse attributes of a DOM element to
235 * get an object of attribute => value
236 *
237 * @param  {NamedNodeMap} attributes Attributes object from DOM element to parse
238 * @return {object}                  Object of attributes
239 */
240Pathformer.prototype.parseAttr = function (element) {
241  var attr, output = {};
242  for (var i = 0; i < element.length; i++) {
243    attr = element[i];
244    // Check if no data attribute contains '%', or the transformation is impossible
245    if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) {
246      throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.');
247    }
248    output[attr.name] = attr.value;
249  }
250  return output;
251};
252
253  'use strict';
254
255var requestAnimFrame, cancelAnimFrame, parsePositiveInt;
256
257/**
258 * Vivus
259 * Beta version
260 *
261 * Take any SVG and make the animation
262 * to give give the impression of live drawing
263 *
264 * This in more than just inspired from codrops
265 * At that point, it's a pure fork.
266 */
267
268/**
269 * Class constructor
270 * option structure
271 *   type: 'delayed'|'async'|'oneByOne'|'script' (to know if the item must be drawn asynchronously or not, default: delayed)
272 *   duration: <int> (in frames)
273 *   start: 'inViewport'|'manual'|'autostart' (start automatically the animation, default: inViewport)
274 *   delay: <int> (delay between the drawing of first and last path)
275 *   dashGap <integer> whitespace extra margin between dashes
276 *   pathTimingFunction <function> timing animation function for each path element of the SVG
277 *   animTimingFunction <function> timing animation function for the complete SVG
278 *   forceRender <boolean> force the browser to re-render all updated path items
279 *   selfDestroy <boolean> removes all extra styling on the SVG, and leaves it as original
280 *
281 * The attribute 'type' is by default on 'delayed'.
282 *  - 'delayed'
283 *    all paths are draw at the same time but with a
284 *    little delay between them before start
285 *  - 'async'
286 *    all path are start and finish at the same time
287 *  - 'oneByOne'
288 *    only one path is draw at the time
289 *    the end of the first one will trigger the draw
290 *    of the next one
291 *
292 * All these values can be overwritten individually
293 * for each path item in the SVG
294 * The value of frames will always take the advantage of
295 * the duration value.
296 * If you fail somewhere, an error will be thrown.
297 * Good luck.
298 *
299 * @constructor
300 * @this {Vivus}
301 * @param {DOM|String}   element  Dom element of the SVG or id of it
302 * @param {Object}       options  Options about the animation
303 * @param {Function}     callback Callback for the end of the animation
304 */
305function Vivus (element, options, callback) {
306
307  // Setup
308  this.isReady = false;
309  this.setElement(element, options);
310  this.setOptions(options);
311  this.setCallback(callback);
312
313  if (this.isReady) {
314    this.init();
315  }
316}
317
318/**
319 * Timing functions
320 **************************************
321 *
322 * Default functions to help developers.
323 * It always take a number as parameter (between 0 to 1) then
324 * return a number (between 0 and 1)
325 */
326Vivus.LINEAR          = function (x) {return x;};
327Vivus.EASE            = function (x) {return -Math.cos(x * Math.PI) / 2 + 0.5;};
328Vivus.EASE_OUT        = function (x) {return 1 - Math.pow(1-x, 3);};
329Vivus.EASE_IN         = function (x) {return Math.pow(x, 3);};
330Vivus.EASE_OUT_BOUNCE = function (x) {
331  var base = -Math.cos(x * (0.5 * Math.PI)) + 1,
332    rate = Math.pow(base,1.5),
333    rateR = Math.pow(1 - x, 2),
334    progress = -Math.abs(Math.cos(rate * (2.5 * Math.PI) )) + 1;
335  return (1- rateR) + (progress * rateR);
336};
337
338
339/**
340 * Setters
341 **************************************
342 */
343
344/**
345 * Check and set the element in the instance
346 * The method will not return anything, but will throw an
347 * error if the parameter is invalid
348 *
349 * @param {DOM|String}   element  SVG Dom element or id of it
350 */
351Vivus.prototype.setElement = function (element, options) {
352  // Basic check
353  if (typeof element === 'undefined') {
354    throw new Error('Vivus [constructor]: "element" parameter is required');
355  }
356
357  // Set the element
358  if (element.constructor === String) {
359    element = document.getElementById(element);
360    if (!element) {
361      throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');
362    }
363  }
364  this.parentEl = element;
365
366  // Create the object element if the property `file` exists in the options object
367  if (options && options.file) {
368    var objElm = document.createElement('object');
369    objElm.setAttribute('type', 'image/svg+xml');
370    objElm.setAttribute('data', options.file);
371    objElm.setAttribute('built-by-vivus', 'true');
372    element.appendChild(objElm);
373    element = objElm;
374  }
375
376  switch (element.constructor) {
377  case window.SVGSVGElement:
378  case window.SVGElement:
379    this.el = element;
380    this.isReady = true;
381    break;
382
383  case window.HTMLObjectElement:
384    // If we have to wait for it
385    var onLoad, self;
386
387    self = this;
388    onLoad = function (e) {
389      if (self.isReady) {
390        return;
391      }
392      self.el = element.contentDocument && element.contentDocument.querySelector('svg');
393      if (!self.el && e) {
394        throw new Error('Vivus [constructor]: object loaded does not contain any SVG');
395      }
396      else if (self.el) {
397        if (element.getAttribute('built-by-vivus')) {
398          self.parentEl.insertBefore(self.el, element);
399          self.parentEl.removeChild(element);
400          self.el.setAttribute('width', '100%');
401          self.el.setAttribute('height', '100%');
402        }
403        self.isReady = true;
404        self.init();
405        return true;
406      }
407    };
408
409    if (!onLoad()) {
410      element.addEventListener('load', onLoad);
411    }
412    break;
413
414  default:
415    throw new Error('Vivus [constructor]: "element" parameter is not valid (or miss the "file" attribute)');
416  }
417};
418
419/**
420 * Set up user option to the instance
421 * The method will not return anything, but will throw an
422 * error if the parameter is invalid
423 *
424 * @param  {object} options Object from the constructor
425 */
426Vivus.prototype.setOptions = function (options) {
427  var allowedTypes = ['delayed', 'async', 'oneByOne', 'scenario', 'scenario-sync'];
428  var allowedStarts =  ['inViewport', 'manual', 'autostart'];
429
430  // Basic check
431  if (options !== undefined && options.constructor !== Object) {
432    throw new Error('Vivus [constructor]: "options" parameter must be an object');
433  }
434  else {
435    options = options || {};
436  }
437
438  // Set the animation type
439  if (options.type && allowedTypes.indexOf(options.type) === -1) {
440    throw new Error('Vivus [constructor]: ' + options.type + ' is not an existing animation `type`');
441  }
442  else {
443    this.type = options.type || allowedTypes[0];
444  }
445
446  // Set the start type
447  if (options.start && allowedStarts.indexOf(options.start) === -1) {
448    throw new Error('Vivus [constructor]: ' + options.start + ' is not an existing `start` option');
449  }
450  else {
451    this.start = options.start || allowedStarts[0];
452  }
453
454  this.isIE        = (window.navigator.userAgent.indexOf('MSIE') !== -1 || window.navigator.userAgent.indexOf('Trident/') !== -1 || window.navigator.userAgent.indexOf('Edge/') !== -1 );
455  this.duration    = parsePositiveInt(options.duration, 120);
456  this.delay       = parsePositiveInt(options.delay, null);
457  this.dashGap     = parsePositiveInt(options.dashGap, 1);
458  this.forceRender = options.hasOwnProperty('forceRender') ? !!options.forceRender : this.isIE;
459  this.selfDestroy = !!options.selfDestroy;
460  this.onReady     = options.onReady;
461  this.frameLength = this.currentFrame = this.map = this.delayUnit = this.speed = this.handle = null;
462
463  this.ignoreInvisible = options.hasOwnProperty('ignoreInvisible') ? !!options.ignoreInvisible : false;
464
465  this.animTimingFunction = options.animTimingFunction || Vivus.LINEAR;
466  this.pathTimingFunction = options.pathTimingFunction || Vivus.LINEAR;
467
468  if (this.delay >= this.duration) {
469    throw new Error('Vivus [constructor]: delay must be shorter than duration');
470  }
471};
472
473/**
474 * Set up callback to the instance
475 * The method will not return enything, but will throw an
476 * error if the parameter is invalid
477 *
478 * @param  {Function} callback Callback for the animation end
479 */
480Vivus.prototype.setCallback = function (callback) {
481  // Basic check
482  if (!!callback && callback.constructor !== Function) {
483    throw new Error('Vivus [constructor]: "callback" parameter must be a function');
484  }
485  this.callback = callback || function () {};
486};
487
488
489/**
490 * Core
491 **************************************
492 */
493
494/**
495 * Map the svg, path by path.
496 * The method return nothing, it just fill the
497 * `map` array. Each item in this array represent
498 * a path element from the SVG, with informations for
499 * the animation.
500 *
501 * ```
502 * [
503 *   {
504 *     el: <DOMobj> the path element
505 *     length: <number> length of the path line
506 *     startAt: <number> time start of the path animation (in frames)
507 *     duration: <number> path animation duration (in frames)
508 *   },
509 *   ...
510 * ]
511 * ```
512 *
513 */
514Vivus.prototype.mapping = function () {
515  var i, paths, path, pAttrs, pathObj, totalLength, lengthMeter, timePoint;
516  timePoint = totalLength = lengthMeter = 0;
517  paths = this.el.querySelectorAll('path');
518
519  for (i = 0; i < paths.length; i++) {
520    path = paths[i];
521    if (this.isInvisible(path)) {
522      continue;
523    }
524    pathObj = {
525      el: path,
526      length: Math.ceil(path.getTotalLength())
527    };
528    // Test if the path length is correct
529    if (isNaN(pathObj.length)) {
530      if (window.console && console.warn) {
531        console.warn('Vivus [mapping]: cannot retrieve a path element length', path);
532      }
533      continue;
534    }
535    this.map.push(pathObj);
536    path.style.strokeDasharray  = pathObj.length + ' ' + (pathObj.length + this.dashGap * 2);
537    path.style.strokeDashoffset = pathObj.length + this.dashGap;
538    pathObj.length += this.dashGap;
539    totalLength += pathObj.length;
540
541    this.renderPath(i);
542  }
543
544  totalLength = totalLength === 0 ? 1 : totalLength;
545  this.delay = this.delay === null ? this.duration / 3 : this.delay;
546  this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1);
547
548  for (i = 0; i < this.map.length; i++) {
549    pathObj = this.map[i];
550
551    switch (this.type) {
552    case 'delayed':
553      pathObj.startAt = this.delayUnit * i;
554      pathObj.duration = this.duration - this.delay;
555      break;
556
557    case 'oneByOne':
558      pathObj.startAt = lengthMeter / totalLength * this.duration;
559      pathObj.duration = pathObj.length / totalLength * this.duration;
560      break;
561
562    case 'async':
563      pathObj.startAt = 0;
564      pathObj.duration = this.duration;
565      break;
566
567    case 'scenario-sync':
568      path = pathObj.el;
569      pAttrs = this.parseAttr(path);
570      pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0);
571      pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
572      timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration;
573      this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
574      break;
575
576    case 'scenario':
577      path = pathObj.el;
578      pAttrs = this.parseAttr(path);
579      pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0;
580      pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
581      this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
582      break;
583    }
584    lengthMeter += pathObj.length;
585    this.frameLength = this.frameLength || this.duration;
586  }
587};
588
589/**
590 * Interval method to draw the SVG from current
591 * position of the animation. It update the value of
592 * `currentFrame` and re-trace the SVG.
593 *
594 * It use this.handle to store the requestAnimationFrame
595 * and clear it one the animation is stopped. So this
596 * attribute can be used to know if the animation is
597 * playing.
598 *
599 * Once the animation at the end, this method will
600 * trigger the Vivus callback.
601 *
602 */
603Vivus.prototype.drawer = function () {
604  var self = this;
605  this.currentFrame += this.speed;
606
607  if (this.currentFrame <= 0) {
608    this.stop();
609    this.reset();
610    this.callback(this);
611  } else if (this.currentFrame >= this.frameLength) {
612    this.stop();
613    this.currentFrame = this.frameLength;
614    this.trace();
vendor: 9,754 bytes, lines 615-990
615    if (this.selfDestroy) {
616      this.destroy();
617    }
618    this.callback(this);
619  } else {
620    this.trace();
621    this.handle = requestAnimFrame(function () {
622      self.drawer();
623    });
624  }
625};
626
627/**
628 * Draw the SVG at the current instant from the
629 * `currentFrame` value. Here is where most of the magic is.
630 * The trick is to use the `strokeDashoffset` style property.
631 *
632 * For optimisation reasons, a new property called `progress`
633 * is added in each item of `map`. This one contain the current
634 * progress of the path element. Only if the new value is different
635 * the new value will be applied to the DOM element. This
636 * method save a lot of resources to re-render the SVG. And could
637 * be improved if the animation couldn't be played forward.
638 *
639 */
640Vivus.prototype.trace = function () {
641  var i, progress, path, currentFrame;
642  currentFrame = this.animTimingFunction(this.currentFrame / this.frameLength) * this.frameLength;
643  for (i = 0; i < this.map.length; i++) {
644    path = this.map[i];
645    progress = (currentFrame - path.startAt) / path.duration;
646    progress = this.pathTimingFunction(Math.max(0, Math.min(1, progress)));
647    if (path.progress !== progress) {
648      path.progress = progress;
649      path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress));
650      this.renderPath(i);
651    }
652  }
653};
654
655/**
656 * Method forcing the browser to re-render a path element
657 * from it's index in the map. Depending on the `forceRender`
658 * value.
659 * The trick is to replace the path element by it's clone.
660 * This practice is not recommended because it's asking more
661 * ressources, too much DOM manupulation..
662 * but it's the only way to let the magic happen on IE.
663 * By default, this fallback is only applied on IE.
664 *
665 * @param  {Number} index Path index
666 */
667Vivus.prototype.renderPath = function (index) {
668  if (this.forceRender && this.map && this.map[index]) {
669    var pathObj = this.map[index],
670        newPath = pathObj.el.cloneNode(true);
671    pathObj.el.parentNode.replaceChild(newPath, pathObj.el);
672    pathObj.el = newPath;
673  }
674};
675
676/**
677 * When the SVG object is loaded and ready,
678 * this method will continue the initialisation.
679 *
680 * This this mainly due to the case of passing an
681 * object tag in the constructor. It will wait
682 * the end of the loading to initialise.
683 *
684 */
685Vivus.prototype.init = function () {
686  // Set object variables
687  this.frameLength = 0;
688  this.currentFrame = 0;
689  this.map = [];
690
691  // Start
692  new Pathformer(this.el);
693  this.mapping();
694  this.starter();
695
696  if (this.onReady) {
697    this.onReady(this);
698  }
699};
700
701/**
702 * Trigger to start of the animation.
703 * Depending on the `start` value, a different script
704 * will be applied.
705 *
706 * If the `start` value is not valid, an error will be thrown.
707 * Even if technically, this is impossible.
708 *
709 */
710Vivus.prototype.starter = function () {
711  switch (this.start) {
712  case 'manual':
713    return;
714
715  case 'autostart':
716    this.play();
717    break;
718
719  case 'inViewport':
720    var self = this,
721    listener = function () {
722      if (self.isInViewport(self.parentEl, 1)) {
723        self.play();
724        window.removeEventListener('scroll', listener);
725      }
726    };
727    window.addEventListener('scroll', listener);
728    listener();
729    break;
730  }
731};
732
733
734/**
735 * Controls
736 **************************************
737 */
738
739/**
740 * Get the current status of the animation between
741 * three different states: 'start', 'progress', 'end'.
742 * @return {string} Instance status
743 */
744Vivus.prototype.getStatus = function () {
745  return this.currentFrame === 0 ? 'start' : this.currentFrame === this.frameLength ? 'end' : 'progress';
746};
747
748/**
749 * Reset the instance to the initial state : undraw
750 * Be careful, it just reset the animation, if you're
751 * playing the animation, this won't stop it. But just
752 * make it start from start.
753 *
754 */
755Vivus.prototype.reset = function () {
756  return this.setFrameProgress(0);
757};
758
759/**
760 * Set the instance to the final state : drawn
761 * Be careful, it just set the animation, if you're
762 * playing the animation on rewind, this won't stop it.
763 * But just make it start from the end.
764 *
765 */
766Vivus.prototype.finish = function () {
767  return this.setFrameProgress(1);
768};
769
770/**
771 * Set the level of progress of the drawing.
772 *
773 * @param {number} progress Level of progress to set
774 */
775Vivus.prototype.setFrameProgress = function (progress) {
776  progress = Math.min(1, Math.max(0, progress));
777  this.currentFrame = Math.round(this.frameLength * progress);
778  this.trace();
779  return this;
780};
781
782/**
783 * Play the animation at the desired speed.
784 * Speed must be a valid number (no zero).
785 * By default, the speed value is 1.
786 * But a negative value is accepted to go forward.
787 *
788 * And works with float too.
789 * But don't forget we are in JavaScript, se be nice
790 * with him and give him a 1/2^x value.
791 *
792 * @param  {number} speed Animation speed [optional]
793 */
794Vivus.prototype.play = function (speed) {
795  if (speed && typeof speed !== 'number') {
796    throw new Error('Vivus [play]: invalid speed');
797  }
798  this.speed = speed || 1;
799  if (!this.handle) {
800    this.drawer();
801  }
802  return this;
803};
804
805/**
806 * Stop the current animation, if on progress.
807 * Should not trigger any error.
808 *
809 */
810Vivus.prototype.stop = function () {
811  if (this.handle) {
812    cancelAnimFrame(this.handle);
813    this.handle = null;
814  }
815  return this;
816};
817
818/**
819 * Destroy the instance.
820 * Remove all bad styling attributes on all
821 * path tags
822 *
823 */
824Vivus.prototype.destroy = function () {
825  this.stop();
826  var i, path;
827  for (i = 0; i < this.map.length; i++) {
828    path = this.map[i];
829    path.el.style.strokeDashoffset = null;
830    path.el.style.strokeDasharray = null;
831    this.renderPath(i);
832  }
833};
834
835
836/**
837 * Utils methods
838 * include methods from Codrops
839 **************************************
840 */
841
842/**
843 * Method to best guess if a path should added into
844 * the animation or not.
845 *
846 * 1. Use the `data-vivus-ignore` attribute if set
847 * 2. Check if the instance must ignore invisible paths
848 * 3. Check if the path is visible
849 *
850 * For now the visibility checking is unstable.
851 * It will be used for a beta phase.
852 *
853 * Other improvments are planned. Like detecting
854 * is the path got a stroke or a valid opacity.
855 */
856Vivus.prototype.isInvisible = function (el) {
857  var rect,
858    ignoreAttr = el.getAttribute('data-ignore');
859
860  if (ignoreAttr !== null) {
861    return ignoreAttr !== 'false';
862  }
863
864  if (this.ignoreInvisible) {
865    rect = el.getBoundingClientRect();
866    return !rect.width && !rect.height;
867  }
868  else {
869    return false;
870  }
871};
872
873/**
874 * Parse attributes of a DOM element to
875 * get an object of {attributeName => attributeValue}
876 *
877 * @param  {object} element DOM element to parse
878 * @return {object}         Object of attributes
879 */
880Vivus.prototype.parseAttr = function (element) {
881  var attr, output = {};
882  if (element && element.attributes) {
883    for (var i = 0; i < element.attributes.length; i++) {
884      attr = element.attributes[i];
885      output[attr.name] = attr.value;
886    }
887  }
888  return output;
889};
890
891/**
892 * Reply if an element is in the page viewport
893 *
894 * @param  {object} el Element to observe
895 * @param  {number} h  Percentage of height
896 * @return {boolean}
897 */
898Vivus.prototype.isInViewport = function (el, h) {
899  var scrolled   = this.scrollY(),
900    viewed       = scrolled + this.getViewportH(),
901    elBCR        = el.getBoundingClientRect(),
902    elHeight     = elBCR.height,
903    elTop        = scrolled + elBCR.top,
904    elBottom     = elTop + elHeight;
905
906  // if 0, the element is considered in the viewport as soon as it enters.
907  // if 1, the element is considered in the viewport only when it's fully inside
908  // value in percentage (1 >= h >= 0)
909  h = h || 0;
910
911  return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled;
912};
913
914/**
915 * Alias for document element
916 *
917 * @type {DOMelement}
918 */
919Vivus.prototype.docElem = window.document.documentElement;
920
921/**
922 * Get the viewport height in pixels
923 *
924 * @return {integer} Viewport height
925 */
926Vivus.prototype.getViewportH = function () {
927  var client = this.docElem.clientHeight,
928    inner = window.innerHeight;
929
930  if (client < inner) {
931    return inner;
932  }
933  else {
934    return client;
935  }
936};
937
938/**
939 * Get the page Y offset
940 *
941 * @return {integer} Page Y offset
942 */
943Vivus.prototype.scrollY = function () {
944  return window.pageYOffset || this.docElem.scrollTop;
945};
946
947/**
948 * Alias for `requestAnimationFrame` or
949 * `setTimeout` function for deprecated browsers.
950 *
951 */
952requestAnimFrame = (function () {
953  return (
954    window.requestAnimationFrame       ||
955    window.webkitRequestAnimationFrame ||
956    window.mozRequestAnimationFrame    ||
957    window.oRequestAnimationFrame      ||
958    window.msRequestAnimationFrame     ||
959    function(/* function */ callback){
960      return window.setTimeout(callback, 1000 / 60);
961    }
962  );
963})();
964
965/**
966 * Alias for `cancelAnimationFrame` or
967 * `cancelTimeout` function for deprecated browsers.
968 *
969 */
970cancelAnimFrame = (function () {
971  return (
972    window.cancelAnimationFrame       ||
973    window.webkitCancelAnimationFrame ||
974    window.mozCancelAnimationFrame    ||
975    window.oCancelAnimationFrame      ||
976    window.msCancelAnimationFrame     ||
977    function(id){
978      return window.clearTimeout(id);
979    }
980  );
981})();
982
983/**
984 * Parse string to integer.
985 * If the number is not positive or null
986 * the method will return the default value
987 * or 0 if undefined
988 *
989 * @param {string} value String to parse
990 * @param {*}
990 defaultValue Value to return if the result parsed is invalid
991 * @return {number}
992 *
993 */
994parsePositiveInt = function (value, defaultValue) {
995  var output = parseInt(value, 10);
996  return (output >= 0) ? output : defaultValue;
997};
998
999
1000  if (typeof define === 'function' && define.amd) {
1001    // AMD. Register as an anonymous module.
1002    define([], function() {
1003      return Vivus;
1004    });
1005  } else if (typeof exports === 'object') {
1006    // Node. Does not work with strict CommonJS, but
1007    // only CommonJS-like environments that support module.exports,
1008    // like Node.
1009    module.exports = Vivus;
1010  } else {
1011    // Browser globals
1012    window.Vivus = Vivus;
1013  }
1014
1015}(window, document));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.