├── LICENSE
├── README.md
├── app.css
├── app.html
├── embed.css
├── embed.html
├── embed.js
├── favicon.ico
├── fonts.js
├── icons.svg
├── index.css
├── index.html
├── lib
├── StackBlur.js
├── canvg.js
└── rgbcolor.js
├── phosphorus.js
├── phosphorus.sublime-project
├── player.css
└── player.js
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2017 Nathan Dinsmore
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [phosphorus.github.io](https://phosphorus.github.io)
2 |
--------------------------------------------------------------------------------
/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #000;
3 | margin: 0;
4 | overflow: hidden;
5 | }
6 | .player {
7 | position: absolute;
8 | }
9 | .splash,
10 | .error {
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | width: 100%;
15 | height: 100%;
16 | background: #000;
17 | display: table;
18 | color: #fff;
19 | cursor: default;
20 | }
21 | .error {
22 | display: none;
23 | }
24 | .splash > div,
25 | .error > div {
26 | display: table-cell;
27 | height: 100%;
28 | text-align: center;
29 | vertical-align: middle;
30 | }
31 | .progress {
32 | width: 80%;
33 | height: 16px;
34 | border: 1px solid #fff;
35 | margin: 0 auto;
36 | }
37 | .progress-bar {
38 | background: #fff;
39 | width: 10%;
40 | height: 100%;
41 | }
42 | h1 {
43 | font: 300 72px Helvetica Neue, Helvetica, Arial, sans-serif;
44 | margin: 0 0 16px;
45 | }
46 | p {
47 | font: 300 24px/1.5 Helvetica Neue, Helvetica, Arial, sans-serif;
48 | margin: 0;
49 | color: rgba(255, 255, 255, .6);
50 | }
51 | .error a {
52 | color: #fff;
53 | }
54 |
--------------------------------------------------------------------------------
/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
phosphorus
7 |
8 |
9 |
10 |
11 |
phosphorus
12 |
13 |
14 |
15 |
16 |
17 |
phosphorus
18 |
An error has occurred. Click here to file a bug report on GitHub.
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
117 |
--------------------------------------------------------------------------------
/embed.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 480px;
3 | margin: 0 auto;
4 | }
5 |
6 | .progress-bar {
7 | padding: 0;
8 | }
9 |
10 | .internal-error {
11 | position: absolute;
12 | left: 1px;
13 | bottom: 1px;
14 | right: 1px;
15 | background: #fff;
16 | box-shadow: 0 -1px rgba(0, 0, 0, .4);
17 | }
18 |
19 | .controls {
20 | display: none;
21 | }
22 |
23 | .has-ui .controls {
24 | display: block;
25 | }
26 |
27 | .hide-ui .controls {
28 | position: absolute;
29 | width: 100%;
30 | }
31 | .hide-ui .controls span {
32 | display: none;
33 | }
34 | .hide-ui .player {
35 | box-shadow: none;
36 | }
37 |
--------------------------------------------------------------------------------
/embed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | phosphorus
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Turbo Mode
13 |
14 |
15 |
16 |
17 | An internal error occurred.
Click here to file a bug report.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
70 |
--------------------------------------------------------------------------------
/embed.js:
--------------------------------------------------------------------------------
1 | (function(global) {
2 | 'use strict';
3 |
4 | var script = document.currentScript || (function(scripts) {
5 | return scripts[scripts.length - 1];
6 | })(document.getElementsByTagName('script'));
7 |
8 | var hasUI = true;
9 | var params = script.src.split('?')[1].split('&');
10 | params.forEach(function(p) {
11 | var parts = p.split('=');
12 | if (parts.length > 1 && parts[0] === 'ui') {
13 | hasUI = parts[1] !== 'false';
14 | }
15 | });
16 |
17 | var iframe = document.createElement('iframe');
18 | iframe.setAttribute('allowfullscreen', true);
19 | iframe.setAttribute('allowtransparency', true);
20 | iframe.src = script.src.replace(/^http:/, 'https:').replace(/embed\.js/, 'embed.html');
21 | iframe.width = hasUI ? 482 : 480;
22 | iframe.height = hasUI ? 393 : 360;
23 | iframe.style.border = '0';
24 | iframe.className = 'phosphorus';
25 |
26 | script.parentNode.replaceChild(iframe, script);
27 |
28 | }(this));
29 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trumank/phosphorus/915ecc1185654e7bb3279faa3ec3457852064088/favicon.ico
--------------------------------------------------------------------------------
/fonts.js:
--------------------------------------------------------------------------------
1 | WebFontConfig = {
2 | google: { families: [ 'Donegal+One::latin', 'Gloria+Hallelujah::latin', 'Permanent+Marker::latin', 'Mystery+Quest::latin' ] }
3 | };
4 | (function() {
5 | var wf = document.createElement('script');
6 | wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
7 | '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
8 | wf.type = 'text/javascript';
9 | wf.async = 'true';
10 | var s = document.getElementsByTagName('script')[0];
11 | s.parentNode.insertBefore(wf, s);
12 | })();
13 |
--------------------------------------------------------------------------------
/icons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
20 |
39 |
46 |
57 |
58 |
60 |
61 |
63 | image/svg+xml
64 |
66 |
67 |
68 |
69 |
70 |
75 |
81 |
87 |
93 |
99 |
105 |
111 |
116 |
122 |
128 |
134 |
140 |
146 |
152 |
158 |
164 |
170 |
176 |
182 |
188 |
194 |
200 |
206 |
216 |
222 |
228 |
234 |
240 |
246 |
256 |
266 |
276 |
286 |
296 |
306 |
312 |
318 |
324 |
330 |
331 |
332 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 480px;
3 | margin: 24px auto 0;
4 | padding-bottom: 24px;
5 | }
6 | .title {
7 | margin: 16px 0 0;
8 | position: relative;
9 | }
10 | .url {
11 | position: relative;
12 | background: 0;
13 | border: 0;
14 | margin: 0;
15 | padding: 0 0 0 32px;
16 | outline: 0;
17 | font: 300 23px/32px Helvetica Neue, Helvetica, Arial, sans-serif;
18 | display: block;
19 | width: 100%;
20 | -moz-box-sizing: border-box;
21 | box-sizing: border-box;
22 | color: rgba(0, 0, 0, .4);
23 | }
24 | .url:focus {
25 | color: #000;
26 | }
27 | .project-link {
28 | position: absolute;
29 | top: 0;
30 | left: 0;
31 | background-position: -160px 0;
32 | }
33 |
34 | a {
35 | color: #25d;
36 | text-decoration: underline;
37 | }
38 | a:visited {
39 | color: #73c;
40 | }
41 | a:active {
42 | color: #03a;
43 | }
44 | .dropdown {
45 | display: inline-block;
46 | position: relative;
47 | }
48 | .dropdown > select {
49 | -webkit-appearance: none;
50 | font: inherit;
51 | position: absolute;
52 | opacity: 0;
53 | cursor: pointer;
54 | top: 0;
55 | left: 0;
56 | width: 100%;
57 | height: 100%;
58 | }
59 |
60 | .area {
61 | overflow: hidden;
62 | margin: 0 -24px;
63 | padding: 0 24px;
64 | }
65 | #player-area, #project-area {
66 | height: 0;
67 | }
68 | #player-area {
69 | border-bottom: 1px solid transparent;
70 | margin-bottom: -1px;
71 | }
72 | .fs #player-area {
73 | overflow: visible;
74 | }
75 |
76 | section {
77 | margin: 24px 0;
78 | }
79 | h1,
80 | p {
81 | font: 300 16px/1.5 Helvetica Neue, Helvetica, Arial, sans-serif;
82 | margin: 0 0 16px;
83 | }
84 | h1 {
85 | font: 300 24px/32px Helvetica Neue, Helvetica, Arial, sans-serif;
86 | margin: 16px 0 0;
87 | }
88 | h1.title {
89 | font: 300 54px/72px Helvetica Neue, Helvetica, Arial, sans-serif;
90 | margin: 0;
91 | }
92 | code {
93 | font: 12px Menlo, Monaco, Consolas, Courier New, monospace;
94 | background: #f5f5f5;
95 | border-radius: 3px;
96 | padding: 3px;
97 | }
98 | .package a {
99 | padding: 2px 8px;
100 | background: linear-gradient(#fafafa, #e8e8e8);
101 | box-shadow:
102 | 0 0 0 1px rgba(0, 0, 0, .35),
103 | 0 1px 4px rgba(0, 0, 0, .2);
104 | border-radius: 3px;
105 | cursor: pointer;
106 | color: #000;
107 | text-decoration: none;
108 | -webkit-tap-highlight-color: transparent;
109 | }
110 | .package a:hover {
111 | background: linear-gradient(#fff, #eaeaea);
112 | box-shadow:
113 | 0 0 0 1px rgba(0, 0, 0, .4),
114 | 0 1px 4px rgba(0, 0, 0, .3);
115 | }
116 | .package a:active {
117 | background: linear-gradient(#ddd, #eaeaea);
118 | box-shadow:
119 | 0 0 0 1px rgba(0, 0, 0, .5),
120 | inset 0 2px 5px rgba(0, 0, 0, .15);
121 | }
122 | .package label,
123 | .package input,
124 | .package a,
125 | .package span {
126 | display: inline-block;
127 | vertical-align: middle;
128 | }
129 | .package input[type=checkbox] {
130 | margin: 0 0 0 16px;
131 | }
132 |
133 | #embed-code {
134 | background: 0;
135 | border: 1px solid rgba(0, 0, 0, .4);
136 | margin: 0;
137 | padding: 4px;
138 | outline: 0;
139 | width: 80px;
140 | font: 12px/16px Menlo, Monaco, Consolas, Courier New, monospace;
141 | -moz-box-sizing: border-box;
142 | box-sizing: border-box;
143 | color: rgba(0, 0, 0, .7);
144 | }
145 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | phosphorus
5 |
6 |
7 |
8 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Turbo Mode
30 |
31 |
32 |
33 |
34 | An internal error occurred.
Click here to file a bug report.
35 |
36 |
37 |
38 |
43 |
44 |
45 |
56 |
69 |
70 | Report a problem
71 | phosphorus is still in development. Click here to report a problem with this project.
72 |
73 |
74 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
273 |
--------------------------------------------------------------------------------
/lib/StackBlur.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | StackBlur - a fast almost Gaussian Blur For Canvas
4 |
5 | Version: 0.5
6 | Author: Mario Klingemann
7 | Contact: mario@quasimondo.com
8 | Website: http://www.quasimondo.com/StackBlurForCanvas
9 | Twitter: @quasimondo
10 |
11 | In case you find this class useful - especially in commercial projects -
12 | I am not totally unhappy for a small donation to my PayPal account
13 | mario@quasimondo.de
14 |
15 | Or support me on flattr:
16 | https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
17 |
18 | Copyright (c) 2010 Mario Klingemann
19 |
20 | Permission is hereby granted, free of charge, to any person
21 | obtaining a copy of this software and associated documentation
22 | files (the "Software"), to deal in the Software without
23 | restriction, including without limitation the rights to use,
24 | copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the
26 | Software is furnished to do so, subject to the following
27 | conditions:
28 |
29 | The above copyright notice and this permission notice shall be
30 | included in all copies or substantial portions of the Software.
31 |
32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
34 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
36 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
38 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
39 | OTHER DEALINGS IN THE SOFTWARE.
40 | */
41 |
42 | (function ( global ) {
43 |
44 | var mul_table = [
45 | 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
46 | 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
47 | 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
48 | 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
49 | 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
50 | 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
51 | 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
52 | 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
53 | 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
54 | 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
55 | 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
56 | 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
57 | 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
58 | 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
59 | 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
60 | 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];
61 |
62 |
63 | var shg_table = [
64 | 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
65 | 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
66 | 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
67 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
68 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
69 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
70 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
71 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
72 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
73 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
74 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
75 | 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
76 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
77 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
78 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
79 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ];
80 |
81 | function premultiplyAlpha(imageData)
82 | {
83 | var pixels = imageData.data;
84 | var size = imageData.width * imageData.height * 4;
85 |
86 | for (var i=0; i> shg_sum;
249 | pixels[yi+1] = (g_sum * mul_sum) >> shg_sum;
250 | pixels[yi+2] = (b_sum * mul_sum) >> shg_sum;
251 | pixels[yi+3] = (a_sum * mul_sum) >> shg_sum;
252 |
253 | r_sum -= r_out_sum;
254 | g_sum -= g_out_sum;
255 | b_sum -= b_out_sum;
256 | a_sum -= a_out_sum;
257 |
258 | r_out_sum -= stackIn.r;
259 | g_out_sum -= stackIn.g;
260 | b_out_sum -= stackIn.b;
261 | a_out_sum -= stackIn.a;
262 |
263 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
264 |
265 | r_in_sum += ( stackIn.r = pixels[p]);
266 | g_in_sum += ( stackIn.g = pixels[p+1]);
267 | b_in_sum += ( stackIn.b = pixels[p+2]);
268 | a_in_sum += ( stackIn.a = pixels[p+3]);
269 |
270 | r_sum += r_in_sum;
271 | g_sum += g_in_sum;
272 | b_sum += b_in_sum;
273 | a_sum += a_in_sum;
274 |
275 | stackIn = stackIn.next;
276 |
277 | r_out_sum += ( pr = stackOut.r );
278 | g_out_sum += ( pg = stackOut.g );
279 | b_out_sum += ( pb = stackOut.b );
280 | a_out_sum += ( pa = stackOut.a );
281 |
282 | r_in_sum -= pr;
283 | g_in_sum -= pg;
284 | b_in_sum -= pb;
285 | a_in_sum -= pa;
286 |
287 | stackOut = stackOut.next;
288 |
289 | yi += 4;
290 | }
291 | yw += width;
292 | }
293 |
294 |
295 | for ( x = 0; x < width; x++ )
296 | {
297 | g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
298 |
299 | yi = x << 2;
300 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
301 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
302 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
303 | a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);
304 |
305 | r_sum += sumFactor * pr;
306 | g_sum += sumFactor * pg;
307 | b_sum += sumFactor * pb;
308 | a_sum += sumFactor * pa;
309 |
310 | stack = stackStart;
311 |
312 | for( i = 0; i < radiusPlus1; i++ )
313 | {
314 | stack.r = pr;
315 | stack.g = pg;
316 | stack.b = pb;
317 | stack.a = pa;
318 | stack = stack.next;
319 | }
320 |
321 | yp = width;
322 |
323 | for( i = 1; i <= radius; i++ )
324 | {
325 | yi = ( yp + x ) << 2;
326 |
327 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
328 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
329 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
330 | a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;
331 |
332 | r_in_sum += pr;
333 | g_in_sum += pg;
334 | b_in_sum += pb;
335 | a_in_sum += pa;
336 |
337 | stack = stack.next;
338 |
339 | if( i < heightMinus1 )
340 | {
341 | yp += width;
342 | }
343 | }
344 |
345 | yi = x;
346 | stackIn = stackStart;
347 | stackOut = stackEnd;
348 | for ( y = 0; y < height; y++ )
349 | {
350 | p = yi << 2;
351 | pixels[p] = (r_sum * mul_sum) >> shg_sum;
352 | pixels[p+1] = (g_sum * mul_sum) >> shg_sum;
353 | pixels[p+2] = (b_sum * mul_sum) >> shg_sum;
354 | pixels[p+3] = (a_sum * mul_sum) >> shg_sum;
355 |
356 | r_sum -= r_out_sum;
357 | g_sum -= g_out_sum;
358 | b_sum -= b_out_sum;
359 | a_sum -= a_out_sum;
360 |
361 | r_out_sum -= stackIn.r;
362 | g_out_sum -= stackIn.g;
363 | b_out_sum -= stackIn.b;
364 | a_out_sum -= stackIn.a;
365 |
366 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
367 |
368 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
369 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
370 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
371 | a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));
372 |
373 | stackIn = stackIn.next;
374 |
375 | r_out_sum += ( pr = stackOut.r );
376 | g_out_sum += ( pg = stackOut.g );
377 | b_out_sum += ( pb = stackOut.b );
378 | a_out_sum += ( pa = stackOut.a );
379 |
380 | r_in_sum -= pr;
381 | g_in_sum -= pg;
382 | b_in_sum -= pb;
383 | a_in_sum -= pa;
384 |
385 | stackOut = stackOut.next;
386 |
387 | yi += width;
388 | }
389 | }
390 |
391 | unpremultiplyAlpha(imageData);
392 |
393 | context.putImageData( imageData, top_x, top_y );
394 | }
395 |
396 |
397 | function stackBlurCanvasRGB( id, top_x, top_y, width, height, radius )
398 | {
399 | if ( isNaN(radius) || radius < 1 ) return;
400 | radius |= 0;
401 |
402 | var canvas = document.getElementById( id );
403 | var context = canvas.getContext("2d");
404 | var imageData;
405 |
406 | try {
407 | try {
408 | imageData = context.getImageData( top_x, top_y, width, height );
409 | } catch(e) {
410 |
411 | // NOTE: this part is supposedly only needed if you want to work with local files
412 | // so it might be okay to remove the whole try/catch block and just use
413 | // imageData = context.getImageData( top_x, top_y, width, height );
414 | try {
415 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
416 | imageData = context.getImageData( top_x, top_y, width, height );
417 | } catch(e) {
418 | alert("Cannot access local image");
419 | throw new Error("unable to access local image data: " + e);
420 | return;
421 | }
422 | }
423 | } catch(e) {
424 | alert("Cannot access image");
425 | throw new Error("unable to access image data: " + e);
426 | }
427 |
428 | var pixels = imageData.data;
429 |
430 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum,
431 | r_out_sum, g_out_sum, b_out_sum,
432 | r_in_sum, g_in_sum, b_in_sum,
433 | pr, pg, pb, rbs;
434 |
435 | var div = radius + radius + 1;
436 | var w4 = width << 2;
437 | var widthMinus1 = width - 1;
438 | var heightMinus1 = height - 1;
439 | var radiusPlus1 = radius + 1;
440 | var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2;
441 |
442 | var stackStart = new BlurStack();
443 | var stack = stackStart;
444 | for ( i = 1; i < div; i++ )
445 | {
446 | stack = stack.next = new BlurStack();
447 | if ( i == radiusPlus1 ) var stackEnd = stack;
448 | }
449 | stack.next = stackStart;
450 | var stackIn = null;
451 | var stackOut = null;
452 |
453 | yw = yi = 0;
454 |
455 | var mul_sum = mul_table[radius];
456 | var shg_sum = shg_table[radius];
457 |
458 | for ( y = 0; y < height; y++ )
459 | {
460 | r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;
461 |
462 | r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
463 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
464 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
465 |
466 | r_sum += sumFactor * pr;
467 | g_sum += sumFactor * pg;
468 | b_sum += sumFactor * pb;
469 |
470 | stack = stackStart;
471 |
472 | for( i = 0; i < radiusPlus1; i++ )
473 | {
474 | stack.r = pr;
475 | stack.g = pg;
476 | stack.b = pb;
477 | stack = stack.next;
478 | }
479 |
480 | for( i = 1; i < radiusPlus1; i++ )
481 | {
482 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
483 | r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
484 | g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
485 | b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
486 |
487 | r_in_sum += pr;
488 | g_in_sum += pg;
489 | b_in_sum += pb;
490 |
491 | stack = stack.next;
492 | }
493 |
494 |
495 | stackIn = stackStart;
496 | stackOut = stackEnd;
497 | for ( x = 0; x < width; x++ )
498 | {
499 | pixels[yi] = (r_sum * mul_sum) >> shg_sum;
500 | pixels[yi+1] = (g_sum * mul_sum) >> shg_sum;
501 | pixels[yi+2] = (b_sum * mul_sum) >> shg_sum;
502 |
503 | r_sum -= r_out_sum;
504 | g_sum -= g_out_sum;
505 | b_sum -= b_out_sum;
506 |
507 | r_out_sum -= stackIn.r;
508 | g_out_sum -= stackIn.g;
509 | b_out_sum -= stackIn.b;
510 |
511 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
512 |
513 | r_in_sum += ( stackIn.r = pixels[p]);
514 | g_in_sum += ( stackIn.g = pixels[p+1]);
515 | b_in_sum += ( stackIn.b = pixels[p+2]);
516 |
517 | r_sum += r_in_sum;
518 | g_sum += g_in_sum;
519 | b_sum += b_in_sum;
520 |
521 | stackIn = stackIn.next;
522 |
523 | r_out_sum += ( pr = stackOut.r );
524 | g_out_sum += ( pg = stackOut.g );
525 | b_out_sum += ( pb = stackOut.b );
526 |
527 | r_in_sum -= pr;
528 | g_in_sum -= pg;
529 | b_in_sum -= pb;
530 |
531 | stackOut = stackOut.next;
532 |
533 | yi += 4;
534 | }
535 | yw += width;
536 | }
537 |
538 |
539 | for ( x = 0; x < width; x++ )
540 | {
541 | g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;
542 |
543 | yi = x << 2;
544 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
545 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
546 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
547 |
548 | r_sum += sumFactor * pr;
549 | g_sum += sumFactor * pg;
550 | b_sum += sumFactor * pb;
551 |
552 | stack = stackStart;
553 |
554 | for( i = 0; i < radiusPlus1; i++ )
555 | {
556 | stack.r = pr;
557 | stack.g = pg;
558 | stack.b = pb;
559 | stack = stack.next;
560 | }
561 |
562 | yp = width;
563 |
564 | for( i = 1; i <= radius; i++ )
565 | {
566 | yi = ( yp + x ) << 2;
567 |
568 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
569 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
570 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
571 |
572 | r_in_sum += pr;
573 | g_in_sum += pg;
574 | b_in_sum += pb;
575 |
576 | stack = stack.next;
577 |
578 | if( i < heightMinus1 )
579 | {
580 | yp += width;
581 | }
582 | }
583 |
584 | yi = x;
585 | stackIn = stackStart;
586 | stackOut = stackEnd;
587 | for ( y = 0; y < height; y++ )
588 | {
589 | p = yi << 2;
590 | pixels[p] = (r_sum * mul_sum) >> shg_sum;
591 | pixels[p+1] = (g_sum * mul_sum) >> shg_sum;
592 | pixels[p+2] = (b_sum * mul_sum) >> shg_sum;
593 |
594 | r_sum -= r_out_sum;
595 | g_sum -= g_out_sum;
596 | b_sum -= b_out_sum;
597 |
598 | r_out_sum -= stackIn.r;
599 | g_out_sum -= stackIn.g;
600 | b_out_sum -= stackIn.b;
601 |
602 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
603 |
604 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
605 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
606 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
607 |
608 | stackIn = stackIn.next;
609 |
610 | r_out_sum += ( pr = stackOut.r );
611 | g_out_sum += ( pg = stackOut.g );
612 | b_out_sum += ( pb = stackOut.b );
613 |
614 | r_in_sum -= pr;
615 | g_in_sum -= pg;
616 | b_in_sum -= pb;
617 |
618 | stackOut = stackOut.next;
619 |
620 | yi += width;
621 | }
622 | }
623 |
624 | context.putImageData( imageData, top_x, top_y );
625 |
626 | }
627 |
628 | function BlurStack()
629 | {
630 | this.r = 0;
631 | this.g = 0;
632 | this.b = 0;
633 | this.a = 0;
634 | this.next = null;
635 | }
636 |
637 | var stackBlur = {
638 | image: stackBlurImage,
639 | canvasRGBA: stackBlurCanvasRGBA,
640 | canvasRGB: stackBlurCanvasRGB
641 | };
642 |
643 | // export as AMD...
644 | if ( typeof define !== 'undefined' && define.amd ) {
645 | define( function () { return stackBlur; });
646 | }
647 |
648 | // ...or as browserify
649 | else if ( typeof module !== 'undefined' && module.exports ) {
650 | module.exports = stackBlur;
651 | }
652 |
653 | global.stackBlur = stackBlur;
654 |
655 | }( typeof window !== 'undefined' ? window : this ));
656 |
--------------------------------------------------------------------------------
/lib/canvg.js:
--------------------------------------------------------------------------------
1 | /*
2 | * canvg.js - Javascript SVG parser and renderer on Canvas
3 | * MIT Licensed
4 | * Gabe Lerner (gabelerner@gmail.com)
5 | * http://code.google.com/p/canvg/
6 | *
7 | * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
8 | */
9 | (function ( global, factory ) {
10 |
11 | 'use strict';
12 |
13 | // export as AMD...
14 | if ( typeof define !== 'undefined' && define.amd ) {
15 | define('canvgModule', [ 'rgbcolor', 'stackblur' ], factory );
16 | }
17 |
18 | // ...or as browserify
19 | else if ( typeof module !== 'undefined' && module.exports ) {
20 | module.exports = factory( require( 'rgbcolor' ), require( 'stackblur' ) );
21 | }
22 |
23 | global.canvg = factory( global.RGBColor, global.stackBlur );
24 |
25 | }( typeof window !== 'undefined' ? window : this, function ( RGBColor, stackBlur ) {
26 |
27 | // canvg(target, s)
28 | // empty parameters: replace all 'svg' elements on page with 'canvas' elements
29 | // target: canvas element or the id of a canvas element
30 | // s: svg string, url to svg file, or xml document
31 | // opts: optional hash of options
32 | // ignoreMouse: true => ignore mouse events
33 | // ignoreAnimation: true => ignore animations
34 | // ignoreDimensions: true => does not try to resize canvas
35 | // ignoreClear: true => does not clear canvas
36 | // offsetX: int => draws at a x offset
37 | // offsetY: int => draws at a y offset
38 | // scaleWidth: int => scales horizontally to width
39 | // scaleHeight: int => scales vertically to height
40 | // renderCallback: function => will call the function after the first render is completed
41 | // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
42 | var canvg = function (target, s, opts) {
43 | // no parameters
44 | if (target == null && s == null && opts == null) {
45 | var svgTags = document.querySelectorAll('svg');
46 | for (var i=0; i~\.\[:]+)/g;
127 | var classRegex = /(\.[^\s\+>~\.\[:]+)/g;
128 | var pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi;
129 | var pseudoClassWithBracketsRegex = /(:[\w-]+\([^\)]*\))/gi;
130 | var pseudoClassRegex = /(:[^\s\+>~\.\[:]+)/g;
131 | var elementRegex = /([^\s\+>~\.\[:]+)/g;
132 | function getSelectorSpecificity(selector) {
133 | var typeCount = [0, 0, 0];
134 | var findMatch = function(regex, type) {
135 | var matches = selector.match(regex);
136 | if (matches == null) {
137 | return;
138 | }
139 | typeCount[type] += matches.length;
140 | selector = selector.replace(regex, ' ');
141 | };
142 |
143 | selector = selector.replace(/:not\(([^\)]*)\)/g, ' $1 ');
144 | selector = selector.replace(/{[^]*/gm, ' ');
145 | findMatch(attributeRegex, 1);
146 | findMatch(idRegex, 0);
147 | findMatch(classRegex, 1);
148 | findMatch(pseudoElementRegex, 2);
149 | findMatch(pseudoClassWithBracketsRegex, 1);
150 | findMatch(pseudoClassRegex, 1);
151 | selector = selector.replace(/[\*\s\+>~]/g, ' ');
152 | selector = selector.replace(/[#\.]/g, ' ');
153 | findMatch(elementRegex, 2);
154 | return typeCount.join('');
155 | }
156 |
157 | function build(opts) {
158 | var svg = { opts: opts };
159 |
160 | svg.FRAMERATE = 30;
161 | svg.MAX_VIRTUAL_PIXELS = 30000;
162 |
163 | svg.log = function(msg) {};
164 | if (svg.opts['log'] == true && typeof(console) != 'undefined') {
165 | svg.log = function(msg) { console.log(msg); };
166 | };
167 |
168 | // globals
169 | svg.init = function(ctx) {
170 | var uniqueId = 0;
171 | svg.UniqueId = function () { uniqueId++; return 'canvg' + uniqueId; };
172 | svg.Definitions = {};
173 | svg.Styles = {};
174 | svg.StylesSpecificity = {};
175 | svg.Animations = [];
176 | svg.Images = [];
177 | svg.ctx = ctx;
178 | svg.ViewPort = new (function () {
179 | this.viewPorts = [];
180 | this.Clear = function() { this.viewPorts = []; }
181 | this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
182 | this.RemoveCurrent = function() { this.viewPorts.pop(); }
183 | this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
184 | this.width = function() { return this.Current().width; }
185 | this.height = function() { return this.Current().height; }
186 | this.ComputeSize = function(d) {
187 | if (d != null && typeof(d) == 'number') return d;
188 | if (d == 'x') return this.width();
189 | if (d == 'y') return this.height();
190 | return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
191 | }
192 | });
193 | }
194 | svg.init();
195 |
196 | // images loaded
197 | svg.ImagesLoaded = function() {
198 | for (var i=0; i]*>/, '');
240 | var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
241 | xmlDoc.async = 'false';
242 | xmlDoc.loadXML(xml);
243 | return xmlDoc;
244 | }
245 | }
246 |
247 | svg.Property = function(name, value) {
248 | this.name = name;
249 | this.value = value;
250 | }
251 | svg.Property.prototype.getValue = function() {
252 | return this.value;
253 | }
254 |
255 | svg.Property.prototype.hasValue = function() {
256 | return (this.value != null && this.value !== '');
257 | }
258 |
259 | // return the numerical value of the property
260 | svg.Property.prototype.numValue = function() {
261 | if (!this.hasValue()) return 0;
262 |
263 | var n = parseFloat(this.value);
264 | if ((this.value + '').match(/%$/)) {
265 | n = n / 100.0;
266 | }
267 | return n;
268 | }
269 |
270 | svg.Property.prototype.valueOrDefault = function(def) {
271 | if (this.hasValue()) return this.value;
272 | return def;
273 | }
274 |
275 | svg.Property.prototype.numValueOrDefault = function(def) {
276 | if (this.hasValue()) return this.numValue();
277 | return def;
278 | }
279 |
280 | // color extensions
281 | // augment the current color value with the opacity
282 | svg.Property.prototype.addOpacity = function(opacityProp) {
283 | var newValue = this.value;
284 | if (opacityProp.value != null && opacityProp.value != '' && typeof(this.value)=='string') { // can only add opacity to colors, not patterns
285 | var color = new RGBColor(this.value);
286 | if (color.ok) {
287 | newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacityProp.numValue() + ')';
288 | }
289 | }
290 | return new svg.Property(this.name, newValue);
291 | }
292 |
293 | // definition extensions
294 | // get the definition from the definitions table
295 | svg.Property.prototype.getDefinition = function() {
296 | var name = this.value.match(/#([^\)'"]+)/);
297 | if (name) { name = name[1]; }
298 | if (!name) { name = this.value; }
299 | return svg.Definitions[name];
300 | }
301 |
302 | svg.Property.prototype.isUrlDefinition = function() {
303 | return this.value.indexOf('url(') == 0
304 | }
305 |
306 | svg.Property.prototype.getFillStyleDefinition = function(e, opacityProp) {
307 | var def = this.getDefinition();
308 |
309 | // gradient
310 | if (def != null && def.createGradient) {
311 | return def.createGradient(svg.ctx, e, opacityProp);
312 | }
313 |
314 | // pattern
315 | if (def != null && def.createPattern) {
316 | if (def.getHrefAttribute().hasValue()) {
317 | var pt = def.attribute('patternTransform');
318 | def = def.getHrefAttribute().getDefinition();
319 | if (pt.hasValue()) { def.attribute('patternTransform', true).value = pt.value; }
320 | }
321 | return def.createPattern(svg.ctx, e);
322 | }
323 |
324 | return null;
325 | }
326 |
327 | // length extensions
328 | svg.Property.prototype.getDPI = function(viewPort) {
329 | return 96.0; // TODO: compute?
330 | }
331 |
332 | svg.Property.prototype.getEM = function(viewPort) {
333 | var em = 12;
334 |
335 | var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
336 | if (fontSize.hasValue()) em = fontSize.toPixels(viewPort);
337 |
338 | return em;
339 | }
340 |
341 | svg.Property.prototype.getUnits = function() {
342 | var s = this.value+'';
343 | return s.replace(/[0-9\.\-]/g,'');
344 | }
345 |
346 | // get the length as pixels
347 | svg.Property.prototype.toPixels = function(viewPort, processPercent) {
348 | if (!this.hasValue()) return 0;
349 | var s = this.value+'';
350 | if (s.match(/em$/)) return this.numValue() * this.getEM(viewPort);
351 | if (s.match(/ex$/)) return this.numValue() * this.getEM(viewPort) / 2.0;
352 | if (s.match(/px$/)) return this.numValue();
353 | if (s.match(/pt$/)) return this.numValue() * this.getDPI(viewPort) * (1.0 / 72.0);
354 | if (s.match(/pc$/)) return this.numValue() * 15;
355 | if (s.match(/cm$/)) return this.numValue() * this.getDPI(viewPort) / 2.54;
356 | if (s.match(/mm$/)) return this.numValue() * this.getDPI(viewPort) / 25.4;
357 | if (s.match(/in$/)) return this.numValue() * this.getDPI(viewPort);
358 | if (s.match(/%$/)) return this.numValue() * svg.ViewPort.ComputeSize(viewPort);
359 | var n = this.numValue();
360 | if (processPercent && n < 1.0) return n * svg.ViewPort.ComputeSize(viewPort);
361 | return n;
362 | }
363 |
364 | // time extensions
365 | // get the time as milliseconds
366 | svg.Property.prototype.toMilliseconds = function() {
367 | if (!this.hasValue()) return 0;
368 | var s = this.value+'';
369 | if (s.match(/s$/)) return this.numValue() * 1000;
370 | if (s.match(/ms$/)) return this.numValue();
371 | return this.numValue();
372 | }
373 |
374 | // angle extensions
375 | // get the angle as radians
376 | svg.Property.prototype.toRadians = function() {
377 | if (!this.hasValue()) return 0;
378 | var s = this.value+'';
379 | if (s.match(/deg$/)) return this.numValue() * (Math.PI / 180.0);
380 | if (s.match(/grad$/)) return this.numValue() * (Math.PI / 200.0);
381 | if (s.match(/rad$/)) return this.numValue();
382 | return this.numValue() * (Math.PI / 180.0);
383 | }
384 |
385 | // text extensions
386 | // get the text baseline
387 | var textBaselineMapping = {
388 | 'baseline': 'alphabetic',
389 | 'before-edge': 'top',
390 | 'text-before-edge': 'top',
391 | 'middle': 'middle',
392 | 'central': 'middle',
393 | 'after-edge': 'bottom',
394 | 'text-after-edge': 'bottom',
395 | 'ideographic': 'ideographic',
396 | 'alphabetic': 'alphabetic',
397 | 'hanging': 'hanging',
398 | 'mathematical': 'alphabetic'
399 | };
400 | svg.Property.prototype.toTextBaseline = function () {
401 | if (!this.hasValue()) return null;
402 | return textBaselineMapping[this.value];
403 | }
404 |
405 | // fonts
406 | svg.Font = new (function() {
407 | this.Styles = 'normal|italic|oblique|inherit';
408 | this.Variants = 'normal|small-caps|inherit';
409 | this.Weights = 'normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit';
410 |
411 | this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
412 | var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
413 | return {
414 | fontFamily: fontFamily || f.fontFamily,
415 | fontSize: fontSize || f.fontSize,
416 | fontStyle: fontStyle || f.fontStyle,
417 | fontWeight: fontWeight || f.fontWeight,
418 | fontVariant: fontVariant || f.fontVariant,
419 | toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
420 | }
421 | }
422 |
423 | var that = this;
424 | this.Parse = function(s) {
425 | var f = {};
426 | var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
427 | var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
428 | var ff = '';
429 | for (var i=0; i this.x2) this.x2 = x;
496 | }
497 |
498 | if (y != null) {
499 | if (isNaN(this.y1) || isNaN(this.y2)) {
500 | this.y1 = y;
501 | this.y2 = y;
502 | }
503 | if (y < this.y1) this.y1 = y;
504 | if (y > this.y2) this.y2 = y;
505 | }
506 | }
507 | this.addX = function(x) { this.addPoint(x, null); }
508 | this.addY = function(y) { this.addPoint(null, y); }
509 |
510 | this.addBoundingBox = function(bb) {
511 | this.addPoint(bb.x1, bb.y1);
512 | this.addPoint(bb.x2, bb.y2);
513 | }
514 |
515 | this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
516 | var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
517 | var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
518 | var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
519 | var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
520 | this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
521 | }
522 |
523 | this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
524 | // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
525 | var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
526 | this.addPoint(p0[0], p0[1]);
527 | this.addPoint(p3[0], p3[1]);
528 |
529 | for (i=0; i<=1; i++) {
530 | var f = function(t) {
531 | return Math.pow(1-t, 3) * p0[i]
532 | + 3 * Math.pow(1-t, 2) * t * p1[i]
533 | + 3 * (1-t) * Math.pow(t, 2) * p2[i]
534 | + Math.pow(t, 3) * p3[i];
535 | }
536 |
537 | var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
538 | var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
539 | var c = 3 * p1[i] - 3 * p0[i];
540 |
541 | if (a == 0) {
542 | if (b == 0) continue;
543 | var t = -c / b;
544 | if (0 < t && t < 1) {
545 | if (i == 0) this.addX(f(t));
546 | if (i == 1) this.addY(f(t));
547 | }
548 | continue;
549 | }
550 |
551 | var b2ac = Math.pow(b, 2) - 4 * c * a;
552 | if (b2ac < 0) continue;
553 | var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
554 | if (0 < t1 && t1 < 1) {
555 | if (i == 0) this.addX(f(t1));
556 | if (i == 1) this.addY(f(t1));
557 | }
558 | var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
559 | if (0 < t2 && t2 < 1) {
560 | if (i == 0) this.addX(f(t2));
561 | if (i == 1) this.addY(f(t2));
562 | }
563 | }
564 | }
565 |
566 | this.isPointInBox = function(x, y) {
567 | return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
568 | }
569 |
570 | this.addPoint(x1, y1);
571 | this.addPoint(x2, y2);
572 | }
573 |
574 | // transforms
575 | svg.Transform = function(v) {
576 | var that = this;
577 | this.Type = {}
578 |
579 | // translate
580 | this.Type.translate = function(s) {
581 | this.p = svg.CreatePoint(s);
582 | this.apply = function(ctx) {
583 | ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
584 | }
585 | this.unapply = function(ctx) {
586 | ctx.translate(-1.0 * this.p.x || 0.0, -1.0 * this.p.y || 0.0);
587 | }
588 | this.applyToPoint = function(p) {
589 | p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
590 | }
591 | }
592 |
593 | // rotate
594 | this.Type.rotate = function(s) {
595 | var a = svg.ToNumberArray(s);
596 | this.angle = new svg.Property('angle', a[0]);
597 | this.cx = a[1] || 0;
598 | this.cy = a[2] || 0;
599 | this.apply = function(ctx) {
600 | ctx.translate(this.cx, this.cy);
601 | ctx.rotate(this.angle.toRadians());
602 | ctx.translate(-this.cx, -this.cy);
603 | }
604 | this.unapply = function(ctx) {
605 | ctx.translate(this.cx, this.cy);
606 | ctx.rotate(-1.0 * this.angle.toRadians());
607 | ctx.translate(-this.cx, -this.cy);
608 | }
609 | this.applyToPoint = function(p) {
610 | var a = this.angle.toRadians();
611 | p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
612 | p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
613 | p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
614 | }
615 | }
616 |
617 | this.Type.scale = function(s) {
618 | this.p = svg.CreatePoint(s);
619 | this.apply = function(ctx) {
620 | ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
621 | }
622 | this.unapply = function(ctx) {
623 | ctx.scale(1.0 / this.p.x || 1.0, 1.0 / this.p.y || this.p.x || 1.0);
624 | }
625 | this.applyToPoint = function(p) {
626 | p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
627 | }
628 | }
629 |
630 | this.Type.matrix = function(s) {
631 | this.m = svg.ToNumberArray(s);
632 | this.apply = function(ctx) {
633 | ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
634 | }
635 | this.unapply = function(ctx) {
636 | var a = this.m[0];
637 | var b = this.m[2];
638 | var c = this.m[4];
639 | var d = this.m[1];
640 | var e = this.m[3];
641 | var f = this.m[5];
642 | var g = 0.0;
643 | var h = 0.0;
644 | var i = 1.0;
645 | var det = 1 / (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g));
646 | ctx.transform(
647 | det*(e*i-f*h),
648 | det*(f*g-d*i),
649 | det*(c*h-b*i),
650 | det*(a*i-c*g),
651 | det*(b*f-c*e),
652 | det*(c*d-a*f)
653 | );
654 | }
655 | this.applyToPoint = function(p) {
656 | p.applyTransform(this.m);
657 | }
658 | }
659 |
660 | this.Type.SkewBase = function(s) {
661 | this.base = that.Type.matrix;
662 | this.base(s);
663 | this.angle = new svg.Property('angle', s);
664 | }
665 | this.Type.SkewBase.prototype = new this.Type.matrix;
666 |
667 | this.Type.skewX = function(s) {
668 | this.base = that.Type.SkewBase;
669 | this.base(s);
670 | this.m = [1, 0, Math.tan(this.angle.toRadians()), 1, 0, 0];
671 | }
672 | this.Type.skewX.prototype = new this.Type.SkewBase;
673 |
674 | this.Type.skewY = function(s) {
675 | this.base = that.Type.SkewBase;
676 | this.base(s);
677 | this.m = [1, Math.tan(this.angle.toRadians()), 0, 1, 0, 0];
678 | }
679 | this.Type.skewY.prototype = new this.Type.SkewBase;
680 |
681 | this.transforms = [];
682 |
683 | this.apply = function(ctx) {
684 | for (var i=0; i=0; i--) {
691 | this.transforms[i].unapply(ctx);
692 | }
693 | }
694 |
695 | this.applyToPoint = function(p) {
696 | for (var i=0; i existingSpecificity) {
865 | this.styles[name] = styles[name];
866 | this.stylesSpecificity[name] = specificity;
867 | }
868 | }
869 | }
870 | }
871 | }
872 | };
873 |
874 | if (node != null && node.nodeType == 1) { //ELEMENT_NODE
875 | // add attributes
876 | for (var i=0; i= this.tokens.length - 1;
1328 | }
1329 |
1330 | this.isCommandOrEnd = function() {
1331 | if (this.isEnd()) return true;
1332 | return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
1333 | }
1334 |
1335 | this.isRelativeCommand = function() {
1336 | switch(this.command)
1337 | {
1338 | case 'm':
1339 | case 'l':
1340 | case 'h':
1341 | case 'v':
1342 | case 'c':
1343 | case 's':
1344 | case 'q':
1345 | case 't':
1346 | case 'a':
1347 | case 'z':
1348 | return true;
1349 | break;
1350 | }
1351 | return false;
1352 | }
1353 |
1354 | this.getToken = function() {
1355 | this.i++;
1356 | return this.tokens[this.i];
1357 | }
1358 |
1359 | this.getScalar = function() {
1360 | return parseFloat(this.getToken());
1361 | }
1362 |
1363 | this.nextCommand = function() {
1364 | this.previousCommand = this.command;
1365 | this.command = this.getToken();
1366 | }
1367 |
1368 | this.getPoint = function() {
1369 | var p = new svg.Point(this.getScalar(), this.getScalar());
1370 | return this.makeAbsolute(p);
1371 | }
1372 |
1373 | this.getAsControlPoint = function() {
1374 | var p = this.getPoint();
1375 | this.control = p;
1376 | return p;
1377 | }
1378 |
1379 | this.getAsCurrentPoint = function() {
1380 | var p = this.getPoint();
1381 | this.current = p;
1382 | return p;
1383 | }
1384 |
1385 | this.getReflectedControlPoint = function() {
1386 | if (this.previousCommand.toLowerCase() != 'c' &&
1387 | this.previousCommand.toLowerCase() != 's' &&
1388 | this.previousCommand.toLowerCase() != 'q' &&
1389 | this.previousCommand.toLowerCase() != 't' ){
1390 | return this.current;
1391 | }
1392 |
1393 | // reflect point
1394 | var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
1395 | return p;
1396 | }
1397 |
1398 | this.makeAbsolute = function(p) {
1399 | if (this.isRelativeCommand()) {
1400 | p.x += this.current.x;
1401 | p.y += this.current.y;
1402 | }
1403 | return p;
1404 | }
1405 |
1406 | this.addMarker = function(p, from, priorTo) {
1407 | // if the last angle isn't filled in because we didn't have this point yet ...
1408 | if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
1409 | this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
1410 | }
1411 | this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
1412 | }
1413 |
1414 | this.addMarkerAngle = function(p, a) {
1415 | this.points.push(p);
1416 | this.angles.push(a);
1417 | }
1418 |
1419 | this.getMarkerPoints = function() { return this.points; }
1420 | this.getMarkerAngles = function() {
1421 | for (var i=0; i 1) {
1556 | rx *= Math.sqrt(l);
1557 | ry *= Math.sqrt(l);
1558 | }
1559 | // cx', cy'
1560 | var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
1561 | ((Math.pow(rx,2)*Math.pow(ry,2))-(Math.pow(rx,2)*Math.pow(currp.y,2))-(Math.pow(ry,2)*Math.pow(currp.x,2))) /
1562 | (Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
1563 | );
1564 | if (isNaN(s)) s = 0;
1565 | var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
1566 | // cx, cy
1567 | var centp = new svg.Point(
1568 | (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
1569 | (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
1570 | );
1571 | // vector magnitude
1572 | var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
1573 | // ratio between two vectors
1574 | var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
1575 | // angle between two vectors
1576 | var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
1577 | // initial angle
1578 | var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
1579 | // angle delta
1580 | var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
1581 | var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
1582 | var ad = a(u, v);
1583 | if (r(u,v) <= -1) ad = Math.PI;
1584 | if (r(u,v) >= 1) ad = 0;
1585 |
1586 | // for markers
1587 | var dir = 1 - sweepFlag ? 1.0 : -1.0;
1588 | var ah = a1 + dir * (ad / 2.0);
1589 | var halfWay = new svg.Point(
1590 | centp.x + rx * Math.cos(ah),
1591 | centp.y + ry * Math.sin(ah)
1592 | );
1593 | pp.addMarkerAngle(halfWay, ah - dir * Math.PI / 2);
1594 | pp.addMarkerAngle(cp, ah - dir * Math.PI);
1595 |
1596 | bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
1597 | if (ctx != null) {
1598 | var r = rx > ry ? rx : ry;
1599 | var sx = rx > ry ? 1 : rx / ry;
1600 | var sy = rx > ry ? ry / rx : 1;
1601 |
1602 | ctx.translate(centp.x, centp.y);
1603 | ctx.rotate(xAxisRotation);
1604 | ctx.scale(sx, sy);
1605 | ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
1606 | ctx.scale(1/sx, 1/sy);
1607 | ctx.rotate(-xAxisRotation);
1608 | ctx.translate(-centp.x, -centp.y);
1609 | }
1610 | }
1611 | break;
1612 | case 'Z':
1613 | case 'z':
1614 | if (ctx != null) ctx.closePath();
1615 | pp.current = pp.start;
1616 | }
1617 | }
1618 |
1619 | return bb;
1620 | }
1621 |
1622 | this.getMarkers = function() {
1623 | var points = this.PathParser.getMarkerPoints();
1624 | var angles = this.PathParser.getMarkerAngles();
1625 |
1626 | var markers = [];
1627 | for (var i=0; i 1) this.offset = 1;
1901 |
1902 | var stopColor = this.style('stop-color', true);
1903 | if (stopColor.value === '') stopColor.value = '#000';
1904 | if (this.style('stop-opacity').hasValue()) stopColor = stopColor.addOpacity(this.style('stop-opacity'));
1905 | this.color = stopColor.value;
1906 | }
1907 | svg.Element.stop.prototype = new svg.Element.ElementBase;
1908 |
1909 | // animation base element
1910 | svg.Element.AnimateBase = function(node) {
1911 | this.base = svg.Element.ElementBase;
1912 | this.base(node);
1913 |
1914 | svg.Animations.push(this);
1915 |
1916 | this.duration = 0.0;
1917 | this.begin = this.attribute('begin').toMilliseconds();
1918 | this.maxDuration = this.begin + this.attribute('dur').toMilliseconds();
1919 |
1920 | this.getProperty = function() {
1921 | var attributeType = this.attribute('attributeType').value;
1922 | var attributeName = this.attribute('attributeName').value;
1923 |
1924 | if (attributeType == 'CSS') {
1925 | return this.parent.style(attributeName, true);
1926 | }
1927 | return this.parent.attribute(attributeName, true);
1928 | };
1929 |
1930 | this.initialValue = null;
1931 | this.initialUnits = '';
1932 | this.removed = false;
1933 |
1934 | this.calcValue = function() {
1935 | // OVERRIDE ME!
1936 | return '';
1937 | }
1938 |
1939 | this.update = function(delta) {
1940 | // set initial value
1941 | if (this.initialValue == null) {
1942 | this.initialValue = this.getProperty().value;
1943 | this.initialUnits = this.getProperty().getUnits();
1944 | }
1945 |
1946 | // if we're past the end time
1947 | if (this.duration > this.maxDuration) {
1948 | // loop for indefinitely repeating animations
1949 | if (this.attribute('repeatCount').value == 'indefinite'
1950 | || this.attribute('repeatDur').value == 'indefinite') {
1951 | this.duration = 0.0
1952 | }
1953 | else if (this.attribute('fill').valueOrDefault('remove') == 'freeze' && !this.frozen) {
1954 | this.frozen = true;
1955 | this.parent.animationFrozen = true;
1956 | this.parent.animationFrozenValue = this.getProperty().value;
1957 | }
1958 | else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
1959 | this.removed = true;
1960 | this.getProperty().value = this.parent.animationFrozen ? this.parent.animationFrozenValue : this.initialValue;
1961 | return true;
1962 | }
1963 | return false;
1964 | }
1965 | this.duration = this.duration + delta;
1966 |
1967 | // if we're past the begin time
1968 | var updated = false;
1969 | if (this.begin < this.duration) {
1970 | var newValue = this.calcValue(); // tween
1971 |
1972 | if (this.attribute('type').hasValue()) {
1973 | // for transform, etc.
1974 | var type = this.attribute('type').value;
1975 | newValue = type + '(' + newValue + ')';
1976 | }
1977 |
1978 | this.getProperty().value = newValue;
1979 | updated = true;
1980 | }
1981 |
1982 | return updated;
1983 | }
1984 |
1985 | this.from = this.attribute('from');
1986 | this.to = this.attribute('to');
1987 | this.values = this.attribute('values');
1988 | if (this.values.hasValue()) this.values.value = this.values.value.split(';');
1989 |
1990 | // fraction of duration we've covered
1991 | this.progress = function() {
1992 | var ret = { progress: (this.duration - this.begin) / (this.maxDuration - this.begin) };
1993 | if (this.values.hasValue()) {
1994 | var p = ret.progress * (this.values.value.length - 1);
1995 | var lb = Math.floor(p), ub = Math.ceil(p);
1996 | ret.from = new svg.Property('from', parseFloat(this.values.value[lb]));
1997 | ret.to = new svg.Property('to', parseFloat(this.values.value[ub]));
1998 | ret.progress = (p - lb) / (ub - lb);
1999 | }
2000 | else {
2001 | ret.from = this.from;
2002 | ret.to = this.to;
2003 | }
2004 | return ret;
2005 | }
2006 | }
2007 | svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
2008 |
2009 | // animate element
2010 | svg.Element.animate = function(node) {
2011 | this.base = svg.Element.AnimateBase;
2012 | this.base(node);
2013 |
2014 | this.calcValue = function() {
2015 | var p = this.progress();
2016 |
2017 | // tween value linearly
2018 | var newValue = p.from.numValue() + (p.to.numValue() - p.from.numValue()) * p.progress;
2019 | return newValue + this.initialUnits;
2020 | };
2021 | }
2022 | svg.Element.animate.prototype = new svg.Element.AnimateBase;
2023 |
2024 | // animate color element
2025 | svg.Element.animateColor = function(node) {
2026 | this.base = svg.Element.AnimateBase;
2027 | this.base(node);
2028 |
2029 | this.calcValue = function() {
2030 | var p = this.progress();
2031 | var from = new RGBColor(p.from.value);
2032 | var to = new RGBColor(p.to.value);
2033 |
2034 | if (from.ok && to.ok) {
2035 | // tween color linearly
2036 | var r = from.r + (to.r - from.r) * p.progress;
2037 | var g = from.g + (to.g - from.g) * p.progress;
2038 | var b = from.b + (to.b - from.b) * p.progress;
2039 | return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
2040 | }
2041 | return this.attribute('from').value;
2042 | };
2043 | }
2044 | svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
2045 |
2046 | // animate transform element
2047 | svg.Element.animateTransform = function(node) {
2048 | this.base = svg.Element.AnimateBase;
2049 | this.base(node);
2050 |
2051 | this.calcValue = function() {
2052 | var p = this.progress();
2053 |
2054 | // tween value linearly
2055 | var from = svg.ToNumberArray(p.from.value);
2056 | var to = svg.ToNumberArray(p.to.value);
2057 | var newValue = '';
2058 | for (var i=0; i startI && child.attribute('x').hasValue()) break; // new group
2173 | width += child.measureTextRecursive(ctx);
2174 | }
2175 | return -1 * (textAnchor == 'end' ? width : width / 2.0);
2176 | }
2177 | return 0;
2178 | }
2179 |
2180 | this.renderChild = function(ctx, parent, i) {
2181 | var child = parent.children[i];
2182 | if (child.attribute('x').hasValue()) {
2183 | child.x = child.attribute('x').toPixels('x') + parent.getAnchorDelta(ctx, parent, i);
2184 | if (child.attribute('dx').hasValue()) child.x += child.attribute('dx').toPixels('x');
2185 | }
2186 | else {
2187 | if (child.attribute('dx').hasValue()) parent.x += child.attribute('dx').toPixels('x');
2188 | child.x = parent.x;
2189 | }
2190 | parent.x = child.x + child.measureText(ctx);
2191 |
2192 | if (child.attribute('y').hasValue()) {
2193 | child.y = child.attribute('y').toPixels('y');
2194 | if (child.attribute('dy').hasValue()) child.y += child.attribute('dy').toPixels('y');
2195 | }
2196 | else {
2197 | if (child.attribute('dy').hasValue()) parent.y += child.attribute('dy').toPixels('y');
2198 | child.y = parent.y;
2199 | }
2200 | parent.y = child.y;
2201 |
2202 | child.render(ctx);
2203 |
2204 | for (var i=0; i0 && text[i-1]!=' ' && i0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
2224 | if (typeof(font.glyphs[c]) != 'undefined') {
2225 | glyph = font.glyphs[c][arabicForm];
2226 | if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
2227 | }
2228 | }
2229 | else {
2230 | glyph = font.glyphs[c];
2231 | }
2232 | if (glyph == null) glyph = font.missingGlyph;
2233 | return glyph;
2234 | }
2235 |
2236 | this.renderChildren = function(ctx) {
2237 | var customFont = this.parent.style('font-family').getDefinition();
2238 | if (customFont != null) {
2239 | var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
2240 | var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
2241 | var text = this.getText();
2242 | if (customFont.isRTL) text = text.split("").reverse().join("");
2243 |
2244 | var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
2245 | for (var i=0; i 0) { return ''; }
2323 | return this.text;
2324 | }
2325 | }
2326 | svg.Element.tspan.prototype = new svg.Element.TextElementBase;
2327 |
2328 | // tref
2329 | svg.Element.tref = function(node) {
2330 | this.base = svg.Element.TextElementBase;
2331 | this.base(node);
2332 |
2333 | this.getText = function() {
2334 | var element = this.getHrefAttribute().getDefinition();
2335 | if (element != null) return element.children[0].getText();
2336 | }
2337 | }
2338 | svg.Element.tref.prototype = new svg.Element.TextElementBase;
2339 |
2340 | // a element
2341 | svg.Element.a = function(node) {
2342 | this.base = svg.Element.TextElementBase;
2343 | this.base(node);
2344 |
2345 | this.hasText = node.childNodes.length > 0;
2346 | for (var i=0; i 0) {
2365 | // render as temporary group
2366 | var g = new svg.Element.g();
2367 | g.children = this.children;
2368 | g.parent = this;
2369 | g.render(ctx);
2370 | }
2371 | }
2372 |
2373 | this.onclick = function() {
2374 | window.open(this.getHrefAttribute().value);
2375 | }
2376 |
2377 | this.onmousemove = function() {
2378 | svg.ctx.canvas.style.cursor = 'pointer';
2379 | }
2380 | }
2381 | svg.Element.a.prototype = new svg.Element.TextElementBase;
2382 |
2383 | // image element
2384 | svg.Element.image = function(node) {
2385 | this.base = svg.Element.RenderedElementBase;
2386 | this.base(node);
2387 |
2388 | var href = this.getHrefAttribute().value;
2389 | if (href == '') { return; }
2390 | var isSvg = href.match(/\.svg$/)
2391 |
2392 | svg.Images.push(this);
2393 | this.loaded = false;
2394 | if (!isSvg) {
2395 | this.img = document.createElement('img');
2396 | if (svg.opts['useCORS'] == true) { this.img.crossOrigin = 'Anonymous'; }
2397 | var self = this;
2398 | this.img.onload = function() { self.loaded = true; }
2399 | this.img.onerror = function() { svg.log('ERROR: image "' + href + '" not found'); self.loaded = true; }
2400 | this.img.src = href;
2401 | }
2402 | else {
2403 | this.img = svg.ajax(href);
2404 | this.loaded = true;
2405 | }
2406 |
2407 | this.renderChildren = function(ctx) {
2408 | var x = this.attribute('x').toPixels('x');
2409 | var y = this.attribute('y').toPixels('y');
2410 |
2411 | var width = this.attribute('width').toPixels('x');
2412 | var height = this.attribute('height').toPixels('y');
2413 | if (width == 0 || height == 0) return;
2414 |
2415 | ctx.save();
2416 | if (isSvg) {
2417 | ctx.drawSvg(this.img, x, y, width, height);
2418 | }
2419 | else {
2420 | ctx.translate(x, y);
2421 | svg.AspectRatio(ctx,
2422 | this.attribute('preserveAspectRatio').value,
2423 | width,
2424 | this.img.width,
2425 | height,
2426 | this.img.height,
2427 | 0,
2428 | 0);
2429 | ctx.drawImage(this.img, 0, 0);
2430 | }
2431 | ctx.restore();
2432 | }
2433 |
2434 | this.getBoundingBox = function() {
2435 | var x = this.attribute('x').toPixels('x');
2436 | var y = this.attribute('y').toPixels('y');
2437 | var width = this.attribute('width').toPixels('x');
2438 | var height = this.attribute('height').toPixels('y');
2439 | return new svg.BoundingBox(x, y, x + width, y + height);
2440 | }
2441 | }
2442 | svg.Element.image.prototype = new svg.Element.RenderedElementBase;
2443 |
2444 | // group element
2445 | svg.Element.g = function(node) {
2446 | this.base = svg.Element.RenderedElementBase;
2447 | this.base(node);
2448 |
2449 | this.getBoundingBox = function() {
2450 | var bb = new svg.BoundingBox();
2451 | for (var i=0; i 0) {
2507 | var urlStart = srcs[s].indexOf('url');
2508 | var urlEnd = srcs[s].indexOf(')', urlStart);
2509 | var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
2510 | var doc = svg.parseXml(svg.ajax(url));
2511 | var fonts = doc.getElementsByTagName('font');
2512 | for (var f=0; f
4 | * @link http://www.phpied.com/rgb-color-parser-in-javascript/
5 | * @license Use it if you like it
6 | */
7 |
8 | (function ( global ) {
9 |
10 | function RGBColor(color_string)
11 | {
12 | this.ok = false;
13 |
14 | // strip any leading #
15 | if (color_string.charAt(0) == '#') { // remove # if any
16 | color_string = color_string.substr(1,6);
17 | }
18 |
19 | color_string = color_string.replace(/ /g,'');
20 | color_string = color_string.toLowerCase();
21 |
22 | // before getting into regexps, try simple matches
23 | // and overwrite the input
24 | var simple_colors = {
25 | aliceblue: 'f0f8ff',
26 | antiquewhite: 'faebd7',
27 | aqua: '00ffff',
28 | aquamarine: '7fffd4',
29 | azure: 'f0ffff',
30 | beige: 'f5f5dc',
31 | bisque: 'ffe4c4',
32 | black: '000000',
33 | blanchedalmond: 'ffebcd',
34 | blue: '0000ff',
35 | blueviolet: '8a2be2',
36 | brown: 'a52a2a',
37 | burlywood: 'deb887',
38 | cadetblue: '5f9ea0',
39 | chartreuse: '7fff00',
40 | chocolate: 'd2691e',
41 | coral: 'ff7f50',
42 | cornflowerblue: '6495ed',
43 | cornsilk: 'fff8dc',
44 | crimson: 'dc143c',
45 | cyan: '00ffff',
46 | darkblue: '00008b',
47 | darkcyan: '008b8b',
48 | darkgoldenrod: 'b8860b',
49 | darkgray: 'a9a9a9',
50 | darkgreen: '006400',
51 | darkkhaki: 'bdb76b',
52 | darkmagenta: '8b008b',
53 | darkolivegreen: '556b2f',
54 | darkorange: 'ff8c00',
55 | darkorchid: '9932cc',
56 | darkred: '8b0000',
57 | darksalmon: 'e9967a',
58 | darkseagreen: '8fbc8f',
59 | darkslateblue: '483d8b',
60 | darkslategray: '2f4f4f',
61 | darkturquoise: '00ced1',
62 | darkviolet: '9400d3',
63 | deeppink: 'ff1493',
64 | deepskyblue: '00bfff',
65 | dimgray: '696969',
66 | dodgerblue: '1e90ff',
67 | feldspar: 'd19275',
68 | firebrick: 'b22222',
69 | floralwhite: 'fffaf0',
70 | forestgreen: '228b22',
71 | fuchsia: 'ff00ff',
72 | gainsboro: 'dcdcdc',
73 | ghostwhite: 'f8f8ff',
74 | gold: 'ffd700',
75 | goldenrod: 'daa520',
76 | gray: '808080',
77 | green: '008000',
78 | greenyellow: 'adff2f',
79 | honeydew: 'f0fff0',
80 | hotpink: 'ff69b4',
81 | indianred : 'cd5c5c',
82 | indigo : '4b0082',
83 | ivory: 'fffff0',
84 | khaki: 'f0e68c',
85 | lavender: 'e6e6fa',
86 | lavenderblush: 'fff0f5',
87 | lawngreen: '7cfc00',
88 | lemonchiffon: 'fffacd',
89 | lightblue: 'add8e6',
90 | lightcoral: 'f08080',
91 | lightcyan: 'e0ffff',
92 | lightgoldenrodyellow: 'fafad2',
93 | lightgrey: 'd3d3d3',
94 | lightgreen: '90ee90',
95 | lightpink: 'ffb6c1',
96 | lightsalmon: 'ffa07a',
97 | lightseagreen: '20b2aa',
98 | lightskyblue: '87cefa',
99 | lightslateblue: '8470ff',
100 | lightslategray: '778899',
101 | lightsteelblue: 'b0c4de',
102 | lightyellow: 'ffffe0',
103 | lime: '00ff00',
104 | limegreen: '32cd32',
105 | linen: 'faf0e6',
106 | magenta: 'ff00ff',
107 | maroon: '800000',
108 | mediumaquamarine: '66cdaa',
109 | mediumblue: '0000cd',
110 | mediumorchid: 'ba55d3',
111 | mediumpurple: '9370d8',
112 | mediumseagreen: '3cb371',
113 | mediumslateblue: '7b68ee',
114 | mediumspringgreen: '00fa9a',
115 | mediumturquoise: '48d1cc',
116 | mediumvioletred: 'c71585',
117 | midnightblue: '191970',
118 | mintcream: 'f5fffa',
119 | mistyrose: 'ffe4e1',
120 | moccasin: 'ffe4b5',
121 | navajowhite: 'ffdead',
122 | navy: '000080',
123 | oldlace: 'fdf5e6',
124 | olive: '808000',
125 | olivedrab: '6b8e23',
126 | orange: 'ffa500',
127 | orangered: 'ff4500',
128 | orchid: 'da70d6',
129 | palegoldenrod: 'eee8aa',
130 | palegreen: '98fb98',
131 | paleturquoise: 'afeeee',
132 | palevioletred: 'd87093',
133 | papayawhip: 'ffefd5',
134 | peachpuff: 'ffdab9',
135 | peru: 'cd853f',
136 | pink: 'ffc0cb',
137 | plum: 'dda0dd',
138 | powderblue: 'b0e0e6',
139 | purple: '800080',
140 | red: 'ff0000',
141 | rosybrown: 'bc8f8f',
142 | royalblue: '4169e1',
143 | saddlebrown: '8b4513',
144 | salmon: 'fa8072',
145 | sandybrown: 'f4a460',
146 | seagreen: '2e8b57',
147 | seashell: 'fff5ee',
148 | sienna: 'a0522d',
149 | silver: 'c0c0c0',
150 | skyblue: '87ceeb',
151 | slateblue: '6a5acd',
152 | slategray: '708090',
153 | snow: 'fffafa',
154 | springgreen: '00ff7f',
155 | steelblue: '4682b4',
156 | tan: 'd2b48c',
157 | teal: '008080',
158 | thistle: 'd8bfd8',
159 | tomato: 'ff6347',
160 | turquoise: '40e0d0',
161 | violet: 'ee82ee',
162 | violetred: 'd02090',
163 | wheat: 'f5deb3',
164 | white: 'ffffff',
165 | whitesmoke: 'f5f5f5',
166 | yellow: 'ffff00',
167 | yellowgreen: '9acd32'
168 | };
169 | for (var key in simple_colors) {
170 | if (color_string == key) {
171 | color_string = simple_colors[key];
172 | }
173 | }
174 | // emd of simple type-in colors
175 |
176 | // array of color definition objects
177 | var color_defs = [
178 | {
179 | re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
180 | example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
181 | process: function (bits){
182 | return [
183 | parseInt(bits[1]),
184 | parseInt(bits[2]),
185 | parseInt(bits[3])
186 | ];
187 | }
188 | },
189 | {
190 | re: /^(\w{2})(\w{2})(\w{2})$/,
191 | example: ['#00ff00', '336699'],
192 | process: function (bits){
193 | return [
194 | parseInt(bits[1], 16),
195 | parseInt(bits[2], 16),
196 | parseInt(bits[3], 16)
197 | ];
198 | }
199 | },
200 | {
201 | re: /^(\w{1})(\w{1})(\w{1})$/,
202 | example: ['#fb0', 'f0f'],
203 | process: function (bits){
204 | return [
205 | parseInt(bits[1] + bits[1], 16),
206 | parseInt(bits[2] + bits[2], 16),
207 | parseInt(bits[3] + bits[3], 16)
208 | ];
209 | }
210 | }
211 | ];
212 |
213 | // search through the definitions to find a match
214 | for (var i = 0; i < color_defs.length; i++) {
215 | var re = color_defs[i].re;
216 | var processor = color_defs[i].process;
217 | var bits = re.exec(color_string);
218 | if (bits) {
219 | channels = processor(bits);
220 | this.r = channels[0];
221 | this.g = channels[1];
222 | this.b = channels[2];
223 | this.ok = true;
224 | }
225 |
226 | }
227 |
228 | // validate/cleanup values
229 | this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
230 | this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
231 | this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
232 |
233 | // some getters
234 | this.toRGB = function () {
235 | return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
236 | }
237 | this.toHex = function () {
238 | var r = this.r.toString(16);
239 | var g = this.g.toString(16);
240 | var b = this.b.toString(16);
241 | if (r.length == 1) r = '0' + r;
242 | if (g.length == 1) g = '0' + g;
243 | if (b.length == 1) b = '0' + b;
244 | return '#' + r + g + b;
245 | }
246 |
247 | // help
248 | this.getHelpXML = function () {
249 |
250 | var examples = new Array();
251 | // add regexps
252 | for (var i = 0; i < color_defs.length; i++) {
253 | var example = color_defs[i].example;
254 | for (var j = 0; j < example.length; j++) {
255 | examples[examples.length] = example[j];
256 | }
257 | }
258 | // add type-in colors
259 | for (var sc in simple_colors) {
260 | examples[examples.length] = sc;
261 | }
262 |
263 | var xml = document.createElement('ul');
264 | xml.setAttribute('id', 'rgbcolor-examples');
265 | for (var i = 0; i < examples.length; i++) {
266 | try {
267 | var list_item = document.createElement('li');
268 | var list_color = new RGBColor(examples[i]);
269 | var example_div = document.createElement('div');
270 | example_div.style.cssText =
271 | 'margin: 3px; '
272 | + 'border: 1px solid black; '
273 | + 'background:' + list_color.toHex() + '; '
274 | + 'color:' + list_color.toHex()
275 | ;
276 | example_div.appendChild(document.createTextNode('test'));
277 | var list_item_value = document.createTextNode(
278 | ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
279 | );
280 | list_item.appendChild(example_div);
281 | list_item.appendChild(list_item_value);
282 | xml.appendChild(list_item);
283 |
284 | } catch(e){}
285 | }
286 | return xml;
287 |
288 | }
289 |
290 | }
291 |
292 | // export as AMD...
293 | if ( typeof define !== 'undefined' && define.amd ) {
294 | define( function () { return RGBColor; });
295 | }
296 |
297 | // ...or as browserify
298 | else if ( typeof module !== 'undefined' && module.exports ) {
299 | module.exports = RGBColor;
300 | }
301 |
302 | global.RGBColor = RGBColor;
303 |
304 | }( typeof window !== 'undefined' ? window : this ));
305 |
--------------------------------------------------------------------------------
/phosphorus.sublime-project:
--------------------------------------------------------------------------------
1 | {
2 | "folders":
3 | [
4 | {
5 | "follow_symlinks": true,
6 | "path": "."
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/player.css:
--------------------------------------------------------------------------------
1 | .progress-bar {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | bottom: 0;
6 | width: 0;
7 | background: #cde;
8 | -webkit-transition: .3s;
9 | -moz-transition: .3s;
10 | -o-transition: .3s;
11 | transition: .3s;
12 | }
13 | .light-content .progress-bar {
14 | background: #468;
15 | }
16 | .progress-bar.error {
17 | background: #ecc;
18 | }
19 | .light-content .progress-bar.error {
20 | background: #844;
21 | }
22 | .controls {
23 | position: relative;
24 | height: 32px;
25 | }
26 | .controls span,
27 | .project-link {
28 | width: 32px;
29 | height: 32px;
30 | float: right;
31 | cursor: pointer;
32 | text-align: center;
33 | opacity: .4;
34 | background-image: url(icons.svg);
35 | text-decoration: none;
36 | }
37 | .controls .flag {
38 | background-position: 0 0;
39 | }
40 | .controls .stop {
41 | background-position: -96px 0;
42 | }
43 | .controls .pause {
44 | background-position: -32px 0;
45 | }
46 | .controls .play {
47 | background-position: -64px 0;
48 | }
49 | .controls .full-screen {
50 | float: left;
51 | background-position: -128px 0;
52 | }
53 | .controls span:active,
54 | .project-link:active {
55 | opacity: 1 !important;
56 | }
57 | .controls .turbo {
58 | float: right;
59 | display: none;
60 | cursor: default;
61 | color: rgba(0, 0, 0, .4);
62 | font: 500 12px/32px Helvetica Neue, Helvetica, Arial, sans-serif;
63 | padding: 0 8px;
64 | }
65 | .player {
66 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .4);
67 | width: 480px;
68 | height: 360px;
69 | position: relative;
70 | -webkit-transform-origin: 0 0;
71 | -moz-transform-origin: 0 0;
72 | -ms-transform-origin: 0 0;
73 | -o-transform-origin: 0 0;
74 | transform-origin: 0 0;
75 | }
76 | .light-content .player {
77 | box-shadow: 0 0 0 1px rgba(255, 255, 255, .4);
78 | }
79 |
80 | .internal-error {
81 | color: rgba(128, 0, 0, .6);
82 | font: 500 12px Helvetica Neue, Helvetica, Arial, sans-serif;
83 | padding: 8px;
84 | display: none;
85 | }
86 | .internal-error a {
87 | color: rgba(128, 0, 0, .8);
88 | }
89 |
90 | .fs {
91 | background: #000;
92 | width: 100%;
93 | height: 100%;
94 | }
95 | .fs body {
96 | margin: 0;
97 | padding: 0;
98 | overflow: hidden;
99 | }
100 | .fs .title,
101 | .fs section {
102 | display: none;
103 | }
104 | .fs .controls span,
105 | .light-content .controls span {
106 | background-position-y: -32px;
107 | opacity: .6;
108 | }
109 | .light-content .controls .full-screen {
110 | background-position-y: -64px;
111 | }
112 | .fs .light-content .controls .full-screen {
113 | background-position-y: -32px;
114 | }
115 | .fs .controls .turbo,
116 | .light-content .controls .turbo {
117 | color: rgba(255, 255, 255, .6);
118 | }
119 | .fs .player {
120 | box-shadow: none;
121 | }
122 |
--------------------------------------------------------------------------------
/player.js:
--------------------------------------------------------------------------------
1 | P.player = (function() {
2 | 'use strict';
3 |
4 | var stage;
5 | var frameId = null;
6 | var isFullScreen = false;
7 |
8 | var progressBar = document.querySelector('.progress-bar');
9 | var player = document.querySelector('.player');
10 | var projectLink = document.querySelector('.project-link');
11 | var bugLink = document.querySelector('#bug-link');
12 |
13 | var controls = document.querySelector('.controls');
14 | var flag = document.querySelector('.flag');
15 | var turbo = document.querySelector('.turbo');
16 | var pause = document.querySelector('.pause');
17 | var stop = document.querySelector('.stop');
18 | var fullScreen = document.querySelector('.full-screen');
19 |
20 | var error = document.querySelector('.internal-error');
21 | var errorBugLink = document.querySelector('#error-bug-link');
22 |
23 | var flagTouchTimeout;
24 | function flagTouchStart() {
25 | flagTouchTimeout = setTimeout(function() {
26 | turboClick();
27 | flagTouchTimeout = true;
28 | }, 500);
29 | }
30 | function turboClick() {
31 | stage.isTurbo = !stage.isTurbo;
32 | flag.title = stage.isTurbo ? 'Turbo mode enabled. Shift+click to disable.' : 'Shift+click to enable turbo mode.';
33 | turbo.style.display = stage.isTurbo ? 'block' : 'none';
34 | }
35 | function flagClick(e) {
36 | if (!stage) return;
37 | if (flagTouchTimeout === true) return;
38 | if (flagTouchTimeout) {
39 | clearTimeout(flagTouchTimeout);
40 | }
41 | if (e.shiftKey) {
42 | turboClick();
43 | } else {
44 | stage.start();
45 | pause.className = 'pause';
46 | stage.stopAll();
47 | stage.triggerGreenFlag();
48 | }
49 | stage.focus();
50 | e.preventDefault();
51 | }
52 |
53 | function pauseClick(e) {
54 | if (!stage) return;
55 | if (stage.isRunning) {
56 | stage.pause();
57 | pause.className = 'play';
58 | } else {
59 | stage.start();
60 | pause.className = 'pause';
61 | }
62 | stage.focus();
63 | e.preventDefault();
64 | }
65 |
66 | function stopClick(e) {
67 | if (!stage) return;
68 | stage.start();
69 | pause.className = 'pause';
70 | stage.stopAll();
71 | stage.focus();
72 | e.preventDefault();
73 | }
74 |
75 | function fullScreenClick(e) {
76 | if (e) e.preventDefault();
77 | if (!stage) return;
78 | document.documentElement.classList.toggle('fs');
79 | isFullScreen = !isFullScreen;
80 | if (!e || !e.shiftKey) {
81 | if (isFullScreen) {
82 | var el = document.documentElement;
83 | if (el.requestFullScreenWithKeys) {
84 | el.requestFullScreenWithKeys();
85 | } else if (el.webkitRequestFullScreen) {
86 | el.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
87 | }
88 | } else {
89 | if (document.exitFullscreen) {
90 | document.exitFullscreen();
91 | } else if (document.mozCancelFullScreen) {
92 | document.mozCancelFullScreen();
93 | } else if (document.webkitCancelFullScreen) {
94 | document.webkitCancelFullScreen();
95 | }
96 | }
97 | }
98 | if (!isFullScreen) {
99 | document.body.style.width =
100 | document.body.style.height =
101 | document.body.style.marginLeft =
102 | document.body.style.marginTop = '';
103 | }
104 | updateFullScreen();
105 | if (!stage.isRunning) {
106 | stage.draw();
107 | }
108 | stage.focus();
109 | }
110 |
111 | function exitFullScreen(e) {
112 | if (isFullScreen && e.keyCode === 27) {
113 | fullScreenClick(e);
114 | }
115 | }
116 |
117 | function updateFullScreen() {
118 | if (!stage) return;
119 | if (isFullScreen) {
120 | window.scrollTo(0, 0);
121 | var padding = 8;
122 | var w = window.innerWidth - padding * 2;
123 | var h = window.innerHeight - padding - controls.offsetHeight;
124 | w = Math.min(w, h / .75);
125 | h = w * .75 + controls.offsetHeight;
126 | document.body.style.width = w + 'px';
127 | document.body.style.height = h + 'px';
128 | document.body.style.marginLeft = (window.innerWidth - w) / 2 + 'px';
129 | document.body.style.marginTop = (window.innerHeight - h - padding) / 2 + 'px';
130 | stage.setZoom(w / 480);
131 | } else {
132 | stage.setZoom(1);
133 | }
134 | }
135 |
136 | function preventDefault(e) {
137 | e.preventDefault();
138 | }
139 |
140 | window.addEventListener('resize', updateFullScreen);
141 |
142 | if (P.hasTouchEvents) {
143 | flag.addEventListener('touchstart', flagTouchStart);
144 | flag.addEventListener('touchend', flagClick);
145 | pause.addEventListener('touchend', pauseClick);
146 | stop.addEventListener('touchend', stopClick);
147 | fullScreen.addEventListener('touchend', fullScreenClick);
148 |
149 | flag.addEventListener('touchstart', preventDefault);
150 | pause.addEventListener('touchstart', preventDefault);
151 | stop.addEventListener('touchstart', preventDefault);
152 | fullScreen.addEventListener('touchstart', preventDefault);
153 |
154 | document.addEventListener('touchmove', function(e) {
155 | if (isFullScreen) e.preventDefault();
156 | });
157 | } else {
158 | flag.addEventListener('click', flagClick);
159 | pause.addEventListener('click', pauseClick);
160 | stop.addEventListener('click', stopClick);
161 | fullScreen.addEventListener('click', fullScreenClick);
162 | }
163 |
164 | document.addEventListener("fullscreenchange", function () {
165 | if (isFullScreen !== document.fullscreen) fullScreenClick();
166 | });
167 | document.addEventListener("mozfullscreenchange", function () {
168 | if (isFullScreen !== document.mozFullScreen) fullScreenClick();
169 | });
170 | document.addEventListener("webkitfullscreenchange", function () {
171 | if (isFullScreen !== document.webkitIsFullScreen) fullScreenClick();
172 | });
173 |
174 | function load(id, cb, titleCallback) {
175 | P.player.projectId = id;
176 | P.player.projectURL = id ? 'https://scratch.mit.edu/projects/' + id + '/' : '';
177 |
178 | if (stage) stage.destroy();
179 | while (player.firstChild) player.removeChild(player.lastChild);
180 | turbo.style.display = 'none';
181 | error.style.display = 'none';
182 | pause.className = 'pause';
183 | progressBar.style.display = 'none';
184 |
185 | if (id) {
186 | showProgress(P.IO.loadScratchr2Project(id), cb);
187 | P.IO.loadScratchr2ProjectTitle(id, function(title) {
188 | if (titleCallback) titleCallback(P.player.projectTitle = title);
189 | });
190 | } else {
191 | if (titleCallback) setTimeout(function() {
192 | titleCallback('');
193 | });
194 | }
195 | }
196 |
197 | function showError(e) {
198 | error.style.display = 'block';
199 | errorBugLink.href = 'https://github.com/nathan/phosphorus/issues/new?title=' + encodeURIComponent(P.player.projectTitle || P.player.projectURL) + '&body=' + encodeURIComponent('\n\n\n' + P.player.projectURL + '\nhttp://phosphorus.github.io/#' + P.player.projectId + '\n' + navigator.userAgent + (e.stack ? '\n\n```\n' + e.stack + '\n```' : ''));
200 | console.error(e.stack);
201 | }
202 |
203 | function showProgress(request, loadCallback) {
204 | progressBar.style.display = 'none';
205 | setTimeout(function() {
206 | progressBar.style.width = '10%';
207 | progressBar.className = 'progress-bar';
208 | progressBar.style.opacity = 1;
209 | progressBar.style.display = 'block';
210 | });
211 | request.onload = function(s) {
212 | progressBar.style.width = '100%';
213 | setTimeout(function() {
214 | progressBar.style.opacity = 0;
215 | setTimeout(function() {
216 | progressBar.style.display = 'none';
217 | }, 300);
218 | }, 100);
219 |
220 | var zoom = stage ? stage.zoom : 1;
221 | window.stage = stage = s;
222 | stage.start();
223 | stage.setZoom(zoom);
224 |
225 | stage.root.addEventListener('keydown', exitFullScreen);
226 | stage.handleError = showError;
227 |
228 | player.appendChild(stage.root);
229 | stage.focus();
230 | if (loadCallback) {
231 | loadCallback(stage);
232 | loadCallback = null;
233 | }
234 | };
235 | request.onerror = function(e) {
236 | progressBar.style.width = '100%';
237 | progressBar.className = 'progress-bar error';
238 | console.error(e.stack);
239 | };
240 | request.onprogress = function(e) {
241 | progressBar.style.width = (10 + e.loaded / e.total * 90) + '%';
242 | };
243 | }
244 |
245 | return {
246 | load: load,
247 | showProgress: showProgress
248 | };
249 |
250 | }());
251 |
--------------------------------------------------------------------------------