feedbackandscreencap 0.0.4

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,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
+