rails_vivus 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0db1a41c628b5f9f467ded76d16367f76ae21496
4
+ data.tar.gz: 564eb04db8fb8b0cabca4a670c1aafc2ab79edf9
5
+ SHA512:
6
+ metadata.gz: cbb38833cfbde467dad7f516e1b8f4ac5fbacd1f65283e9abc62febb151dba9bd81baf1dcbb2f9fa224098e1b179308713c66a62a9f4b2138d2348fc94a49584
7
+ data.tar.gz: ed3fb8d550270daeb2a4d21082915f7f0a84f04fe84989aa0888776df2eadd5c4c906787562b62bd938b679e01c08f900962f7eedebe67e9dabc772caad8c1cd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Jack A. Huang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = RailsVivus
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsVivus'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,825 @@
1
+ /**
2
+ * vivus - JavaScript library to make drawing animation on SVG
3
+ * @version v0.1.2
4
+ * @link https://github.com/maxwellito/vivus
5
+ * @license MIT
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ (function () {
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
+ */
30
+ function 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 === SVGSVGElement) {
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
+ */
59
+ Pathformer.prototype.TYPES = ['line', 'elipse', '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
+ */
68
+ Pathformer.prototype.ATTR_WATCH = ['cx', 'cy', 'points', 'r', 'rx', 'ry', 'x', 'x1', 'x2', 'y', 'y1', '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
+ */
76
+ Pathformer.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
+ */
96
+ Pathformer.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
+ */
111
+ Pathformer.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
+ */
131
+ Pathformer.prototype.polylineToPath = function (element) {
132
+ var newElement = {};
133
+ var points = element.points.split(' ');
134
+ var path = 'M' + points[0];
135
+ for(var i = 1; i < points.length; i++) {
136
+ if (points[i].indexOf(',') !== -1) {
137
+ path += 'L'+points[i];
138
+ }
139
+ }
140
+ newElement.d = path;
141
+ return newElement;
142
+ };
143
+
144
+ /**
145
+ * Read `polygon` element to extract and transform
146
+ * data, to make it ready for a `path` object.
147
+ * This method rely on polylineToPath, because the
148
+ * logic is similar. THe path created is just closed,
149
+ * so it needs an 'Z' at the end.
150
+ *
151
+ * @param {DOMelement} element Polygon element to transform
152
+ * @return {object} Data for a `path` element
153
+ */
154
+ Pathformer.prototype.polygonToPath = function (element) {
155
+ var newElement = Pathformer.prototype.polylineToPath(element);
156
+ newElement.d += 'Z';
157
+ return newElement;
158
+ };
159
+
160
+ /**
161
+ * Read `elipse` element to extract and transform
162
+ * data, to make it ready for a `path` object.
163
+ *
164
+ * @param {DOMelement} element Elipse element to transform
165
+ * @return {object} Data for a `path` element
166
+ */
167
+ Pathformer.prototype.elipseToPath = function (element) {
168
+ var startX = element.cx - element.rx,
169
+ startY = element.cy;
170
+ var endX = parseFloat(element.cx) + parseFloat(element.rx),
171
+ endY = element.cy;
172
+
173
+ var newElement = {};
174
+ newElement.d = 'M' + startX + ',' + startY +
175
+ 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + endX + ',' + endY +
176
+ 'A' + element.rx + ',' + element.ry + ' 0,1,1 ' + startX + ',' + endY;
177
+ return newElement;
178
+ };
179
+
180
+ /**
181
+ * Read `circle` element to extract and transform
182
+ * data, to make it ready for a `path` object.
183
+ *
184
+ * @param {DOMelement} element Circle element to transform
185
+ * @return {object} Data for a `path` element
186
+ */
187
+ Pathformer.prototype.circleToPath = function (element) {
188
+ var newElement = {};
189
+ var startX = element.cx - element.r,
190
+ startY = element.cy;
191
+ var endX = parseFloat(element.cx) + parseFloat(element.r),
192
+ endY = element.cy;
193
+ newElement.d = 'M' + startX + ',' + startY +
194
+ 'A' + element.r + ',' + element.r + ' 0,1,1 ' + endX + ',' + endY +
195
+ 'A' + element.r + ',' + element.r + ' 0,1,1 ' + startX + ',' + endY;
196
+ return newElement;
197
+ };
198
+
199
+ /**
200
+ * Create `path` elements form original element
201
+ * and prepared objects
202
+ *
203
+ * @param {DOMelement} element Original element to transform
204
+ * @param {object} pathData Path data (from `toPath` methods)
205
+ * @return {DOMelement} Path element
206
+ */
207
+ Pathformer.prototype.pathMaker = function (element, pathData) {
208
+ var i, attr, pathTag = document.createElementNS('http://www.w3.org/2000/svg','path');
209
+ for(i = 0; i < element.attributes.length; i++) {
210
+ attr = element.attributes[i];
211
+ if (this.ATTR_WATCH.indexOf(attr.name) === -1) {
212
+ pathTag.setAttribute(attr.name, attr.value);
213
+ }
214
+ }
215
+ for(i in pathData) {
216
+ pathTag.setAttribute(i, pathData[i]);
217
+ }
218
+ return pathTag;
219
+ };
220
+
221
+ /**
222
+ * Parse attributes of a DOM element to
223
+ * get an object of attribute => value
224
+ *
225
+ * @param {NamedNodeMap} attributes Attributes object from DOM element to parse
226
+ * @return {object} Object of attributes
227
+ */
228
+ Pathformer.prototype.parseAttr = function (element) {
229
+ var attr, output = {};
230
+ for (var i = 0; i < element.length; i++) {
231
+ attr = element[i];
232
+ // Check if no data attribute contains '%', or the transformation is impossible
233
+ if (this.ATTR_WATCH.indexOf(attr.name) !== -1 && attr.value.indexOf('%') !== -1) {
234
+ throw new Error('Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into \'path\' tags. Please use \'viewBox\'.');
235
+ }
236
+ output[attr.name] = attr.value;
237
+ }
238
+ return output;
239
+ };
240
+
241
+ 'use strict';
242
+
243
+ var requestAnimFrame, cancelAnimFrame, parsePositiveInt;
244
+
245
+ /**
246
+ * Vivus
247
+ * Beta version
248
+ *
249
+ * Take any SVG and make the animation
250
+ * to give give the impression of live drawing
251
+ *
252
+ * This in more than just inspired from codrops
253
+ * At that point, it's a pure fork.
254
+ */
255
+
256
+ /**
257
+ * Class constructor
258
+ * option structure
259
+ * type: 'delayed'|'async'|'oneByOne'|'script' (to know if the item must be drawn asynchronously or not, default: delayed)
260
+ * duration: <int> (in frames)
261
+ * start: 'inViewport'|'manual'|'autostart' (start automatically the animation, default: inViewport)
262
+ * delay: <int> (delay between the drawing of first and last path)
263
+ *
264
+ * The attribute 'type' is by default on 'delayed'.
265
+ * - 'delayed'
266
+ * all paths are draw at the same time but with a
267
+ * little delay between them before start
268
+ * - 'async'
269
+ * all path are start and finish at the same time
270
+ * - 'oneByOne'
271
+ * only one path is draw at the time
272
+ * the end of the first one will trigger the draw
273
+ * of the next one
274
+ *
275
+ * All these values can be overwritten individually
276
+ * for each path item in the SVG
277
+ * The value of frames will always take the advantage of
278
+ * the duration value.
279
+ * If you fail somewhere, an error will be thrown.
280
+ * Good luck.
281
+ *
282
+ * @constructor
283
+ * @this {Vivus}
284
+ * @param {DOM|String} element Dom element of the SVG or id of it
285
+ * @param {Object} options Options about the animation
286
+ * @param {Function} callback Callback for the end of the animation
287
+ */
288
+ function Vivus (element, options, callback) {
289
+
290
+ // Setup
291
+ this.setElement(element);
292
+ this.setOptions(options);
293
+ this.setCallback(callback);
294
+
295
+ // Set object variables
296
+ this.frameLength = 0;
297
+ this.currentFrame = 0;
298
+ this.map = [];
299
+
300
+ // Start
301
+ new Pathformer(element);
302
+ this.mapping();
303
+ this.starter();
304
+ }
305
+
306
+
307
+ /**
308
+ * Setters
309
+ **************************************
310
+ */
311
+
312
+ /**
313
+ * Check and set the element in the instance
314
+ * The method will not return anything, but will throw an
315
+ * error if the parameter is invalid
316
+ *
317
+ * @param {DOM|String} element SVG Dom element or id of it
318
+ */
319
+ Vivus.prototype.setElement = function (element) {
320
+ // Basic check
321
+ if (typeof element === 'undefined') {
322
+ throw new Error('Vivus [constructor]: "element" parameter is required');
323
+ }
324
+
325
+ // Set the element
326
+ if (element.constructor === String) {
327
+ element = document.getElementById(element);
328
+ if (!element) {
329
+ throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');
330
+ }
331
+ }
332
+ if (element.constructor === SVGSVGElement) {
333
+ this.el = element;
334
+ } else {
335
+ throw new Error('Vivus [constructor]: "element" parameter must be a string or a SVGelement');
336
+ }
337
+ };
338
+
339
+ /**
340
+ * Set up user option to the instance
341
+ * The method will not return anything, but will throw an
342
+ * error if the parameter is invalid
343
+ *
344
+ * @param {object} options Object from the constructor
345
+ */
346
+ Vivus.prototype.setOptions = function (options) {
347
+ var allowedTypes = ['delayed', 'async', 'oneByOne', 'scenario', 'scenario-sync'];
348
+ var allowedStarts = ['inViewport', 'manual', 'autostart'];
349
+
350
+ // Basic check
351
+ if (options !== undefined && options.constructor !== Object) {
352
+ throw new Error('Vivus [constructor]: "options" parameter must be an object');
353
+ }
354
+ else {
355
+ options = options || {};
356
+ }
357
+
358
+ // Set the animation type
359
+ if (options.type && allowedTypes.indexOf(options.type) === -1) {
360
+ throw new Error('Vivus [constructor]: ' + options.type + ' is not an existing animation `type`');
361
+ }
362
+ else {
363
+ this.type = options.type || allowedTypes[0];
364
+ }
365
+
366
+ // Set the start type
367
+ if (options.start && allowedStarts.indexOf(options.start) === -1) {
368
+ throw new Error('Vivus [constructor]: ' + options.start + ' is not an existing `start` option');
369
+ }
370
+ else {
371
+ this.start = options.start || allowedStarts[0];
372
+ }
373
+
374
+ this.isIE = (navigator.userAgent.indexOf('MSIE') !== -1);
375
+ this.duration = parsePositiveInt(options.duration, 120);
376
+ this.delay = parsePositiveInt(options.delay, null);
377
+ this.dashGap = parsePositiveInt(options.dashGap, 2);
378
+ this.forceRender = options.hasOwnProperty('forceRender') ? !!options.forceRender : this.isIE;
379
+ this.selfDestroy = !!options.selfDestroy;
380
+
381
+ if (this.delay >= this.duration) {
382
+ throw new Error('Vivus [constructor]: delay must be shorter than duration');
383
+ }
384
+ };
385
+
386
+ /**
387
+ * Set up callback to the instance
388
+ * The method will not return enything, but will throw an
389
+ * error if the parameter is invalid
390
+ *
391
+ * @param {Function} callback Callback for the animation end
392
+ */
393
+ Vivus.prototype.setCallback = function (callback) {
394
+ // Basic check
395
+ if (!!callback && callback.constructor !== Function) {
396
+ throw new Error('Vivus [constructor]: "callback" parameter must be a function');
397
+ }
398
+ this.callback = callback || function () {};
399
+ };
400
+
401
+
402
+ /**
403
+ * Core
404
+ **************************************
405
+ */
406
+
407
+ /**
408
+ * Map the svg, path by path.
409
+ * The method return nothing, it just fill the
410
+ * `map` array. Each item in this array represent
411
+ * a path element from the SVG, with informations for
412
+ * the animation.
413
+ *
414
+ * ```
415
+ * [
416
+ * {
417
+ * el: <DOMobj> the path element
418
+ * length: <number> length of the path line
419
+ * startAt: <number> time start of the path animation (in frames)
420
+ * duration: <number> path animation duration (in frames)
421
+ * },
422
+ * ...
423
+ * ]
424
+ * ```
425
+ *
426
+ */
427
+ Vivus.prototype.mapping = function () {
428
+ var i, paths, path, pAttrs, pathObj, totalLength, lengthMeter, timePoint;
429
+ timePoint = totalLength = lengthMeter = 0;
430
+ paths = this.el.querySelectorAll('path');
431
+
432
+ for (i = 0; i < paths.length; i++) {
433
+ path = paths[i];
434
+ pathObj = {
435
+ el: path,
436
+ length: Math.ceil(path.getTotalLength())
437
+ };
438
+ // Test if the path length is correct
439
+ if (isNaN(pathObj.length)) {
440
+ if (window.console && console.warn) {
441
+ console.warn('Vivus [mapping]: cannot retrieve a path element length', path);
442
+ }
443
+ continue;
444
+ }
445
+ totalLength += pathObj.length;
446
+ this.map.push(pathObj);
447
+ path.style.strokeDasharray = pathObj.length + ' ' + (pathObj.length + this.dashGap);
448
+ path.style.strokeDashoffset = pathObj.length;
449
+
450
+ // Fix IE glitch
451
+ if (this.isIE) {
452
+ pathObj.length += this.dashGap;
453
+ }
454
+ this.renderPath(i);
455
+ }
456
+
457
+ totalLength = totalLength === 0 ? 1 : totalLength;
458
+ this.delay = this.delay === null ? this.duration / 3 : this.delay;
459
+ this.delayUnit = this.delay / (paths.length > 1 ? paths.length - 1 : 1);
460
+
461
+ for (i = 0; i < this.map.length; i++) {
462
+ pathObj = this.map[i];
463
+
464
+ switch (this.type) {
465
+ case 'delayed':
466
+ pathObj.startAt = this.delayUnit * i;
467
+ pathObj.duration = this.duration - this.delay;
468
+ break;
469
+
470
+ case 'oneByOne':
471
+ pathObj.startAt = lengthMeter / totalLength * this.duration;
472
+ pathObj.duration = pathObj.length / totalLength * this.duration;
473
+ break;
474
+
475
+ case 'async':
476
+ pathObj.startAt = 0;
477
+ pathObj.duration = this.duration;
478
+ break;
479
+
480
+ case 'scenario-sync':
481
+ path = paths[i];
482
+ pAttrs = this.parseAttr(path);
483
+ pathObj.startAt = timePoint + (parsePositiveInt(pAttrs['data-delay'], this.delayUnit) || 0);
484
+ pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
485
+ timePoint = pAttrs['data-async'] !== undefined ? pathObj.startAt : pathObj.startAt + pathObj.duration;
486
+ this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
487
+ break;
488
+
489
+ case 'scenario':
490
+ path = paths[i];
491
+ pAttrs = this.parseAttr(path);
492
+ pathObj.startAt = parsePositiveInt(pAttrs['data-start'], this.delayUnit) || 0;
493
+ pathObj.duration = parsePositiveInt(pAttrs['data-duration'], this.duration);
494
+ this.frameLength = Math.max(this.frameLength, (pathObj.startAt + pathObj.duration));
495
+ break;
496
+ }
497
+ lengthMeter += pathObj.length;
498
+ this.frameLength = this.frameLength || this.duration;
499
+ }
500
+ };
501
+
502
+ /**
503
+ * Interval method to draw the SVG from current
504
+ * position of the animation. It update the value of
505
+ * `currentFrame` and re-trace the SVG.
506
+ *
507
+ * It use this.handle to store the requestAnimationFrame
508
+ * and clear it one the animation is stopped. So this
509
+ * attribute can be used to know if the animation is
510
+ * playing.
511
+ *
512
+ * Once the animation at the end, this method will
513
+ * trigger the Vivus callback.
514
+ *
515
+ */
516
+ Vivus.prototype.drawer = function () {
517
+ var self = this;
518
+ this.currentFrame += this.speed;
519
+
520
+ if (this.currentFrame <= 0) {
521
+ this.stop();
522
+ this.reset();
523
+ } else if (this.currentFrame >= this.frameLength) {
524
+ this.stop();
525
+ this.currentFrame = this.frameLength;
526
+ this.trace();
527
+ if (this.selfDestroy) {
528
+ this.destroy();
529
+ }
530
+ this.callback(this);
531
+ } else {
532
+ this.trace();
533
+ this.handle = requestAnimFrame(function () {
534
+ self.drawer();
535
+ });
536
+ }
537
+ };
538
+
539
+ /**
540
+ * Draw the SVG at the current instant from the
541
+ * `currentFrame` value. Here is where most of the magic is.
542
+ * The trick is to use the `strokeDashoffset` style property.
543
+ *
544
+ * For optimisation reasons, a new property called `progress`
545
+ * is added in each item of `map`. This one contain the current
546
+ * progress of the path element. Only if the new value is different
547
+ * the new value will be applied to the DOM element. This
548
+ * method save a lot of resources to re-render the SVG. And could
549
+ * be improved if the animation couldn't be played forward.
550
+ *
551
+ */
552
+ Vivus.prototype.trace = function () {
553
+ var i, progress, path;
554
+ for (i = 0; i < this.map.length; i++) {
555
+ path = this.map[i];
556
+ progress = (this.currentFrame - path.startAt) / path.duration;
557
+ progress = Math.max(0, Math.min(1, progress));
558
+ if (path.progress !== progress) {
559
+ path.progress = progress;
560
+ path.el.style.strokeDashoffset = Math.floor(path.length * (1 - progress));
561
+ this.renderPath(i);
562
+ }
563
+ }
564
+ };
565
+
566
+ /**
567
+ * Method forcing the browser to re-render a path element
568
+ * from it's index in the map. Depending on the `forceRender`
569
+ * value.
570
+ * The trick is to replace the path element by it's clone.
571
+ * This practice is not recommended because it's asking more
572
+ * ressources, too much DOM manupulation..
573
+ * but it's the only way to let the magic happen on IE.
574
+ * By default, this fallback is only applied on IE.
575
+ *
576
+ * @param {Number} index Path index
577
+ */
578
+ Vivus.prototype.renderPath = function (index) {
579
+ if (this.forceRender && this.map && this.map[index]) {
580
+ var pathObj = this.map[index],
581
+ newPath = pathObj.el.cloneNode(true);
582
+ pathObj.el.parentNode.replaceChild(newPath, pathObj.el);
583
+ pathObj.el = newPath;
584
+ }
585
+ };
586
+
587
+ /**
588
+ * Trigger to start of the animation.
589
+ * Depending on the `start` value, a different script
590
+ * will be applied.
591
+ *
592
+ * If the `start` value is not valid, an error will be thrown.
593
+ * Even if technically, this is impossible.
594
+ *
595
+ */
596
+ Vivus.prototype.starter = function () {
597
+ switch (this.start) {
598
+ case 'manual':
599
+ return;
600
+
601
+ case 'autostart':
602
+ this.play();
603
+ break;
604
+
605
+ case 'inViewport':
606
+ var self = this,
607
+ listener = function () {
608
+ if (self.isInViewport(self.el, 1)) {
609
+ self.play();
610
+ window.removeEventListener('scroll', listener);
611
+ }
612
+ };
613
+ window.addEventListener('scroll', listener);
614
+ listener();
615
+ break;
616
+ }
617
+ };
618
+
619
+
620
+ /**
621
+ * Controls
622
+ **************************************
623
+ */
624
+
625
+ /**
626
+ * Reset the instance to the initial state : undraw
627
+ * Be careful, it just reset the animation, if you're
628
+ * playing the animation, this won't stop it. But just
629
+ * make it start from start.
630
+ *
631
+ */
632
+ Vivus.prototype.reset = function () {
633
+ this.currentFrame = 0;
634
+ this.trace();
635
+ return this;
636
+ };
637
+
638
+ /**
639
+ * Play the animation at the desired speed.
640
+ * Speed must be a valid number (no zero).
641
+ * By default, the speed value is 1.
642
+ * But a negative value is accepted to go forward.
643
+ *
644
+ * And works with float too.
645
+ * But don't forget we are in JavaScript, se be nice
646
+ * with him and give him a 1/2^x value.
647
+ *
648
+ * @param {number} speed Animation speed [optional]
649
+ */
650
+ Vivus.prototype.play = function (speed) {
651
+ if (speed && typeof speed !== 'number') {
652
+ throw new Error('Vivus [play]: invalid speed');
653
+ }
654
+ this.speed = speed || 1;
655
+ if (!this.handle) {
656
+ this.drawer();
657
+ }
658
+ return this;
659
+ };
660
+
661
+ /**
662
+ * Stop the current animation, if on progress.
663
+ * Should not trigger any error.
664
+ *
665
+ */
666
+ Vivus.prototype.stop = function () {
667
+ if (this.handle) {
668
+ cancelAnimFrame(this.handle);
669
+ delete this.handle;
670
+ }
671
+ return this;
672
+ };
673
+
674
+ /**
675
+ * Destroy the instance.
676
+ * Remove all bad styling attributes on all
677
+ * path tags
678
+ *
679
+ */
680
+ Vivus.prototype.destroy = function () {
681
+ var i, path;
682
+ for (i = 0; i < this.map.length; i++) {
683
+ path = this.map[i];
684
+ path.el.style.strokeDashoffset = null;
685
+ path.el.style.strokeDasharray = null;
686
+ this.renderPath(i);
687
+ }
688
+ };
689
+
690
+
691
+ /**
692
+ * Utils methods
693
+ * from Codrops
694
+ **************************************
695
+ */
696
+
697
+ /**
698
+ * Parse attributes of a DOM element to
699
+ * get an object of {attributeName => attributeValue}
700
+ *
701
+ * @param {object} element DOM element to parse
702
+ * @return {object} Object of attributes
703
+ */
704
+ Vivus.prototype.parseAttr = function (element) {
705
+ var attr, output = {};
706
+ if (element && element.attributes) {
707
+ for (var i = 0; i < element.attributes.length; i++) {
708
+ attr = element.attributes[i];
709
+ output[attr.name] = attr.value;
710
+ }
711
+ }
712
+ return output;
713
+ };
714
+
715
+ /**
716
+ * Reply if an element is in the page viewport
717
+ *
718
+ * @param {object} el Element to observe
719
+ * @param {number} h Percentage of height
720
+ * @return {boolean}
721
+ */
722
+ Vivus.prototype.isInViewport = function (el, h) {
723
+ var scrolled = this.scrollY(),
724
+ viewed = scrolled + this.getViewportH(),
725
+ elBCR = el.getBoundingClientRect(),
726
+ elHeight = elBCR.height,
727
+ elTop = scrolled + elBCR.top,
728
+ elBottom = elTop + elHeight;
729
+
730
+ // if 0, the element is considered in the viewport as soon as it enters.
731
+ // if 1, the element is considered in the viewport only when it's fully inside
732
+ // value in percentage (1 >= h >= 0)
733
+ h = h || 0;
734
+
735
+ return (elTop + elHeight * h) <= viewed && (elBottom) >= scrolled;
736
+ };
737
+
738
+ /**
739
+ * Alias for document element
740
+ *
741
+ * @type {DOMelement}
742
+ */
743
+ Vivus.prototype.docElem = window.document.documentElement;
744
+
745
+ /**
746
+ * Get the viewport height in pixels
747
+ *
748
+ * @return {integer} Viewport height
749
+ */
750
+ Vivus.prototype.getViewportH = function () {
751
+ var client = this.docElem.clientHeight,
752
+ inner = window.innerHeight;
753
+
754
+ if (client < inner) {
755
+ return inner;
756
+ }
757
+ else {
758
+ return client;
759
+ }
760
+ };
761
+
762
+ /**
763
+ * Get the page Y offset
764
+ *
765
+ * @return {integer} Page Y offset
766
+ */
767
+ Vivus.prototype.scrollY = function () {
768
+ return window.pageYOffset || this.docElem.scrollTop;
769
+ };
770
+
771
+ /**
772
+ * Alias for `requestAnimationFrame` or
773
+ * `setTimeout` function for deprecated browsers.
774
+ *
775
+ */
776
+ requestAnimFrame = (function () {
777
+ return (
778
+ window.requestAnimationFrame ||
779
+ window.webkitRequestAnimationFrame ||
780
+ window.mozRequestAnimationFrame ||
781
+ window.oRequestAnimationFrame ||
782
+ window.msRequestAnimationFrame ||
783
+ function(/* function */ callback){
784
+ return window.setTimeout(callback, 1000 / 60);
785
+ }
786
+ );
787
+ })();
788
+
789
+ /**
790
+ * Alias for `cancelAnimationFrame` or
791
+ * `cancelTimeout` function for deprecated browsers.
792
+ *
793
+ */
794
+ cancelAnimFrame = (function () {
795
+ return (
796
+ window.cancelAnimationFrame ||
797
+ window.webkitCancelAnimationFrame ||
798
+ window.mozCancelAnimationFrame ||
799
+ window.oCancelAnimationFrame ||
800
+ window.msCancelAnimationFrame ||
801
+ function(id){
802
+ return window.clearTimeout(id);
803
+ }
804
+ );
805
+ })();
806
+
807
+ /**
808
+ * Parse string to integer.
809
+ * If the number is not positive or null
810
+ * the method will return the default value
811
+ * or 0 if undefined
812
+ *
813
+ * @param {string} value String to parse
814
+ * @param {*} defaultValue Value to return if the result parsed is invalid
815
+ * @return {number}
816
+ *
817
+ */
818
+ parsePositiveInt = function (value, defaultValue) {
819
+ var output = parseInt(value, 10);
820
+ return (output >= 0) ? output : defaultValue;
821
+ };
822
+
823
+
824
+ window.Vivus = Vivus;
825
+ }());
@@ -0,0 +1,7 @@
1
+ /**
2
+ * vivus - JavaScript library to make drawing animation on SVG
3
+ * @version v0.1.2
4
+ * @link https://github.com/maxwellito/vivus
5
+ * @license MIT
6
+ */
7
+ "use strict";!function(){function t(t){if("undefined"==typeof t)throw new Error('Pathformer [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Pathformer [constructor]: "element" parameter is not related to an existing ID');if(t.constructor!==SVGSVGElement)throw new Error('Pathformer [constructor]: "element" parameter must be a string or a SVGelement');this.el=t,this.scan(t)}function e(e,r,n){this.setElement(e),this.setOptions(r),this.setCallback(n),this.frameLength=0,this.currentFrame=0,this.map=[],new t(e),this.mapping(),this.starter()}t.prototype.TYPES=["line","elipse","circle","polygon","polyline","rect"],t.prototype.ATTR_WATCH=["cx","cy","points","r","rx","ry","x","x1","x2","y","y1","y2"],t.prototype.scan=function(t){for(var e,r,n,o,a=t.querySelectorAll(this.TYPES.join(",")),i=0;i<a.length;i++)r=a[i],e=this[r.tagName.toLowerCase()+"ToPath"],n=e(this.parseAttr(r.attributes)),o=this.pathMaker(r,n),r.parentNode.replaceChild(o,r)},t.prototype.lineToPath=function(t){var e={};return e.d="M"+t.x1+","+t.y1+"L"+t.x2+","+t.y2,e},t.prototype.rectToPath=function(t){var e={},r=parseFloat(t.x)||0,n=parseFloat(t.y)||0,o=parseFloat(t.width)||0,a=parseFloat(t.height)||0;return e.d="M"+r+" "+n+" ",e.d+="L"+(r+o)+" "+n+" ",e.d+="L"+(r+o)+" "+(n+a)+" ",e.d+="L"+r+" "+(n+a)+" Z",e},t.prototype.polylineToPath=function(t){for(var e={},r=t.points.split(" "),n="M"+r[0],o=1;o<r.length;o++)-1!==r[o].indexOf(",")&&(n+="L"+r[o]);return e.d=n,e},t.prototype.polygonToPath=function(e){var r=t.prototype.polylineToPath(e);return r.d+="Z",r},t.prototype.elipseToPath=function(t){var e=t.cx-t.rx,r=t.cy,n=parseFloat(t.cx)+parseFloat(t.rx),o=t.cy,a={};return a.d="M"+e+","+r+"A"+t.rx+","+t.ry+" 0,1,1 "+n+","+o+"A"+t.rx+","+t.ry+" 0,1,1 "+e+","+o,a},t.prototype.circleToPath=function(t){var e={},r=t.cx-t.r,n=t.cy,o=parseFloat(t.cx)+parseFloat(t.r),a=t.cy;return e.d="M"+r+","+n+"A"+t.r+","+t.r+" 0,1,1 "+o+","+a+"A"+t.r+","+t.r+" 0,1,1 "+r+","+a,e},t.prototype.pathMaker=function(t,e){var r,n,o=document.createElementNS("http://www.w3.org/2000/svg","path");for(r=0;r<t.attributes.length;r++)n=t.attributes[r],-1===this.ATTR_WATCH.indexOf(n.name)&&o.setAttribute(n.name,n.value);for(r in e)o.setAttribute(r,e[r]);return o},t.prototype.parseAttr=function(t){for(var e,r={},n=0;n<t.length;n++){if(e=t[n],-1!==this.ATTR_WATCH.indexOf(e.name)&&-1!==e.value.indexOf("%"))throw new Error("Pathformer [parseAttr]: a SVG shape got values in percentage. This cannot be transformed into 'path' tags. Please use 'viewBox'.");r[e.name]=e.value}return r};var r,n,o;e.prototype.setElement=function(t){if("undefined"==typeof t)throw new Error('Vivus [constructor]: "element" parameter is required');if(t.constructor===String&&(t=document.getElementById(t),!t))throw new Error('Vivus [constructor]: "element" parameter is not related to an existing ID');if(t.constructor!==SVGSVGElement)throw new Error('Vivus [constructor]: "element" parameter must be a string or a SVGelement');this.el=t},e.prototype.setOptions=function(t){var e=["delayed","async","oneByOne","scenario","scenario-sync"],r=["inViewport","manual","autostart"];if(void 0!==t&&t.constructor!==Object)throw new Error('Vivus [constructor]: "options" parameter must be an object');if(t=t||{},t.type&&-1===e.indexOf(t.type))throw new Error("Vivus [constructor]: "+t.type+" is not an existing animation `type`");if(this.type=t.type||e[0],t.start&&-1===r.indexOf(t.start))throw new Error("Vivus [constructor]: "+t.start+" is not an existing `start` option");if(this.start=t.start||r[0],this.isIE=-1!==navigator.userAgent.indexOf("MSIE"),this.duration=o(t.duration,120),this.delay=o(t.delay,null),this.dashGap=o(t.dashGap,2),this.forceRender=t.hasOwnProperty("forceRender")?!!t.forceRender:this.isIE,this.selfDestroy=!!t.selfDestroy,this.delay>=this.duration)throw new Error("Vivus [constructor]: delay must be shorter than duration")},e.prototype.setCallback=function(t){if(t&&t.constructor!==Function)throw new Error('Vivus [constructor]: "callback" parameter must be a function');this.callback=t||function(){}},e.prototype.mapping=function(){var t,e,r,n,a,i,s,h;for(h=i=s=0,e=this.el.querySelectorAll("path"),t=0;t<e.length;t++)r=e[t],a={el:r,length:Math.ceil(r.getTotalLength())},isNaN(a.length)?window.console&&console.warn&&console.warn("Vivus [mapping]: cannot retrieve a path element length",r):(i+=a.length,this.map.push(a),r.style.strokeDasharray=a.length+" "+(a.length+this.dashGap),r.style.strokeDashoffset=a.length,this.isIE&&(a.length+=this.dashGap),this.renderPath(t));for(i=0===i?1:i,this.delay=null===this.delay?this.duration/3:this.delay,this.delayUnit=this.delay/(e.length>1?e.length-1:1),t=0;t<this.map.length;t++){switch(a=this.map[t],this.type){case"delayed":a.startAt=this.delayUnit*t,a.duration=this.duration-this.delay;break;case"oneByOne":a.startAt=s/i*this.duration,a.duration=a.length/i*this.duration;break;case"async":a.startAt=0,a.duration=this.duration;break;case"scenario-sync":r=e[t],n=this.parseAttr(r),a.startAt=h+(o(n["data-delay"],this.delayUnit)||0),a.duration=o(n["data-duration"],this.duration),h=void 0!==n["data-async"]?a.startAt:a.startAt+a.duration,this.frameLength=Math.max(this.frameLength,a.startAt+a.duration);break;case"scenario":r=e[t],n=this.parseAttr(r),a.startAt=o(n["data-start"],this.delayUnit)||0,a.duration=o(n["data-duration"],this.duration),this.frameLength=Math.max(this.frameLength,a.startAt+a.duration)}s+=a.length,this.frameLength=this.frameLength||this.duration}},e.prototype.drawer=function(){var t=this;this.currentFrame+=this.speed,this.currentFrame<=0?(this.stop(),this.reset()):this.currentFrame>=this.frameLength?(this.stop(),this.currentFrame=this.frameLength,this.trace(),this.selfDestroy&&this.destroy(),this.callback(this)):(this.trace(),this.handle=r(function(){t.drawer()}))},e.prototype.trace=function(){var t,e,r;for(t=0;t<this.map.length;t++)r=this.map[t],e=(this.currentFrame-r.startAt)/r.duration,e=Math.max(0,Math.min(1,e)),r.progress!==e&&(r.progress=e,r.el.style.strokeDashoffset=Math.floor(r.length*(1-e)),this.renderPath(t))},e.prototype.renderPath=function(t){if(this.forceRender&&this.map&&this.map[t]){var e=this.map[t],r=e.el.cloneNode(!0);e.el.parentNode.replaceChild(r,e.el),e.el=r}},e.prototype.starter=function(){switch(this.start){case"manual":return;case"autostart":this.play();break;case"inViewport":var t=this,e=function(){t.isInViewport(t.el,1)&&(t.play(),window.removeEventListener("scroll",e))};window.addEventListener("scroll",e),e()}},e.prototype.reset=function(){return this.currentFrame=0,this.trace(),this},e.prototype.play=function(t){if(t&&"number"!=typeof t)throw new Error("Vivus [play]: invalid speed");return this.speed=t||1,this.handle||this.drawer(),this},e.prototype.stop=function(){return this.handle&&(n(this.handle),delete this.handle),this},e.prototype.destroy=function(){var t,e;for(t=0;t<this.map.length;t++)e=this.map[t],e.el.style.strokeDashoffset=null,e.el.style.strokeDasharray=null,this.renderPath(t)},e.prototype.parseAttr=function(t){var e,r={};if(t&&t.attributes)for(var n=0;n<t.attributes.length;n++)e=t.attributes[n],r[e.name]=e.value;return r},e.prototype.isInViewport=function(t,e){var r=this.scrollY(),n=r+this.getViewportH(),o=t.getBoundingClientRect(),a=o.height,i=r+o.top,s=i+a;return e=e||0,n>=i+a*e&&s>=r},e.prototype.docElem=window.document.documentElement,e.prototype.getViewportH=function(){var t=this.docElem.clientHeight,e=window.innerHeight;return e>t?e:t},e.prototype.scrollY=function(){return window.pageYOffset||this.docElem.scrollTop},r=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),n=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t)}}(),o=function(t,e){var r=parseInt(t,10);return r>=0?r:e},window.Vivus=e}();
@@ -0,0 +1,3 @@
1
+ module RailsVivus
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ module RailsVivus
2
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_vivus do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_vivus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jack A. Huang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Vivus (SVG animation) assets for Rails
42
+ email:
43
+ - huynhquancam@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.rdoc
50
+ - Rakefile
51
+ - app/assets/javascripts/vivus.js
52
+ - app/assets/javascripts/vivus.min.js
53
+ - lib/rails_vivus.rb
54
+ - lib/rails_vivus/version.rb
55
+ - lib/tasks/rails_vivus_tasks.rake
56
+ homepage: http://github.com/huynhquancam/rails_vivus
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.4.5
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Vivus (SVG animation) assets for Rails
80
+ test_files: []