PageSourceSearch

https://www.nju.edu.cn/en/js/v.js

js nju.edu.cn collected 2026-09-24 08:35:02 UTC 29,986 bytes, 995 lines download raw bytes

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