httpng 0.1.0

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.
@@ -0,0 +1,2795 @@
1
+ /**
2
+ @license html2canvas v0.33 <http://html2canvas.hertzen.com>
3
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
4
+ http://www.twitter.com/niklasvh
5
+
6
+ Released under MIT License
7
+ */
8
+ (function(window, document, undefined){
9
+ /*
10
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
11
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
12
+ http://www.twitter.com/niklasvh
13
+
14
+ Released under MIT License
15
+ */
16
+ "use strict";
17
+
18
+ var _html2canvas = {},
19
+ html2canvas;
20
+
21
+
22
+ function h2clog(a) {
23
+ if (_html2canvas.logging && window.console && window.console.log) {
24
+ window.console.log(a);
25
+ }
26
+ }
27
+
28
+ _html2canvas.Util = {};
29
+
30
+ _html2canvas.Util.backgroundImage = function (src) {
31
+
32
+ if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
33
+ return src;
34
+ }
35
+
36
+ if (src.toLowerCase().substr( 0, 5 ) === 'url("') {
37
+ src = src.substr( 5 );
38
+ src = src.substr( 0, src.length - 2 );
39
+ } else {
40
+ src = src.substr( 4 );
41
+ src = src.substr( 0, src.length - 1 );
42
+ }
43
+
44
+ return src;
45
+ };
46
+
47
+ _html2canvas.Util.Bounds = function getBounds (el) {
48
+ var clientRect,
49
+ bounds = {};
50
+
51
+ if (el.getBoundingClientRect){
52
+ clientRect = el.getBoundingClientRect();
53
+
54
+
55
+ // TODO add scroll position to bounds, so no scrolling of window necessary
56
+ bounds.top = clientRect.top;
57
+ bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
58
+ bounds.left = clientRect.left;
59
+
60
+ // older IE doesn't have width/height, but top/bottom instead
61
+ bounds.width = clientRect.width || (clientRect.right - clientRect.left);
62
+ bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
63
+
64
+ return bounds;
65
+
66
+ }
67
+ };
68
+
69
+ _html2canvas.Util.getCSS = function (el, attribute) {
70
+ // return $(el).css(attribute);
71
+
72
+ var val;
73
+
74
+ function toPX( attribute, val ) {
75
+ var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
76
+ left,
77
+ style = el.style;
78
+
79
+ // Check if we are not dealing with pixels, (Opera has issues with this)
80
+ // Ported from jQuery css.js
81
+ // From the awesome hack by Dean Edwards
82
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
83
+
84
+ // If we're not dealing with a regular pixel number
85
+ // but a number that has a weird ending, we need to convert it to pixels
86
+
87
+ if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {
88
+
89
+ // Remember the original values
90
+ left = style.left;
91
+
92
+ // Put in the new values to get a computed value out
93
+ if ( rsLeft ) {
94
+ el.runtimeStyle.left = el.currentStyle.left;
95
+ }
96
+ style.left = attribute === "fontSize" ? "1em" : (val || 0);
97
+ val = style.pixelLeft + "px";
98
+
99
+ // Revert the changed values
100
+ style.left = left;
101
+ if ( rsLeft ) {
102
+ el.runtimeStyle.left = rsLeft;
103
+ }
104
+
105
+ }
106
+
107
+ if (!/^(thin|medium|thick)$/i.test( val )) {
108
+ return Math.round(parseFloat( val )) + "px";
109
+ }
110
+
111
+ return val;
112
+
113
+ }
114
+
115
+
116
+ if ( window.getComputedStyle ) {
117
+ val = document.defaultView.getComputedStyle(el, null)[ attribute ];
118
+
119
+ if ( attribute === "backgroundPosition" ) {
120
+
121
+ val = (val.split(",")[0] || "0 0").split(" ");
122
+
123
+ val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ];
124
+ val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always
125
+ val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ];
126
+ }
127
+
128
+ } else if ( el.currentStyle ) {
129
+ // IE 9>
130
+ if (attribute === "backgroundPosition") {
131
+ // Older IE uses -x and -y
132
+ val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ];
133
+ } else {
134
+
135
+ val = toPX( attribute, el.currentStyle[ attribute ] );
136
+
137
+ if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) {
138
+ switch (val) {
139
+ case "thin":
140
+ val = "1px";
141
+ break;
142
+ case "medium":
143
+ val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around
144
+ break;
145
+ case "thick":
146
+ val = "5px";
147
+ break;
148
+ }
149
+ }
150
+ }
151
+
152
+
153
+
154
+ }
155
+
156
+
157
+
158
+
159
+ return val;
160
+
161
+
162
+
163
+ //return $(el).css(attribute);
164
+
165
+
166
+ };
167
+
168
+
169
+ _html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) {
170
+ // TODO add support for multi image backgrounds
171
+
172
+ var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) ,
173
+ topPos,
174
+ left,
175
+ percentage,
176
+ val;
177
+
178
+ if (bgposition.length === 1){
179
+ val = bgposition;
180
+
181
+ bgposition = [];
182
+
183
+ bgposition[0] = val;
184
+ bgposition[1] = val;
185
+ }
186
+
187
+
188
+
189
+ if (bgposition[0].toString().indexOf("%") !== -1){
190
+ percentage = (parseFloat(bgposition[0])/100);
191
+ left = ((bounds.width * percentage)-(image.width*percentage));
192
+
193
+ }else{
194
+ left = parseInt(bgposition[0],10);
195
+ }
196
+
197
+ if (bgposition[1].toString().indexOf("%") !== -1){
198
+
199
+ percentage = (parseFloat(bgposition[1])/100);
200
+ topPos = ((bounds.height * percentage)-(image.height*percentage));
201
+ }else{
202
+ topPos = parseInt(bgposition[1],10);
203
+ }
204
+
205
+
206
+
207
+
208
+ return {
209
+ top: topPos,
210
+ left: left
211
+ };
212
+
213
+ };
214
+
215
+ _html2canvas.Util.Extend = function (options, defaults) {
216
+ for (var key in options) {
217
+ if (options.hasOwnProperty(key)) {
218
+ defaults[key] = options[key];
219
+ }
220
+ }
221
+ return defaults;
222
+ };
223
+
224
+ _html2canvas.Util.Children = function(el) {
225
+ // $(el).contents() !== el.childNodes, Opera / IE have issues with that
226
+ var children;
227
+ try {
228
+ children = $(el).contents();
229
+ //children = (el.nodeName && el.nodeName.toUpperCase() === "IFRAME") ? el.contentDocument || el.contentWindow.document : el.childNodes ;
230
+
231
+ } catch (ex) {
232
+ h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
233
+ children = [];
234
+ }
235
+ return children;
236
+ };
237
+ /*
238
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
239
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
240
+ http://www.twitter.com/niklasvh
241
+
242
+ Contributor(s):
243
+ Niklas von Hertzen <http://www.twitter.com/niklasvh>
244
+ André Fiedler <http://www.twitter.com/sonnenkiste>
245
+
246
+ Released under MIT License
247
+ */
248
+
249
+ (function(){
250
+
251
+ _html2canvas.Generate = {};
252
+
253
+ var reGradients = [
254
+ /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
255
+ /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
256
+ /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)-]+)\)$/,
257
+ /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/,
258
+ /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/,
259
+ /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z-]*)([\w\d\.\s,%\(\)]+)\)$/,
260
+ /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/
261
+ ];
262
+
263
+ /*
264
+ * TODO: Add IE10 vendor prefix (-ms) support
265
+ * TODO: Add W3C gradient (linear-gradient) support
266
+ * TODO: Add old Webkit -webkit-gradient(radial, ...) support
267
+ * TODO: Maybe some RegExp optimizations are possible ;o)
268
+ */
269
+ _html2canvas.Generate.parseGradient = function(css, bounds) {
270
+ var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3;
271
+
272
+ for(i = 0; i < len; i+=1){
273
+ m1 = css.match(reGradients[i]);
274
+ if(m1) break;
275
+ }
276
+
277
+ if(m1) {
278
+ switch(m1[1]) {
279
+ case '-webkit-linear-gradient':
280
+ case '-o-linear-gradient':
281
+
282
+ gradient = {
283
+ type: 'linear',
284
+ x0: null,
285
+ y0: null,
286
+ x1: null,
287
+ y1: null,
288
+ colorStops: []
289
+ };
290
+
291
+ // get coordinates
292
+ m2 = m1[2].match(/\w+/g);
293
+ if(m2){
294
+ m2Len = m2.length;
295
+ for(i = 0; i < m2Len; i+=1){
296
+ switch(m2[i]) {
297
+ case 'top':
298
+ gradient.y0 = 0;
299
+ gradient.y1 = bounds.height;
300
+ break;
301
+
302
+ case 'right':
303
+ gradient.x0 = bounds.width;
304
+ gradient.x1 = 0;
305
+ break;
306
+
307
+ case 'bottom':
308
+ gradient.y0 = bounds.height;
309
+ gradient.y1 = 0;
310
+ break;
311
+
312
+ case 'left':
313
+ gradient.x0 = 0;
314
+ gradient.x1 = bounds.width;
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ if(gradient.x0 === null && gradient.x1 === null){ // center
320
+ gradient.x0 = gradient.x1 = bounds.width / 2;
321
+ }
322
+ if(gradient.y0 === null && gradient.y1 === null){ // center
323
+ gradient.y0 = gradient.y1 = bounds.height / 2;
324
+ }
325
+
326
+ // get colors and stops
327
+ m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
328
+ if(m2){
329
+ m2Len = m2.length;
330
+ step = 1 / Math.max(m2Len - 1, 1);
331
+ for(i = 0; i < m2Len; i+=1){
332
+ m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
333
+ if(m3[2]){
334
+ stop = parseFloat(m3[2]);
335
+ if(m3[3] === '%'){
336
+ stop /= 100;
337
+ } else { // px - stupid opera
338
+ stop /= bounds.width;
339
+ }
340
+ } else {
341
+ stop = i * step;
342
+ }
343
+ gradient.colorStops.push({
344
+ color: m3[1],
345
+ stop: stop
346
+ });
347
+ }
348
+ }
349
+ break;
350
+
351
+ case '-webkit-gradient':
352
+
353
+ gradient = {
354
+ type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions
355
+ x0: 0,
356
+ y0: 0,
357
+ x1: 0,
358
+ y1: 0,
359
+ colorStops: []
360
+ };
361
+
362
+ // get coordinates
363
+ m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
364
+ if(m2){
365
+ gradient.x0 = (m2[1] * bounds.width) / 100;
366
+ gradient.y0 = (m2[2] * bounds.height) / 100;
367
+ gradient.x1 = (m2[3] * bounds.width) / 100;
368
+ gradient.y1 = (m2[4] * bounds.height) / 100;
369
+ }
370
+
371
+ // get colors and stops
372
+ m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
373
+ if(m2){
374
+ m2Len = m2.length;
375
+ for(i = 0; i < m2Len; i+=1){
376
+ m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
377
+ stop = parseFloat(m3[2]);
378
+ if(m3[1] === 'from') stop = 0.0;
379
+ if(m3[1] === 'to') stop = 1.0;
380
+ gradient.colorStops.push({
381
+ color: m3[3],
382
+ stop: stop
383
+ });
384
+ }
385
+ }
386
+ break;
387
+
388
+ case '-moz-linear-gradient':
389
+
390
+ gradient = {
391
+ type: 'linear',
392
+ x0: 0,
393
+ y0: 0,
394
+ x1: 0,
395
+ y1: 0,
396
+ colorStops: []
397
+ };
398
+
399
+ // get coordinates
400
+ m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
401
+
402
+ // m2[1] == 0% -> left
403
+ // m2[1] == 50% -> center
404
+ // m2[1] == 100% -> right
405
+
406
+ // m2[2] == 0% -> top
407
+ // m2[2] == 50% -> center
408
+ // m2[2] == 100% -> bottom
409
+
410
+ if(m2){
411
+ gradient.x0 = (m2[1] * bounds.width) / 100;
412
+ gradient.y0 = (m2[2] * bounds.height) / 100;
413
+ gradient.x1 = bounds.width - gradient.x0;
414
+ gradient.y1 = bounds.height - gradient.y0;
415
+ }
416
+
417
+ // get colors and stops
418
+ m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
419
+ if(m2){
420
+ m2Len = m2.length;
421
+ step = 1 / Math.max(m2Len - 1, 1);
422
+ for(i = 0; i < m2Len; i+=1){
423
+ m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
424
+ if(m3[2]){
425
+ stop = parseFloat(m3[2]);
426
+ if(m3[3]){ // percentage
427
+ stop /= 100;
428
+ }
429
+ } else {
430
+ stop = i * step;
431
+ }
432
+ gradient.colorStops.push({
433
+ color: m3[1],
434
+ stop: stop
435
+ });
436
+ }
437
+ }
438
+ break;
439
+
440
+ case '-webkit-radial-gradient':
441
+ case '-moz-radial-gradient':
442
+ case '-o-radial-gradient':
443
+
444
+ gradient = {
445
+ type: 'circle',
446
+ x0: 0,
447
+ y0: 0,
448
+ x1: bounds.width,
449
+ y1: bounds.height,
450
+ cx: 0,
451
+ cy: 0,
452
+ rx: 0,
453
+ ry: 0,
454
+ colorStops: []
455
+ };
456
+
457
+ // center
458
+ m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
459
+ if(m2){
460
+ gradient.cx = (m2[1] * bounds.width) / 100;
461
+ gradient.cy = (m2[2] * bounds.height) / 100;
462
+ }
463
+
464
+ // size
465
+ m2 = m1[3].match(/\w+/);
466
+ m3 = m1[4].match(/[a-z-]*/);
467
+ if(m2 && m3){
468
+ switch(m3[0]){
469
+ case 'farthest-corner':
470
+ case 'cover': // is equivalent to farthest-corner
471
+ case '': // mozilla removes "cover" from definition :(
472
+ var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
473
+ var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
474
+ var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
475
+ var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
476
+ gradient.rx = gradient.ry = Math.max(tl, tr, br, bl);
477
+ break;
478
+ case 'closest-corner':
479
+ var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
480
+ var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
481
+ var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
482
+ var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
483
+ gradient.rx = gradient.ry = Math.min(tl, tr, br, bl);
484
+ break;
485
+ case 'farthest-side':
486
+ if(m2[0] === 'circle'){
487
+ gradient.rx = gradient.ry = Math.max(
488
+ gradient.cx,
489
+ gradient.cy,
490
+ gradient.x1 - gradient.cx,
491
+ gradient.y1 - gradient.cy
492
+ );
493
+ } else { // ellipse
494
+
495
+ gradient.type = m2[0];
496
+
497
+ gradient.rx = Math.max(
498
+ gradient.cx,
499
+ gradient.x1 - gradient.cx
500
+ );
501
+ gradient.ry = Math.max(
502
+ gradient.cy,
503
+ gradient.y1 - gradient.cy
504
+ );
505
+ }
506
+ break;
507
+ case 'closest-side':
508
+ case 'contain': // is equivalent to closest-side
509
+ if(m2[0] === 'circle'){
510
+ gradient.rx = gradient.ry = Math.min(
511
+ gradient.cx,
512
+ gradient.cy,
513
+ gradient.x1 - gradient.cx,
514
+ gradient.y1 - gradient.cy
515
+ );
516
+ } else { // ellipse
517
+
518
+ gradient.type = m2[0];
519
+
520
+ gradient.rx = Math.min(
521
+ gradient.cx,
522
+ gradient.x1 - gradient.cx
523
+ );
524
+ gradient.ry = Math.min(
525
+ gradient.cy,
526
+ gradient.y1 - gradient.cy
527
+ );
528
+ }
529
+ break;
530
+
531
+ // TODO: add support for "30px 40px" sizes (webkit only)
532
+ }
533
+ }
534
+
535
+ // color stops
536
+ m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
537
+ if(m2){
538
+ m2Len = m2.length;
539
+ step = 1 / Math.max(m2Len - 1, 1);
540
+ for(i = 0; i < m2Len; i+=1){
541
+ m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
542
+ if(m3[2]){
543
+ stop = parseFloat(m3[2]);
544
+ if(m3[3] === '%'){
545
+ stop /= 100;
546
+ } else { // px - stupid opera
547
+ stop /= bounds.width;
548
+ }
549
+ } else {
550
+ stop = i * step;
551
+ }
552
+ gradient.colorStops.push({
553
+ color: m3[1],
554
+ stop: stop
555
+ });
556
+ }
557
+ }
558
+ break;
559
+ }
560
+ }
561
+
562
+ return gradient;
563
+ };
564
+
565
+ _html2canvas.Generate.Gradient = function(src, bounds) {
566
+ var canvas = document.createElement('canvas'),
567
+ ctx = canvas.getContext('2d'),
568
+ gradient, grad, i, len, img;
569
+
570
+ canvas.width = bounds.width;
571
+ canvas.height = bounds.height;
572
+
573
+ // TODO: add support for multi defined background gradients (like radial gradient example in background.html)
574
+ gradient = _html2canvas.Generate.parseGradient(src, bounds);
575
+
576
+ img = new Image();
577
+
578
+ if(gradient){
579
+ if(gradient.type === 'linear'){
580
+ grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);
581
+
582
+ for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
583
+ try {
584
+ grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
585
+ }
586
+ catch(e) {
587
+ h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
588
+ }
589
+ }
590
+
591
+ ctx.fillStyle = grad;
592
+ ctx.fillRect(0, 0, bounds.width, bounds.height);
593
+
594
+ img.src = canvas.toDataURL();
595
+ } else if(gradient.type === 'circle'){
596
+
597
+ grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx);
598
+
599
+ for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
600
+ try {
601
+ grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
602
+ }
603
+ catch(e) {
604
+ h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
605
+ }
606
+ }
607
+
608
+ ctx.fillStyle = grad;
609
+ ctx.fillRect(0, 0, bounds.width, bounds.height);
610
+
611
+ img.src = canvas.toDataURL();
612
+ } else if(gradient.type === 'ellipse'){
613
+
614
+ // draw circle
615
+ var canvasRadial = document.createElement('canvas'),
616
+ ctxRadial = canvasRadial.getContext('2d'),
617
+ ri = Math.max(gradient.rx, gradient.ry),
618
+ di = ri * 2, imgRadial;
619
+
620
+ canvasRadial.width = canvasRadial.height = di;
621
+
622
+ grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri);
623
+
624
+ for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
625
+ try {
626
+ grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
627
+ }
628
+ catch(e) {
629
+ h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
630
+ }
631
+ }
632
+
633
+ ctxRadial.fillStyle = grad;
634
+ ctxRadial.fillRect(0, 0, di, di);
635
+
636
+ ctx.fillStyle = gradient.colorStops[i - 1].color;
637
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
638
+
639
+ imgRadial = new Image();
640
+ imgRadial.onload = function() { // wait until the image is filled
641
+
642
+ // transform circle to ellipse
643
+ ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);
644
+
645
+ img.src = canvas.toDataURL();
646
+
647
+ }
648
+ imgRadial.src = canvasRadial.toDataURL();
649
+ }
650
+ }
651
+
652
+ return img;
653
+ };
654
+
655
+ _html2canvas.Generate.ListAlpha = function(number) {
656
+ var tmp = "",
657
+ modulus;
658
+
659
+ do {
660
+ modulus = number % 26;
661
+ tmp = String.fromCharCode((modulus) + 64) + tmp;
662
+ number = number / 26;
663
+ }while((number*26) > 26);
664
+
665
+ return tmp;
666
+ };
667
+
668
+ _html2canvas.Generate.ListRoman = function(number) {
669
+ var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
670
+ decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
671
+ roman = "",
672
+ v,
673
+ len = romanArray.length;
674
+
675
+ if (number <= 0 || number >= 4000) {
676
+ return number;
677
+ }
678
+
679
+ for (v=0; v < len; v+=1) {
680
+ while (number >= decimal[v]) {
681
+ number -= decimal[v];
682
+ roman += romanArray[v];
683
+ }
684
+ }
685
+
686
+ return roman;
687
+
688
+ };
689
+
690
+ })();
691
+ /*
692
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
693
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
694
+ http://www.twitter.com/niklasvh
695
+
696
+ Released under MIT License
697
+ */
698
+
699
+ /*
700
+ * New function for traversing elements
701
+ */
702
+
703
+ _html2canvas.Parse = function ( images, options ) {
704
+ window.scroll(0,0);
705
+
706
+ var support = {
707
+ rangeBounds: false,
708
+ svgRendering: options.svgRendering && (function( ){
709
+ var img = new Image(),
710
+ canvas = document.createElement("canvas"),
711
+ ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d");
712
+ if (ctx === false) {
713
+ // browser doesn't support canvas, good luck supporting SVG on canvas
714
+ return false;
715
+ }
716
+ canvas.width = canvas.height = 10;
717
+ img.src = [
718
+ "data:image/svg+xml,",
719
+ "<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>",
720
+ "<foreignObject width='10' height='10'>",
721
+ "<div xmlns='http://www.w3.org/1999/xhtml' style='width:10;height:10;'>",
722
+ "sup",
723
+ "</div>",
724
+ "</foreignObject>",
725
+ "</svg>"
726
+ ].join("");
727
+ try {
728
+ ctx.drawImage(img, 0, 0);
729
+ canvas.toDataURL();
730
+ } catch(e) {
731
+ return false;
732
+ }
733
+ h2clog('html2canvas: Parse: SVG powered rendering available');
734
+ return true;
735
+
736
+ })()
737
+ },
738
+ element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default
739
+ needReorder = false,
740
+ numDraws = 0,
741
+ fontData = {},
742
+ doc = element.ownerDocument,
743
+ ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"),
744
+ body = doc.body,
745
+ r,
746
+ testElement,
747
+ rangeBounds,
748
+ rangeHeight,
749
+ stack,
750
+ ctx,
751
+ docDim,
752
+ i,
753
+ children,
754
+ childrenLen;
755
+
756
+
757
+ function docSize(){
758
+
759
+ return {
760
+ width: Math.max(
761
+ Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
762
+ Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
763
+ Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
764
+ ),
765
+ height: Math.max(
766
+ Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
767
+ Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
768
+ Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
769
+ )
770
+ };
771
+
772
+ }
773
+
774
+ images = images || {};
775
+
776
+ // Test whether we can use ranges to measure bounding boxes
777
+ // Opera doesn't provide valid bounds.height/bottom even though it supports the method.
778
+
779
+
780
+ if (doc.createRange) {
781
+ r = doc.createRange();
782
+ //this.support.rangeBounds = new Boolean(r.getBoundingClientRect);
783
+ if (r.getBoundingClientRect){
784
+ testElement = doc.createElement('boundtest');
785
+ testElement.style.height = "123px";
786
+ testElement.style.display = "block";
787
+ body.appendChild(testElement);
788
+
789
+ r.selectNode(testElement);
790
+ rangeBounds = r.getBoundingClientRect();
791
+ rangeHeight = rangeBounds.height;
792
+
793
+ if (rangeHeight === 123) {
794
+ support.rangeBounds = true;
795
+ }
796
+ body.removeChild(testElement);
797
+
798
+
799
+ }
800
+
801
+ }
802
+
803
+
804
+ /*
805
+ var rootStack = new this.storageContext($(document).width(),$(document).height());
806
+ rootStack.opacity = this.getCSS(this.element,"opacity");
807
+ var stack = this.newElement(this.element,rootStack);
808
+
809
+
810
+ this.parseElement(this.element,stack);
811
+ */
812
+
813
+
814
+
815
+
816
+ var getCSS = _html2canvas.Util.getCSS;
817
+ function getCSSInt(element, attribute) {
818
+ var val = parseInt(getCSS(element, attribute), 10);
819
+ return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html
820
+ }
821
+
822
+ // Drawing a rectangle
823
+ function renderRect (ctx, x, y, w, h, bgcolor) {
824
+ if (bgcolor !=="transparent"){
825
+ ctx.setVariable("fillStyle", bgcolor);
826
+ ctx.fillRect (x, y, w, h);
827
+ numDraws+=1;
828
+ }
829
+ }
830
+
831
+
832
+ function textTransform (text, transform) {
833
+ switch(transform){
834
+ case "lowercase":
835
+ return text.toLowerCase();
836
+
837
+ case "capitalize":
838
+ return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) {
839
+ if (m.length > 0) {
840
+ return p1 + p2.toUpperCase();
841
+ }
842
+ } );
843
+
844
+ case "uppercase":
845
+ return text.toUpperCase();
846
+
847
+ default:
848
+ return text;
849
+
850
+ }
851
+
852
+ }
853
+
854
+ function trimText (text) {
855
+ return text.replace(/^\s*/g, "").replace(/\s*$/g, "");
856
+ }
857
+
858
+ function fontMetrics (font, fontSize) {
859
+
860
+ if (fontData[font + "-" + fontSize] !== undefined) {
861
+ return fontData[font + "-" + fontSize];
862
+ }
863
+
864
+
865
+ var container = doc.createElement('div'),
866
+ img = doc.createElement('img'),
867
+ span = doc.createElement('span'),
868
+ baseline,
869
+ middle,
870
+ metricsObj;
871
+
872
+
873
+ container.style.visibility = "hidden";
874
+ container.style.fontFamily = font;
875
+ container.style.fontSize = fontSize;
876
+ container.style.margin = 0;
877
+ container.style.padding = 0;
878
+
879
+ body.appendChild(container);
880
+
881
+
882
+
883
+ // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif)
884
+ img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=";
885
+ img.width = 1;
886
+ img.height = 1;
887
+
888
+ img.style.margin = 0;
889
+ img.style.padding = 0;
890
+ img.style.verticalAlign = "baseline";
891
+
892
+ span.style.fontFamily = font;
893
+ span.style.fontSize = fontSize;
894
+ span.style.margin = 0;
895
+ span.style.padding = 0;
896
+
897
+
898
+
899
+
900
+ span.appendChild(doc.createTextNode('Hidden Text'));
901
+ container.appendChild(span);
902
+ container.appendChild(img);
903
+ baseline = (img.offsetTop - span.offsetTop) + 1;
904
+
905
+ container.removeChild(span);
906
+ container.appendChild(doc.createTextNode('Hidden Text'));
907
+
908
+ container.style.lineHeight = "normal";
909
+ img.style.verticalAlign = "super";
910
+
911
+ middle = (img.offsetTop-container.offsetTop) + 1;
912
+ metricsObj = {
913
+ baseline: baseline,
914
+ lineWidth: 1,
915
+ middle: middle
916
+ };
917
+
918
+
919
+ fontData[font + "-" + fontSize] = metricsObj;
920
+
921
+ body.removeChild(container);
922
+
923
+ return metricsObj;
924
+
925
+ }
926
+
927
+
928
+ function drawText(currentText, x, y, ctx){
929
+ if (trimText(currentText).length>0) {
930
+ ctx.fillText(currentText,x,y);
931
+ numDraws+=1;
932
+ }
933
+ }
934
+
935
+
936
+ function renderText(el, textNode, stack) {
937
+ var ctx = stack.ctx,
938
+ family = getCSS(el, "fontFamily"),
939
+ size = getCSS(el, "fontSize"),
940
+ color = getCSS(el, "color"),
941
+ text_decoration = getCSS(el, "textDecoration"),
942
+ text_align = getCSS(el, "textAlign"),
943
+ letter_spacing = getCSS(el, "letterSpacing"),
944
+ bounds,
945
+ text,
946
+ metrics,
947
+ renderList,
948
+ listLen,
949
+ bold = getCSS(el, "fontWeight"),
950
+ font_style = getCSS(el, "fontStyle"),
951
+ font_variant = getCSS(el, "fontVariant"),
952
+ align = false,
953
+ newTextNode,
954
+ textValue,
955
+ textOffset = 0,
956
+ oldTextNode,
957
+ c,
958
+ range,
959
+ parent,
960
+ wrapElement,
961
+ backupText;
962
+
963
+ // apply text-transform:ation to the text
964
+
965
+
966
+
967
+ textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform"));
968
+ text = trimText(textNode.nodeValue);
969
+
970
+ if (text.length>0){
971
+
972
+ if (text_decoration !== "none"){
973
+ metrics = fontMetrics(family, size);
974
+ }
975
+
976
+ text_align = text_align.replace(["-webkit-auto"],["auto"]);
977
+
978
+ if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){
979
+ // this.setContextVariable(ctx,"textAlign",text_align);
980
+ renderList = textNode.nodeValue.split(/(\b| )/);
981
+
982
+ }else{
983
+ // this.setContextVariable(ctx,"textAlign","left");
984
+ renderList = textNode.nodeValue.split("");
985
+ }
986
+
987
+ switch(parseInt(bold, 10)){
988
+ case 401:
989
+ bold = "bold";
990
+ break;
991
+ case 400:
992
+ bold = "normal";
993
+ break;
994
+ }
995
+
996
+ ctx.setVariable("fillStyle", color);
997
+
998
+ /*
999
+ need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
1000
+ to properly work in Firefox
1001
+ */
1002
+ ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family);
1003
+
1004
+ if (align){
1005
+ ctx.setVariable("textAlign", "right");
1006
+ }else{
1007
+ ctx.setVariable("textAlign", "left");
1008
+ }
1009
+
1010
+
1011
+ /*
1012
+ if (stack.clip){
1013
+ ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height);
1014
+ ctx.clip();
1015
+ }
1016
+ */
1017
+
1018
+
1019
+ oldTextNode = textNode;
1020
+
1021
+
1022
+ for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) {
1023
+ textValue = null;
1024
+
1025
+
1026
+
1027
+ if (support.rangeBounds){
1028
+ // getBoundingClientRect is supported for ranges
1029
+ if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) {
1030
+ textValue = renderList[c];
1031
+ if (doc.createRange){
1032
+ range = doc.createRange();
1033
+
1034
+ range.setStart(textNode, textOffset);
1035
+ range.setEnd(textNode, textOffset + textValue.length);
1036
+ }else{
1037
+ // TODO add IE support
1038
+ range = body.createTextRange();
1039
+ }
1040
+
1041
+ if (range.getBoundingClientRect()) {
1042
+ bounds = range.getBoundingClientRect();
1043
+ }else{
1044
+ bounds = {};
1045
+ }
1046
+
1047
+ }
1048
+ }else{
1049
+ // it isn't supported, so let's wrap it inside an element instead and get the bounds there
1050
+
1051
+ // IE 9 bug
1052
+ if (typeof oldTextNode.nodeValue !== "string" ){
1053
+ continue;
1054
+ }
1055
+
1056
+ newTextNode = oldTextNode.splitText(renderList[c].length);
1057
+
1058
+ parent = oldTextNode.parentNode;
1059
+ wrapElement = doc.createElement('wrapper');
1060
+ backupText = oldTextNode.cloneNode(true);
1061
+
1062
+ wrapElement.appendChild(oldTextNode.cloneNode(true));
1063
+ parent.replaceChild(wrapElement, oldTextNode);
1064
+
1065
+ bounds = _html2canvas.Util.Bounds(wrapElement);
1066
+
1067
+ textValue = oldTextNode.nodeValue;
1068
+
1069
+ oldTextNode = newTextNode;
1070
+ parent.replaceChild(backupText, wrapElement);
1071
+
1072
+
1073
+ }
1074
+
1075
+ if (textValue !== null){
1076
+ drawText(textValue, bounds.left, bounds.bottom, ctx);
1077
+ }
1078
+
1079
+ switch(text_decoration) {
1080
+ case "underline":
1081
+ // Draws a line at the baseline of the font
1082
+ // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
1083
+ renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color);
1084
+ break;
1085
+ case "overline":
1086
+ renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color);
1087
+ break;
1088
+ case "line-through":
1089
+ // TODO try and find exact position for line-through
1090
+ renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color);
1091
+ break;
1092
+
1093
+ }
1094
+
1095
+
1096
+
1097
+
1098
+
1099
+ textOffset += renderList[c].length;
1100
+
1101
+ }
1102
+
1103
+
1104
+
1105
+ }
1106
+
1107
+ }
1108
+
1109
+ function listPosition (element, val) {
1110
+ var boundElement = doc.createElement( "boundelement" ),
1111
+ type,
1112
+ bounds;
1113
+
1114
+ boundElement.style.display = "inline";
1115
+ //boundElement.style.width = "1px";
1116
+ //boundElement.style.height = "1px";
1117
+
1118
+ type = element.style.listStyleType;
1119
+ element.style.listStyleType = "none";
1120
+
1121
+ boundElement.appendChild( doc.createTextNode( val ) );
1122
+
1123
+
1124
+ element.insertBefore(boundElement, element.firstChild);
1125
+
1126
+
1127
+ bounds = _html2canvas.Util.Bounds( boundElement );
1128
+ element.removeChild( boundElement );
1129
+ element.style.listStyleType = type;
1130
+ return bounds;
1131
+
1132
+ }
1133
+
1134
+
1135
+ function renderListItem(element, stack, elBounds) {
1136
+
1137
+
1138
+ var position = getCSS(element, "listStylePosition"),
1139
+ x,
1140
+ y,
1141
+ type = getCSS(element, "listStyleType"),
1142
+ currentIndex,
1143
+ text,
1144
+ listBounds,
1145
+ bold = getCSS(element, "fontWeight");
1146
+
1147
+ if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) {
1148
+
1149
+ // TODO remove jQuery dependency
1150
+ currentIndex = $(element).index()+1;
1151
+
1152
+ switch(type){
1153
+ case "decimal":
1154
+ text = currentIndex;
1155
+ break;
1156
+ case "decimal-leading-zero":
1157
+ if (currentIndex.toString().length === 1){
1158
+ text = currentIndex = "0" + currentIndex.toString();
1159
+ }else{
1160
+ text = currentIndex.toString();
1161
+ }
1162
+ break;
1163
+ case "upper-roman":
1164
+ text = _html2canvas.Generate.ListRoman( currentIndex );
1165
+ break;
1166
+ case "lower-roman":
1167
+ text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase();
1168
+ break;
1169
+ case "lower-alpha":
1170
+ text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase();
1171
+ break;
1172
+ case "upper-alpha":
1173
+ text = _html2canvas.Generate.ListAlpha( currentIndex );
1174
+ break;
1175
+ }
1176
+
1177
+
1178
+ text += ". ";
1179
+ listBounds = listPosition(element, text);
1180
+
1181
+
1182
+
1183
+ switch(bold){
1184
+ case 401:
1185
+ bold = "bold";
1186
+ break;
1187
+ case 400:
1188
+ bold = "normal";
1189
+ break;
1190
+ }
1191
+
1192
+
1193
+
1194
+
1195
+ ctx.setVariable( "fillStyle", getCSS(element, "color") );
1196
+ ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") );
1197
+
1198
+
1199
+ if ( position === "inside" ) {
1200
+ ctx.setVariable("textAlign", "left");
1201
+ // this.setFont(stack.ctx, element, false);
1202
+ x = elBounds.left;
1203
+
1204
+ }else{
1205
+ return;
1206
+ /*
1207
+ TODO really need to figure out some more accurate way to try and find the position.
1208
+ as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser
1209
+ may display it whatever way it feels like.
1210
+ "The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order"
1211
+
1212
+ ctx.setVariable("textAlign", "right");
1213
+ // this.setFont(stack.ctx, element, true);
1214
+ x = elBounds.left - 10;
1215
+ */
1216
+ }
1217
+
1218
+ y = listBounds.bottom;
1219
+
1220
+ drawText(text, x, y, ctx);
1221
+
1222
+
1223
+ }
1224
+
1225
+
1226
+ }
1227
+
1228
+ function loadImage (src){
1229
+ var img = images[src];
1230
+ if (img && img.succeeded === true) {
1231
+ return img.img;
1232
+ } else {
1233
+ return false;
1234
+ }
1235
+ }
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+
1242
+ function clipBounds(src, dst){
1243
+
1244
+ var x = Math.max(src.left, dst.left),
1245
+ y = Math.max(src.top, dst.top),
1246
+ x2 = Math.min((src.left + src.width), (dst.left + dst.width)),
1247
+ y2 = Math.min((src.top + src.height), (dst.top + dst.height));
1248
+
1249
+ return {
1250
+ left:x,
1251
+ top:y,
1252
+ width:x2-x,
1253
+ height:y2-y
1254
+ };
1255
+
1256
+ }
1257
+
1258
+ function setZ(zIndex, parentZ){
1259
+ // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them
1260
+ var newContext;
1261
+ if (!parentZ){
1262
+ newContext = h2czContext(0);
1263
+ return newContext;
1264
+ }
1265
+
1266
+ if (zIndex !== "auto"){
1267
+ needReorder = true;
1268
+ newContext = h2czContext(zIndex);
1269
+ parentZ.children.push(newContext);
1270
+ return newContext;
1271
+
1272
+ }
1273
+
1274
+ return parentZ;
1275
+
1276
+ }
1277
+
1278
+ function renderBorders(el, ctx, bounds, clip){
1279
+
1280
+ /*
1281
+ * TODO add support for different border-style's than solid
1282
+ */
1283
+
1284
+ var x = bounds.left,
1285
+ y = bounds.top,
1286
+ w = bounds.width,
1287
+ h = bounds.height,
1288
+ borderSide,
1289
+ borderData,
1290
+ bx,
1291
+ by,
1292
+ bw,
1293
+ bh,
1294
+ borderBounds,
1295
+ borders = (function(el){
1296
+ var borders = [],
1297
+ sides = ["Top","Right","Bottom","Left"],
1298
+ s;
1299
+
1300
+ for (s = 0; s < 4; s+=1){
1301
+ borders.push({
1302
+ width: getCSSInt(el, 'border' + sides[s] + 'Width'),
1303
+ color: getCSS(el, 'border' + sides[s] + 'Color')
1304
+ });
1305
+ }
1306
+
1307
+ return borders;
1308
+
1309
+ }(el));
1310
+
1311
+
1312
+ for (borderSide = 0; borderSide < 4; borderSide+=1){
1313
+ borderData = borders[borderSide];
1314
+
1315
+ if (borderData.width>0){
1316
+ bx = x;
1317
+ by = y;
1318
+ bw = w;
1319
+ bh = h - (borders[2].width);
1320
+
1321
+ switch(borderSide){
1322
+ case 0:
1323
+ // top border
1324
+ bh = borders[0].width;
1325
+ break;
1326
+ case 1:
1327
+ // right border
1328
+ bx = x + w - (borders[1].width);
1329
+ bw = borders[1].width;
1330
+ break;
1331
+ case 2:
1332
+ // bottom border
1333
+ by = (by + h) - (borders[2].width);
1334
+ bh = borders[2].width;
1335
+ break;
1336
+ case 3:
1337
+ // left border
1338
+ bw = borders[3].width;
1339
+ break;
1340
+ }
1341
+
1342
+ borderBounds = {
1343
+ left:bx,
1344
+ top:by,
1345
+ width: bw,
1346
+ height:bh
1347
+ };
1348
+
1349
+ if (clip){
1350
+ borderBounds = clipBounds(borderBounds, clip);
1351
+ }
1352
+
1353
+
1354
+ if (borderBounds.width>0 && borderBounds.height>0){
1355
+ renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color);
1356
+ }
1357
+
1358
+
1359
+ }
1360
+ }
1361
+
1362
+ return borders;
1363
+
1364
+ }
1365
+
1366
+
1367
+ function renderFormValue (el, bounds, stack){
1368
+
1369
+ var valueWrap = doc.createElement('valuewrap'),
1370
+ cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'],
1371
+ i,
1372
+ textValue,
1373
+ textNode,
1374
+ arrLen,
1375
+ style;
1376
+
1377
+ for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){
1378
+ style = cssArr[i];
1379
+
1380
+ try {
1381
+ valueWrap.style[style] = getCSS(el, style);
1382
+ } catch( e ) {
1383
+ // Older IE has issues with "border"
1384
+ h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
1385
+ }
1386
+ }
1387
+
1388
+
1389
+ valueWrap.style.borderColor = "black";
1390
+ valueWrap.style.borderStyle = "solid";
1391
+ valueWrap.style.display = "block";
1392
+ valueWrap.style.position = "absolute";
1393
+ if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){
1394
+ valueWrap.style.lineHeight = getCSS(el, "height");
1395
+ }
1396
+
1397
+
1398
+ valueWrap.style.top = bounds.top + "px";
1399
+ valueWrap.style.left = bounds.left + "px";
1400
+
1401
+ if (el.nodeName === "SELECT"){
1402
+ // TODO increase accuracy of text position
1403
+ textValue = el.options[el.selectedIndex].text;
1404
+ } else{
1405
+ textValue = el.value;
1406
+ }
1407
+ textNode = doc.createTextNode(textValue);
1408
+
1409
+ valueWrap.appendChild(textNode);
1410
+ body.appendChild(valueWrap);
1411
+
1412
+
1413
+ renderText(el, textNode, stack);
1414
+ body.removeChild(valueWrap);
1415
+
1416
+
1417
+
1418
+ }
1419
+
1420
+
1421
+
1422
+
1423
+
1424
+ function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) {
1425
+ ctx.drawImage(
1426
+ image,
1427
+ sx, //sx
1428
+ sy, //sy
1429
+ sw, //sw
1430
+ sh, //sh
1431
+ dx, //dx
1432
+ dy, // dy
1433
+ dw, //dw
1434
+ dh //dh
1435
+ );
1436
+ numDraws+=1;
1437
+
1438
+ }
1439
+
1440
+
1441
+ function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){
1442
+ var sourceX = 0,
1443
+ sourceY=0;
1444
+ if (elx-x>0){
1445
+ sourceX = elx-x;
1446
+ }
1447
+
1448
+ if (ely-y>0){
1449
+ sourceY = ely-y;
1450
+ }
1451
+
1452
+ renderImage(
1453
+ ctx,
1454
+ image,
1455
+ sourceX, // source X
1456
+ sourceY, // source Y
1457
+ width-sourceX, // source Width
1458
+ height-sourceY, // source Height
1459
+ x+sourceX, // destination X
1460
+ y+sourceY, // destination Y
1461
+ width-sourceX, // destination width
1462
+ height-sourceY // destination height
1463
+ );
1464
+ }
1465
+
1466
+
1467
+ function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){
1468
+
1469
+ var height,
1470
+ width = Math.min(image.width,w),bgy;
1471
+
1472
+ bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height;
1473
+
1474
+
1475
+ for(bgy=(y+bgp.top);bgy<h+y;){
1476
+
1477
+
1478
+ if ( Math.floor(bgy+image.height)>h+y){
1479
+ height = (h+y)-bgy;
1480
+ }else{
1481
+ height = image.height;
1482
+ }
1483
+ renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y);
1484
+
1485
+ bgy = Math.floor(bgy+image.height);
1486
+
1487
+ }
1488
+ }
1489
+
1490
+ function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){
1491
+
1492
+ var height = Math.min(image.height,h),
1493
+ width,bgx;
1494
+
1495
+
1496
+ bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width;
1497
+
1498
+
1499
+ for (bgx=(x+bgp.left);bgx<w+x;) {
1500
+
1501
+ if (Math.floor(bgx+image.width)>w+x){
1502
+ width = (w+x)-bgx;
1503
+ }else{
1504
+ width = image.width;
1505
+ }
1506
+
1507
+ renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y);
1508
+
1509
+ bgx = Math.floor(bgx+image.width);
1510
+
1511
+
1512
+ }
1513
+ }
1514
+
1515
+ function renderBackground(el,bounds,ctx){
1516
+
1517
+ // TODO add support for multi background-images
1518
+ var background_image = getCSS(el, "backgroundImage"),
1519
+ background_repeat = getCSS(el, "backgroundRepeat").split(",")[0],
1520
+ image,
1521
+ bgp,
1522
+ bgy,
1523
+ bgw,
1524
+ bgsx,
1525
+ bgsy,
1526
+ bgdx,
1527
+ bgdy,
1528
+ bgh,
1529
+ h,
1530
+ height,
1531
+ add;
1532
+
1533
+ // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){
1534
+
1535
+ if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) {
1536
+ background_image = background_image.split(",")[0];
1537
+ }
1538
+
1539
+ if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) {
1540
+ background_image = _html2canvas.Util.backgroundImage( background_image );
1541
+ image = loadImage( background_image );
1542
+
1543
+
1544
+ bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image);
1545
+
1546
+ // TODO add support for background-origin
1547
+ if ( image ){
1548
+ switch ( background_repeat ) {
1549
+
1550
+ case "repeat-x":
1551
+ renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height );
1552
+ break;
1553
+
1554
+ case "repeat-y":
1555
+ renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height );
1556
+ break;
1557
+
1558
+ case "no-repeat":
1559
+ /*
1560
+ this.drawBackgroundRepeat(
1561
+ ctx,
1562
+ image,
1563
+ bgp.left+bounds.left, // sx
1564
+ bgp.top+bounds.top, // sy
1565
+ Math.min(bounds.width,image.width),
1566
+ Math.min(bounds.height,image.height),
1567
+ bounds.left,
1568
+ bounds.top
1569
+ );*/
1570
+
1571
+
1572
+ // console.log($(el).css('background-image'));
1573
+ bgw = bounds.width - bgp.left;
1574
+ bgh = bounds.height - bgp.top;
1575
+ bgsx = bgp.left;
1576
+ bgsy = bgp.top;
1577
+ bgdx = bgp.left+bounds.left;
1578
+ bgdy = bgp.top+bounds.top;
1579
+
1580
+ //
1581
+ // bgw = Math.min(bgw,image.width);
1582
+ // bgh = Math.min(bgh,image.height);
1583
+
1584
+ if (bgsx<0){
1585
+ bgsx = Math.abs(bgsx);
1586
+ bgdx += bgsx;
1587
+ bgw = Math.min(bounds.width,image.width-bgsx);
1588
+ }else{
1589
+ bgw = Math.min(bgw,image.width);
1590
+ bgsx = 0;
1591
+ }
1592
+
1593
+ if (bgsy<0){
1594
+ bgsy = Math.abs(bgsy);
1595
+ bgdy += bgsy;
1596
+ // bgh = bgh-bgsy;
1597
+ bgh = Math.min(bounds.height,image.height-bgsy);
1598
+ }else{
1599
+ bgh = Math.min(bgh,image.height);
1600
+ bgsy = 0;
1601
+ }
1602
+
1603
+
1604
+ if (bgh>0 && bgw > 0){
1605
+ renderImage(
1606
+ ctx,
1607
+ image,
1608
+ bgsx, // source X : 0
1609
+ bgsy, // source Y : 1695
1610
+ bgw, // source Width : 18
1611
+ bgh, // source Height : 1677
1612
+ bgdx, // destination X :906
1613
+ bgdy, // destination Y : 1020
1614
+ bgw, // destination width : 18
1615
+ bgh // destination height : 1677
1616
+ );
1617
+
1618
+ }
1619
+ break;
1620
+ default:
1621
+
1622
+
1623
+
1624
+ bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height;
1625
+
1626
+
1627
+ for(bgy=(bounds.top+bgp.top);bgy<bounds.height+bounds.top;){
1628
+
1629
+
1630
+
1631
+ h = Math.min(image.height,(bounds.height+bounds.top)-bgy);
1632
+
1633
+
1634
+ if ( Math.floor(bgy+image.height)>h+bgy){
1635
+ height = (h+bgy)-bgy;
1636
+ }else{
1637
+ height = image.height;
1638
+ }
1639
+ // console.log(height);
1640
+
1641
+ if (bgy<bounds.top){
1642
+ add = bounds.top-bgy;
1643
+ bgy = bounds.top;
1644
+
1645
+ }else{
1646
+ add = 0;
1647
+ }
1648
+
1649
+ renderBackgroundRepeatX(ctx,image,bgp,bounds.left,bgy,bounds.width,height);
1650
+ if (add>0){
1651
+ bgp.top += add;
1652
+ }
1653
+ bgy = Math.floor(bgy+image.height)-add;
1654
+ }
1655
+ break;
1656
+
1657
+
1658
+ }
1659
+ }else{
1660
+ h2clog("html2canvas: Error loading background:" + background_image);
1661
+ //console.log(images);
1662
+ }
1663
+
1664
+ }
1665
+ }
1666
+
1667
+
1668
+
1669
+ function renderElement(el, parentStack){
1670
+
1671
+ var bounds = _html2canvas.Util.Bounds(el),
1672
+ x = bounds.left,
1673
+ y = bounds.top,
1674
+ w = bounds.width,
1675
+ h = bounds.height,
1676
+ image,
1677
+ bgcolor = getCSS(el, "backgroundColor"),
1678
+ cssPosition = getCSS(el, "position"),
1679
+ zindex,
1680
+ opacity = getCSS(el, "opacity"),
1681
+ stack,
1682
+ stackLength,
1683
+ borders,
1684
+ ctx,
1685
+ bgbounds,
1686
+ imgSrc,
1687
+ paddingLeft,
1688
+ paddingTop,
1689
+ paddingRight,
1690
+ paddingBottom;
1691
+
1692
+ if (!parentStack){
1693
+ docDim = docSize();
1694
+ parentStack = {
1695
+ opacity: 1
1696
+ };
1697
+ }else{
1698
+ docDim = {};
1699
+ }
1700
+
1701
+
1702
+ //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode);
1703
+
1704
+ zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex );
1705
+
1706
+
1707
+
1708
+ stack = {
1709
+ ctx: h2cRenderContext( docDim.width || w , docDim.height || h ),
1710
+ zIndex: zindex,
1711
+ opacity: opacity * parentStack.opacity,
1712
+ cssPosition: cssPosition
1713
+ };
1714
+
1715
+
1716
+
1717
+ // TODO correct overflow for absolute content residing under a static position
1718
+
1719
+ if (parentStack.clip){
1720
+ stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip );
1721
+ //stack.clip = parentStack.clip;
1722
+ // stack.clip.height = stack.clip.height - parentStack.borders[2].width;
1723
+ }
1724
+
1725
+
1726
+ if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){
1727
+ if (stack.clip){
1728
+ stack.clip = clipBounds(stack.clip, bounds);
1729
+ }else{
1730
+ stack.clip = bounds;
1731
+ }
1732
+ }
1733
+
1734
+
1735
+ stackLength = zindex.children.push(stack);
1736
+
1737
+ ctx = zindex.children[stackLength-1].ctx;
1738
+
1739
+ ctx.setVariable("globalAlpha", stack.opacity);
1740
+
1741
+ // draw element borders
1742
+ borders = renderBorders(el, ctx, bounds, false);
1743
+ stack.borders = borders;
1744
+
1745
+
1746
+ // let's modify clip area for child elements, so borders dont get overwritten
1747
+
1748
+ /*
1749
+ if (stack.clip){
1750
+ stack.clip.width = stack.clip.width-(borders[1].width);
1751
+ stack.clip.height = stack.clip.height-(borders[2].width);
1752
+ }
1753
+ */
1754
+ if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){
1755
+ if (options.iframeDefault === "default"){
1756
+ bgcolor = "#efefef";
1757
+ }else{
1758
+ bgcolor = options.iframeDefault;
1759
+ }
1760
+ }
1761
+
1762
+ // draw base element bgcolor
1763
+
1764
+ bgbounds = {
1765
+ left: x + borders[3].width,
1766
+ top: y + borders[0].width,
1767
+ width: w - (borders[1].width + borders[3].width),
1768
+ height: h - (borders[0].width + borders[2].width)
1769
+ };
1770
+
1771
+ //if (this.withinBounds(stack.clip,bgbounds)){
1772
+
1773
+ if (stack.clip){
1774
+ bgbounds = clipBounds(bgbounds, stack.clip);
1775
+
1776
+ //}
1777
+
1778
+ }
1779
+
1780
+
1781
+ if (bgbounds.height > 0 && bgbounds.width > 0){
1782
+ renderRect(
1783
+ ctx,
1784
+ bgbounds.left,
1785
+ bgbounds.top,
1786
+ bgbounds.width,
1787
+ bgbounds.height,
1788
+ bgcolor
1789
+ );
1790
+
1791
+ renderBackground(el, bgbounds, ctx);
1792
+ }
1793
+
1794
+ switch(el.nodeName){
1795
+ case "IMG":
1796
+ imgSrc = el.getAttribute('src');
1797
+ image = loadImage(imgSrc);
1798
+ if (image){
1799
+
1800
+ paddingLeft = getCSSInt(el, 'paddingLeft');
1801
+ paddingTop = getCSSInt(el, 'paddingTop');
1802
+ paddingRight = getCSSInt(el, 'paddingRight');
1803
+ paddingBottom = getCSSInt(el, 'paddingBottom');
1804
+
1805
+
1806
+ renderImage(
1807
+ ctx,
1808
+ image,
1809
+ 0, //sx
1810
+ 0, //sy
1811
+ image.width, //sw
1812
+ image.height, //sh
1813
+ x + paddingLeft + borders[3].width, //dx
1814
+ y + paddingTop + borders[0].width, // dy
1815
+ bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw
1816
+ bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh
1817
+ );
1818
+
1819
+ }else{
1820
+ h2clog("html2canvas: Error loading <img>:" + imgSrc);
1821
+ }
1822
+ break;
1823
+ case "INPUT":
1824
+ // TODO add all relevant type's, i.e. HTML5 new stuff
1825
+ // todo add support for placeholder attribute for browsers which support it
1826
+ if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){
1827
+
1828
+ renderFormValue(el, bounds, stack);
1829
+
1830
+
1831
+ /*
1832
+ this just doesn't work well enough
1833
+
1834
+ this.newText(el,{
1835
+ nodeValue:el.value,
1836
+ splitText: function(){
1837
+ return this;
1838
+ },
1839
+ formValue:true
1840
+ },stack);
1841
+ */
1842
+ }
1843
+ break;
1844
+ case "TEXTAREA":
1845
+ if (el.value.length > 0){
1846
+ renderFormValue(el, bounds, stack);
1847
+ }
1848
+ break;
1849
+ case "SELECT":
1850
+ if (el.options.length > 0){
1851
+ renderFormValue(el, bounds, stack);
1852
+ }
1853
+ break;
1854
+ case "LI":
1855
+ renderListItem(el, stack, bgbounds);
1856
+ break;
1857
+ case "CANVAS":
1858
+ paddingLeft = getCSSInt(el, 'paddingLeft');
1859
+ paddingTop = getCSSInt(el, 'paddingTop');
1860
+ paddingRight = getCSSInt(el, 'paddingRight');
1861
+ paddingBottom = getCSSInt(el, 'paddingBottom');
1862
+ renderImage(
1863
+ ctx,
1864
+ el,
1865
+ 0, //sx
1866
+ 0, //sy
1867
+ el.width, //sw
1868
+ el.height, //sh
1869
+ x + paddingLeft + borders[3].width, //dx
1870
+ y + paddingTop + borders[0].width, // dy
1871
+ bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw
1872
+ bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh
1873
+ );
1874
+ break;
1875
+ }
1876
+
1877
+ return zindex.children[stackLength - 1];
1878
+ }
1879
+
1880
+
1881
+
1882
+ function parseElement (el, stack) {
1883
+
1884
+ // skip hidden elements and their children
1885
+ if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden") {
1886
+
1887
+ stack = renderElement(el, stack) || stack;
1888
+
1889
+ ctx = stack.ctx;
1890
+
1891
+ if ( !ignoreElementsRegExp.test( el.nodeName ) ) {
1892
+ var elementChildren = _html2canvas.Util.Children( el ),
1893
+ i,
1894
+ node,
1895
+ childrenLen;
1896
+ for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) {
1897
+ node = elementChildren[i];
1898
+
1899
+ if ( node.nodeType === 1 ) {
1900
+ parseElement(node, stack);
1901
+ }else if ( node.nodeType === 3 ) {
1902
+ renderText(el, node, stack);
1903
+ }
1904
+
1905
+ }
1906
+
1907
+ }
1908
+ }
1909
+ }
1910
+
1911
+ stack = renderElement(element, null);
1912
+
1913
+ /*
1914
+ SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards
1915
+ */
1916
+
1917
+ if ( support.svgRendering ) {
1918
+ (function( body ){
1919
+ var img = new Image(),
1920
+ size = docSize(),
1921
+ html = "";
1922
+
1923
+ function parseDOM( el ) {
1924
+ var children = _html2canvas.Util.Children( el ),
1925
+ len = children.length,
1926
+ attr,
1927
+ a,
1928
+ alen,
1929
+ elm,
1930
+ i;
1931
+ for ( i = 0; i < len; i+=1 ) {
1932
+ elm = children[ i ];
1933
+ if ( elm.nodeType === 3 ) {
1934
+ // Text node
1935
+
1936
+ html += elm.nodeValue.replace(/\</g,"&lt;").replace(/\>/g,"&gt;");
1937
+ } else if ( elm.nodeType === 1 ) {
1938
+ // Element
1939
+ if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) {
1940
+
1941
+ html += "<" + elm.nodeName.toLowerCase();
1942
+
1943
+ // add attributes
1944
+ if ( elm.hasAttributes() ) {
1945
+ attr = elm.attributes;
1946
+ alen = attr.length;
1947
+ for ( a = 0; a < alen; a+=1 ) {
1948
+ html += " " + attr[ a ].name + '="' + attr[ a ].value + '"';
1949
+ }
1950
+ }
1951
+
1952
+
1953
+ html += '>';
1954
+
1955
+ parseDOM( elm );
1956
+
1957
+
1958
+ html += "</" + elm.nodeName.toLowerCase() + ">";
1959
+ }
1960
+ }
1961
+
1962
+ }
1963
+
1964
+ }
1965
+
1966
+ parseDOM( body );
1967
+ img.src = [
1968
+ "data:image/svg+xml,",
1969
+ "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" + size.width + "' height='" + size.height + "'>",
1970
+ "<foreignObject width='" + size.width + "' height='" + size.height + "'>",
1971
+ "<html xmlns='http://www.w3.org/1999/xhtml' style='margin:0;'>",
1972
+ html.replace(/\#/g,"%23"),
1973
+ "</html>",
1974
+ "</foreignObject>",
1975
+ "</svg>"
1976
+ ].join("");
1977
+
1978
+
1979
+
1980
+
1981
+ img.onload = function() {
1982
+ stack.svgRender = img;
1983
+ };
1984
+
1985
+ })( document.documentElement );
1986
+
1987
+ }
1988
+
1989
+
1990
+ // parse every child element
1991
+ for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){
1992
+ parseElement(children[i], stack);
1993
+ }
1994
+
1995
+
1996
+ stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" );
1997
+
1998
+ return stack;
1999
+
2000
+ };
2001
+
2002
+ function h2czContext(zindex) {
2003
+ return {
2004
+ zindex: zindex,
2005
+ children: []
2006
+ };
2007
+ }
2008
+ /*
2009
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
2010
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2011
+ http://www.twitter.com/niklasvh
2012
+
2013
+ Released under MIT License
2014
+ */
2015
+
2016
+ _html2canvas.Preload = function( options ) {
2017
+
2018
+ var images = {
2019
+ numLoaded: 0, // also failed are counted here
2020
+ numFailed: 0,
2021
+ numTotal: 0,
2022
+ cleanupDone: false
2023
+ },
2024
+ pageOrigin,
2025
+ methods,
2026
+ i,
2027
+ count = 0,
2028
+ element = options.elements[0] || document.body,
2029
+ doc = element.ownerDocument,
2030
+ domImages = doc.images, // TODO probably should limit it to images present in the element only
2031
+ imgLen = domImages.length,
2032
+ link = doc.createElement("a"),
2033
+ supportCORS = (function( img ){
2034
+ return (img.crossOrigin !== undefined);
2035
+ })(new Image()),
2036
+ timeoutTimer;
2037
+
2038
+ link.href = window.location.href;
2039
+ pageOrigin = link.protocol + link.host;
2040
+
2041
+
2042
+
2043
+
2044
+
2045
+
2046
+ function isSameOrigin(url){
2047
+ link.href = url;
2048
+ link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
2049
+ var origin = link.protocol + link.host;
2050
+ return (origin === pageOrigin);
2051
+ }
2052
+
2053
+ function start(){
2054
+ h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")");
2055
+ if (!images.firstRun && images.numLoaded >= images.numTotal){
2056
+ h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")");
2057
+
2058
+ if (typeof options.complete === "function"){
2059
+ options.complete(images);
2060
+ }
2061
+
2062
+ }
2063
+ }
2064
+
2065
+ // TODO modify proxy to serve images with CORS enabled, where available
2066
+ function proxyGetImage(url, img, imageObj){
2067
+ var callback_name,
2068
+ scriptUrl = options.proxy,
2069
+ script;
2070
+
2071
+ link.href = url;
2072
+ url = link.href; // work around for pages with base href="" set - WARNING: this may change the url
2073
+
2074
+ callback_name = 'html2canvas_' + (count++);
2075
+ imageObj.callbackname = callback_name;
2076
+
2077
+ if (scriptUrl.indexOf("?") > -1) {
2078
+ scriptUrl += "&";
2079
+ } else {
2080
+ scriptUrl += "?";
2081
+ }
2082
+ scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name;
2083
+ script = doc.createElement("script");
2084
+
2085
+ window[callback_name] = function(a){
2086
+ if (a.substring(0,6) === "error:"){
2087
+ imageObj.succeeded = false;
2088
+ images.numLoaded++;
2089
+ images.numFailed++;
2090
+ start();
2091
+ } else {
2092
+ setImageLoadHandlers(img, imageObj);
2093
+ img.src = a;
2094
+ }
2095
+ window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
2096
+ try {
2097
+ delete window[callback_name]; // for all browser that support this
2098
+ } catch(ex) {}
2099
+ script.parentNode.removeChild(script);
2100
+ script = null;
2101
+ delete imageObj.script;
2102
+ delete imageObj.callbackname;
2103
+ };
2104
+
2105
+ script.setAttribute("type", "text/javascript");
2106
+ script.setAttribute("src", scriptUrl);
2107
+ imageObj.script = script;
2108
+ window.document.body.appendChild(script);
2109
+
2110
+ }
2111
+
2112
+ function getImages (el) {
2113
+
2114
+
2115
+
2116
+ // if (!this.ignoreRe.test(el.nodeName)){
2117
+ //
2118
+
2119
+ var contents = _html2canvas.Util.Children(el),
2120
+ i,
2121
+ contentsLen = contents.length,
2122
+ background_image,
2123
+ src,
2124
+ img,
2125
+ elNodeType = false;
2126
+
2127
+ for (i = 0; i < contentsLen; i+=1 ){
2128
+ // var ignRe = new RegExp("("+this.ignoreElements+")");
2129
+ // if (!ignRe.test(element.nodeName)){
2130
+ getImages(contents[i]);
2131
+ // }
2132
+ }
2133
+
2134
+ // }
2135
+ try {
2136
+ elNodeType = el.nodeType;
2137
+ } catch (ex) {
2138
+ elNodeType = false;
2139
+ h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
2140
+ }
2141
+
2142
+ if (elNodeType === 1 || elNodeType === undefined){
2143
+
2144
+ // opera throws exception on external-content.html
2145
+ try {
2146
+ background_image = _html2canvas.Util.getCSS(el, 'backgroundImage');
2147
+ }catch(e) {
2148
+ h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
2149
+ }
2150
+ if ( background_image && background_image !== "1" && background_image !== "none" ) {
2151
+
2152
+ // TODO add multi image background support
2153
+
2154
+ if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) {
2155
+ // if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") {
2156
+
2157
+ img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
2158
+
2159
+ if ( img !== undefined ){
2160
+ images[background_image] = {
2161
+ img: img,
2162
+ succeeded: true
2163
+ };
2164
+ images.numTotal++;
2165
+ images.numLoaded++;
2166
+ start();
2167
+
2168
+ }
2169
+
2170
+ } else {
2171
+ src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]);
2172
+ methods.loadImage(src);
2173
+ }
2174
+
2175
+ /*
2176
+ if (background_image && background_image !== "1" && background_image !== "none" && background_image.substring(0,7) !== "-webkit" && background_image.substring(0,3)!== "-o-" && background_image.substring(0,4) !== "-moz"){
2177
+ // TODO add multi image background support
2178
+ src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]);
2179
+ methods.loadImage(src); */
2180
+ }
2181
+ }
2182
+ }
2183
+
2184
+ function setImageLoadHandlers(img, imageObj) {
2185
+ img.onload = function() {
2186
+ if ( imageObj.timer !== undefined ) {
2187
+ // CORS succeeded
2188
+ window.clearTimeout( imageObj.timer );
2189
+ }
2190
+
2191
+ images.numLoaded++;
2192
+ imageObj.succeeded = true;
2193
+ img.onerror = img.onload = null;
2194
+ start();
2195
+ };
2196
+ img.onerror = function() {
2197
+
2198
+ if (img.crossOrigin === "anonymous") {
2199
+ // CORS failed
2200
+ window.clearTimeout( imageObj.timer );
2201
+
2202
+ // let's try with proxy instead
2203
+ if ( options.proxy ) {
2204
+ var src = img.src;
2205
+ img = new Image();
2206
+ imageObj.img = img;
2207
+ img.src = src;
2208
+
2209
+ proxyGetImage( img.src, img, imageObj );
2210
+ return;
2211
+ }
2212
+ }
2213
+
2214
+
2215
+ images.numLoaded++;
2216
+ images.numFailed++;
2217
+ imageObj.succeeded = false;
2218
+ img.onerror = img.onload = null;
2219
+ start();
2220
+
2221
+ };
2222
+
2223
+ // TODO Opera has no load/error event for SVG images
2224
+
2225
+ // Opera ninja onload's cached images
2226
+ /*
2227
+ window.setTimeout(function(){
2228
+ if ( img.width !== 0 && imageObj.succeeded === undefined ) {
2229
+ img.onload();
2230
+ }
2231
+ }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does.
2232
+ */
2233
+ }
2234
+
2235
+
2236
+ methods = {
2237
+ loadImage: function( src ) {
2238
+ var img, imageObj;
2239
+ if ( src && images[src] === undefined ) {
2240
+ img = new Image();
2241
+ if ( src.match(/data:image\/.*;base64,/i) ) {
2242
+ img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
2243
+ imageObj = images[src] = {
2244
+ img: img
2245
+ };
2246
+ images.numTotal++;
2247
+ setImageLoadHandlers(img, imageObj);
2248
+ } else if ( isSameOrigin( src ) || options.allowTaint === true ) {
2249
+ imageObj = images[src] = {
2250
+ img: img
2251
+ };
2252
+ images.numTotal++;
2253
+ setImageLoadHandlers(img, imageObj);
2254
+ img.src = src;
2255
+ } else if ( supportCORS && !options.allowTaint && options.useCORS ) {
2256
+ // attempt to load with CORS
2257
+
2258
+ img.crossOrigin = "anonymous";
2259
+ imageObj = images[src] = {
2260
+ img: img
2261
+ };
2262
+ images.numTotal++;
2263
+ setImageLoadHandlers(img, imageObj);
2264
+ img.src = src;
2265
+
2266
+ // work around for https://bugs.webkit.org/show_bug.cgi?id=80028
2267
+ img.customComplete = function () {
2268
+ if (!this.img.complete) {
2269
+ this.timer = window.setTimeout(this.img.customComplete, 100);
2270
+ } else {
2271
+ this.img.onerror();
2272
+ }
2273
+ }.bind(imageObj);
2274
+ img.customComplete();
2275
+
2276
+ } else if ( options.proxy ) {
2277
+ imageObj = images[src] = {
2278
+ img: img
2279
+ };
2280
+ images.numTotal++;
2281
+ proxyGetImage( src, img, imageObj );
2282
+ }
2283
+ }
2284
+
2285
+ },
2286
+ cleanupDOM: function(cause) {
2287
+ var img, src;
2288
+ if (!images.cleanupDone) {
2289
+ if (cause && typeof cause === "string") {
2290
+ h2clog("html2canvas: Cleanup because: " + cause);
2291
+ } else {
2292
+ h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
2293
+ }
2294
+
2295
+ for (src in images) {
2296
+ if (images.hasOwnProperty(src)) {
2297
+ img = images[src];
2298
+ if (typeof img === "object" && img.callbackname && img.succeeded === undefined) {
2299
+ // cancel proxy image request
2300
+ window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
2301
+ try {
2302
+ delete window[img.callbackname]; // for all browser that support this
2303
+ } catch(ex) {}
2304
+ if (img.script && img.script.parentNode) {
2305
+ img.script.setAttribute("src", "about:blank"); // try to cancel running request
2306
+ img.script.parentNode.removeChild(img.script);
2307
+ }
2308
+ images.numLoaded++;
2309
+ images.numFailed++;
2310
+ h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);
2311
+ }
2312
+ }
2313
+ }
2314
+
2315
+ // cancel any pending requests
2316
+ if(window.stop !== undefined) {
2317
+ window.stop();
2318
+ } else if(document.execCommand !== undefined) {
2319
+ document.execCommand("Stop", false);
2320
+ }
2321
+ if (document.close !== undefined) {
2322
+ document.close();
2323
+ }
2324
+ images.cleanupDone = true;
2325
+ if (!(cause && typeof cause === "string")) {
2326
+ start();
2327
+ }
2328
+ }
2329
+ },
2330
+ renderingDone: function() {
2331
+ if (timeoutTimer) {
2332
+ window.clearTimeout(timeoutTimer);
2333
+ }
2334
+ }
2335
+
2336
+ };
2337
+
2338
+ if (options.timeout > 0) {
2339
+ timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout);
2340
+ }
2341
+ h2clog('html2canvas: Preload starts: finding background-images');
2342
+ images.firstRun = true;
2343
+
2344
+ getImages( element );
2345
+
2346
+ h2clog('html2canvas: Preload: Finding images');
2347
+ // load <img> images
2348
+ for (i = 0; i < imgLen; i+=1){
2349
+ methods.loadImage( domImages[i].getAttribute( "src" ) );
2350
+ }
2351
+
2352
+ images.firstRun = false;
2353
+ h2clog('html2canvas: Preload: Done.');
2354
+ if ( images.numTotal === images.numLoaded ) {
2355
+ start();
2356
+ }
2357
+
2358
+ return methods;
2359
+
2360
+ };
2361
+
2362
+
2363
+
2364
+ /*
2365
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
2366
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2367
+ http://www.twitter.com/niklasvh
2368
+
2369
+ Released under MIT License
2370
+ */
2371
+ function h2cRenderContext(width, height) {
2372
+ var storage = [];
2373
+ return {
2374
+ storage: storage,
2375
+ width: width,
2376
+ height: height,
2377
+ fillRect: function () {
2378
+ storage.push({
2379
+ type: "function",
2380
+ name: "fillRect",
2381
+ 'arguments': arguments
2382
+ });
2383
+ },
2384
+ drawImage: function () {
2385
+ storage.push({
2386
+ type: "function",
2387
+ name: "drawImage",
2388
+ 'arguments': arguments
2389
+ });
2390
+ },
2391
+ fillText: function () {
2392
+ storage.push({
2393
+ type: "function",
2394
+ name: "fillText",
2395
+ 'arguments': arguments
2396
+ });
2397
+ },
2398
+ setVariable: function (variable, value) {
2399
+ storage.push({
2400
+ type: "variable",
2401
+ name: variable,
2402
+ 'arguments': value
2403
+ });
2404
+ }
2405
+ };
2406
+ }
2407
+ /*
2408
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
2409
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2410
+ http://www.twitter.com/niklasvh
2411
+
2412
+ Released under MIT License
2413
+ */
2414
+ _html2canvas.Renderer = function(parseQueue, options){
2415
+
2416
+
2417
+ var queue = [];
2418
+
2419
+ function sortZ(zStack){
2420
+ var subStacks = [],
2421
+ stackValues = [],
2422
+ zStackChildren = zStack.children,
2423
+ s,
2424
+ i,
2425
+ stackLen,
2426
+ zValue,
2427
+ zLen,
2428
+ stackChild,
2429
+ b,
2430
+ subStackLen;
2431
+
2432
+
2433
+ for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){
2434
+
2435
+ stackChild = zStackChildren[s];
2436
+
2437
+ if (stackChild.children && stackChild.children.length > 0){
2438
+ subStacks.push(stackChild);
2439
+ stackValues.push(stackChild.zindex);
2440
+ }else{
2441
+ queue.push(stackChild);
2442
+ }
2443
+
2444
+ }
2445
+
2446
+ stackValues.sort(function(a, b) {
2447
+ return a - b;
2448
+ });
2449
+
2450
+ for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){
2451
+ zValue = stackValues[i];
2452
+ for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){
2453
+
2454
+ if (subStacks[b].zindex === zValue){
2455
+ stackChild = subStacks.splice(b, 1);
2456
+ sortZ(stackChild[0]);
2457
+ break;
2458
+
2459
+ }
2460
+ }
2461
+ }
2462
+
2463
+ }
2464
+
2465
+
2466
+ sortZ(parseQueue.zIndex);
2467
+ if ( typeof options.renderer._create !== "function" ) {
2468
+ throw new Error("Invalid renderer defined");
2469
+ }
2470
+ return options.renderer._create( parseQueue, options, document, queue, _html2canvas );
2471
+
2472
+ };
2473
+ /*
2474
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
2475
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2476
+ http://www.twitter.com/niklasvh
2477
+
2478
+ Released under MIT License
2479
+ */
2480
+
2481
+
2482
+ html2canvas = function( elements, opts ) {
2483
+
2484
+ var queue,
2485
+ canvas,
2486
+ options = {
2487
+ // general
2488
+ logging: false,
2489
+ elements: elements,
2490
+
2491
+ // preload options
2492
+ proxy: "http://html2canvas.appspot.com/",
2493
+ timeout: 0, // no timeout
2494
+ useCORS: false, // try to load images as CORS (where available), before falling back to proxy
2495
+ allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true
2496
+
2497
+ // parse options
2498
+ svgRendering: false, // use svg powered rendering where available (FF11+)
2499
+ iframeDefault: "default",
2500
+ ignoreElements: "IFRAME|OBJECT|PARAM",
2501
+ useOverflow: true,
2502
+ letterRendering: false,
2503
+
2504
+ // render options
2505
+
2506
+ flashcanvas: undefined, // path to flashcanvas
2507
+ width: null,
2508
+ height: null,
2509
+ taintTest: true // do a taint test with all images before applying to canvas
2510
+
2511
+ };
2512
+
2513
+ options = _html2canvas.Util.Extend(opts, options);
2514
+
2515
+ options.renderer = options.renderer || _html2canvas.Renderer.Canvas( options );
2516
+
2517
+ _html2canvas.logging = options.logging;
2518
+ options.complete = function( images ) {
2519
+
2520
+ if (typeof options.onpreloaded === "function") {
2521
+ if ( options.onpreloaded( images ) === false ) {
2522
+ return;
2523
+ }
2524
+ }
2525
+ queue = _html2canvas.Parse( images, options );
2526
+
2527
+ if (typeof options.onparsed === "function") {
2528
+ if ( options.onparsed( queue ) === false ) {
2529
+ return;
2530
+ }
2531
+ }
2532
+
2533
+ canvas = _html2canvas.Renderer( queue, options );
2534
+
2535
+ if (typeof options.onrendered === "function") {
2536
+ options.onrendered( canvas );
2537
+ }
2538
+
2539
+
2540
+ };
2541
+
2542
+ // for pages without images, we still want this to be async, i.e. return methods before executing
2543
+ window.setTimeout( function(){
2544
+ _html2canvas.Preload( options );
2545
+ }, 0 );
2546
+
2547
+ return {
2548
+ render: function( queue, opts ) {
2549
+ return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
2550
+ },
2551
+ parse: function( images, opts ) {
2552
+ return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) );
2553
+ },
2554
+ preload: function( opts ) {
2555
+ return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) );
2556
+ },
2557
+ log: h2clog
2558
+ };
2559
+ };
2560
+
2561
+ html2canvas.log = h2clog; // for renderers
2562
+ html2canvas.Renderer = {
2563
+ Canvas: undefined // We are assuming this will be used
2564
+ };
2565
+ /*
2566
+ html2canvas v0.33 <http://html2canvas.hertzen.com>
2567
+ Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2568
+ http://www.twitter.com/niklasvh
2569
+
2570
+ Released under MIT License
2571
+ */
2572
+
2573
+
2574
+ _html2canvas.Renderer.Canvas = function( options ) {
2575
+
2576
+ options = options || {};
2577
+
2578
+ var doc = document,
2579
+ canvas = options.canvas || doc.createElement('canvas'),
2580
+ usingFlashcanvas = false,
2581
+ _createCalled = false,
2582
+ canvasReadyToDraw = false,
2583
+ methods,
2584
+ flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
2585
+
2586
+
2587
+ if (canvas.getContext){
2588
+ h2clog("html2canvas: Renderer: using canvas renderer");
2589
+ canvasReadyToDraw = true;
2590
+ } else if ( options.flashcanvas !== undefined ){
2591
+ usingFlashcanvas = true;
2592
+ h2clog("html2canvas: Renderer: canvas not available, using flashcanvas");
2593
+ var script = doc.createElement("script");
2594
+ script.src = options.flashcanvas;
2595
+
2596
+ script.onload = (function(script, func){
2597
+ var intervalFunc;
2598
+
2599
+ if (script.onload === undefined) {
2600
+ // IE lack of support for script onload
2601
+
2602
+ if( script.onreadystatechange !== undefined ) {
2603
+
2604
+ intervalFunc = function() {
2605
+ if (script.readyState !== "loaded" && script.readyState !== "complete") {
2606
+ window.setTimeout( intervalFunc, 250 );
2607
+
2608
+ } else {
2609
+ // it is loaded
2610
+ func();
2611
+
2612
+ }
2613
+
2614
+ };
2615
+
2616
+ window.setTimeout( intervalFunc, 250 );
2617
+
2618
+ } else {
2619
+ h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded");
2620
+ }
2621
+
2622
+ } else {
2623
+ return func;
2624
+ }
2625
+
2626
+ })(script, function(){
2627
+
2628
+ if (typeof window.FlashCanvas !== "undefined") {
2629
+ h2clog("html2canvas: Renderer: Flashcanvas initialized");
2630
+ window.FlashCanvas.initElement( canvas );
2631
+
2632
+ canvasReadyToDraw = true;
2633
+ if ( _createCalled !== false ) {
2634
+ methods._create.apply( null, _createCalled );
2635
+ }
2636
+ }
2637
+ });
2638
+
2639
+ doc.body.appendChild( script );
2640
+
2641
+ }
2642
+
2643
+ methods = {
2644
+ _create: function( zStack, options, doc, queue, _html2canvas ) {
2645
+
2646
+ if ( !canvasReadyToDraw ) {
2647
+ _createCalled = arguments;
2648
+ return canvas;
2649
+ }
2650
+
2651
+ var ctx = canvas.getContext("2d"),
2652
+ storageContext,
2653
+ i,
2654
+ queueLen,
2655
+ a,
2656
+ newCanvas,
2657
+ bounds,
2658
+ testCanvas = document.createElement("canvas"),
2659
+ hasCTX = ( testCanvas.getContext !== undefined ),
2660
+ storageLen,
2661
+ renderItem,
2662
+ testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {},
2663
+ safeImages = [],
2664
+ fstyle;
2665
+
2666
+ canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) );
2667
+ canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) );
2668
+
2669
+ fstyle = ctx.fillStyle;
2670
+ ctx.fillStyle = zStack.backgroundColor;
2671
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
2672
+ ctx.fillStyle = fstyle;
2673
+
2674
+ if ( options.svgRendering && zStack.svgRender !== undefined ) {
2675
+ // TODO: enable async rendering to support this
2676
+ ctx.drawImage( zStack.svgRender, 0, 0 );
2677
+ } else {
2678
+ for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) {
2679
+
2680
+ storageContext = queue.splice(0, 1)[0];
2681
+ storageContext.canvasPosition = storageContext.canvasPosition || {};
2682
+
2683
+ //this.canvasRenderContext(storageContext,parentctx);
2684
+
2685
+ // set common settings for canvas
2686
+ ctx.textBaseline = "bottom";
2687
+
2688
+ if (storageContext.clip){
2689
+ ctx.save();
2690
+ ctx.beginPath();
2691
+ // console.log(storageContext);
2692
+ ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
2693
+ ctx.clip();
2694
+
2695
+ }
2696
+
2697
+ if (storageContext.ctx.storage){
2698
+
2699
+ for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
2700
+
2701
+ renderItem = storageContext.ctx.storage[a];
2702
+
2703
+
2704
+ switch(renderItem.type){
2705
+ case "variable":
2706
+ ctx[renderItem.name] = renderItem['arguments'];
2707
+ break;
2708
+ case "function":
2709
+ if (renderItem.name === "fillRect") {
2710
+
2711
+ if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) {
2712
+ ctx.fillRect.apply( ctx, renderItem['arguments'] );
2713
+ }
2714
+ }else if(renderItem.name === "fillText") {
2715
+ if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) {
2716
+ ctx.fillText.apply( ctx, renderItem['arguments'] );
2717
+ }
2718
+ }else if(renderItem.name === "drawImage") {
2719
+
2720
+ if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
2721
+ if ( hasCTX && options.taintTest ) {
2722
+ if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) {
2723
+ testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 );
2724
+ try {
2725
+ testctx.getImageData( 0, 0, 1, 1 );
2726
+ } catch(e) {
2727
+ testCanvas = doc.createElement("canvas");
2728
+ testctx = testCanvas.getContext("2d");
2729
+ continue;
2730
+ }
2731
+
2732
+ safeImages.push( renderItem['arguments'][ 0 ].src );
2733
+
2734
+ }
2735
+ }
2736
+ ctx.drawImage.apply( ctx, renderItem['arguments'] );
2737
+ }
2738
+ }
2739
+
2740
+
2741
+ break;
2742
+ default:
2743
+
2744
+ }
2745
+
2746
+ }
2747
+
2748
+ }
2749
+ if (storageContext.clip){
2750
+ ctx.restore();
2751
+ }
2752
+
2753
+ }
2754
+ }
2755
+
2756
+ h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
2757
+
2758
+ queueLen = options.elements.length;
2759
+
2760
+ if (queueLen === 1) {
2761
+ if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) {
2762
+ // crop image to the bounds of selected (single) element
2763
+ bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] );
2764
+ newCanvas = doc.createElement('canvas');
2765
+ newCanvas.width = bounds.width;
2766
+ newCanvas.height = bounds.height;
2767
+ ctx = newCanvas.getContext("2d");
2768
+
2769
+ ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height );
2770
+ canvas = null;
2771
+ return newCanvas;
2772
+ }
2773
+ } /*else {
2774
+ // TODO clip and resize multiple elements
2775
+
2776
+ for ( i = 0; i < queueLen; i+=1 ) {
2777
+ if (options.elements[ i ] instanceof Element) {
2778
+
2779
+ }
2780
+
2781
+ }
2782
+ }
2783
+ */
2784
+
2785
+
2786
+
2787
+ return canvas;
2788
+ }
2789
+ };
2790
+
2791
+ return methods;
2792
+
2793
+ };
2794
+ window.html2canvas = html2canvas;
2795
+ }(window, document));