feedbackandscreencap 0.0.4

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