PageSourceSearch

https://bawaslu.go.id/modules/custom/nikadevs_cms/support/js/html2canvas.js?tljyuq

js bawaslu.go.id collected 2026-09-24 09:00:04 UTC 107,536 bytes, 3,253 lines download raw bytes

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
19var _html2canvas = {},
20previousElement,
21computedCSS,
22html2canvas;
23
24
25function 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
305var 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, b
1414y ]; // 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, b
1494y + 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
2178function 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        if(url.indexOf('flickr.com') > 0) {
2245            //return;
2246        }
2247        
2248        var callback_name,
2249        scriptUrl = options.proxy,
2250        script;
2251
2252        link.href = url;
2253        url = link.href; // work around for pages with base href="" set - WARNING: this may change the url
2254
2255        callback_name = 'html2canvas_' + (count++);
2256        imageObj.callbackname = callback_name;
2257
2258        if (scriptUrl.indexOf("?") > -1) {
2259            scriptUrl += "&";
2260        } else {
2261            scriptUrl += "?";
2262        }
2263        scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name;
2264        script = doc.createElement("script");
2265
2266        window[callback_name] = function(a){
2267            if (a.substring(0,6) === "error:"){
2268                imageObj.succeeded = false;
2269                images.numLoaded++;
2270                images.numFailed++;
2271                start();
2272            } else {
2273                setImageLoadHandlers(img, imageObj);
2274                img.src = a;
2275            }
2276            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)
2277            try {
2278                delete window[callback_name];  // for all browser that support this
2279            } catch(ex) {}
2280            script.parentNode.removeChild(script);
2281            script = null;
2282            delete imageObj.script;
2283            delete imageObj.callbackname;
2284        };
2285
2286        script.setAttribute("type", "text/javascript");
2287        script.setAttribute("src", scriptUrl);
2288        imageObj.script = script;
2289        //console.log(script);
2290        window.document.body.appendChild(script);
2291
2292    }
2293
2294    function getImages (el) {
2295
2296
2297
2298        // if (!this.ignoreRe.test(el.nodeName)){
2299        //
2300        
2301        var contents = _html2canvas.Util.Children(el),
2302        i,
2303        background_image,
2304        src,
2305        img,
2306        elNodeType = false;
2307
2308        // Firefox fails with permission denied on pages with iframes
2309        try {
2310            var contentsLen = contents.length;
2311            for (i = 0;  i < contentsLen; i+=1 ){
2312                // var ignRe = new RegExp("("+this.ignoreElements+")");
2313                // if (!ignRe.test(element.nodeName)){
2314                getImages(contents[i]);
2315                // }
2316            }
2317        }
2318        catch( e ) {}
2319
2320
2321        // }
2322        try {
2323            elNodeType = el.nodeType;
2324        } catch (ex) {
2325            elNodeType = false;
2326            h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
2327        }
2328
2329        if (elNodeType === 1 || elNodeType === undefined){
2330
2331            // opera throws exception on external-content.html
2332            try {
2333                background_image = _html2canvas.Util.getCSS(el, 'backgroundImage');
2334            }catch(e) {
2335                h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
2336            }
2337            if ( background_image && background_image !== "1" && background_image !== "none" ) {
2338
2339                // TODO add multi image background support
2340
2341                if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) {
2342
2343                    img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
2344
2345                    if ( img !== undefined ){
2346                        images[background_image] = {
2347                            img: img,
2348                            succeeded: true
2349                        };
2350                        images.numTotal++;
2351                        images.numLoaded++;
2352                        start();
2353
2354                    }
2355
2356                } else {
2357                    src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]);
2358                    methods.loadImage(src);
2359                }
2360
2361                /*
2362            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"){
2363                // TODO add multi image background support
2364                src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]);
2365                methods.loadImage(src);            */
2366            }
2367        }
2368    }
2369
2370    function setImageLoadHandlers(img, imageObj) {
2371        img.onload = function() {
2372            if ( imageObj.timer !== undefined ) {
2373                // CORS succeeded
2374                window.clearTimeout( imageObj.timer );
2375            }
2376
2377            images.numLoaded++;
2378            imageObj.succeeded = true;
2379            img.onerror = img.onload = null;
2380            start();
2381        };
2382        img.onerror = function() {
2383
2384            if (img.crossOrigin === "anonymous") {
2385                // CORS failed
2386                window.clearTimeout( imageObj.timer );
2387
2388                // let's try with proxy instead
2389                if ( options.proxy ) {
2390                    var src = img.src;
2391                    img = new Image();
2392                    imageObj.img = img;
2393                    img.src = src;
2394
2395                    proxyGetImage( img.src, img, imageObj );
2396                    return;
2397                }
2398            }
2399
2400
2401            images.numLoaded++;
2402            images.numFailed++;
2403            imageObj.succeeded = false;
2404            img.onerror = img.onload = null;
2405            start();
2406
2407        };
2408
2409        // TODO Opera has no load/error event for SVG images
2410
2411        // Opera ninja onload's cached images
2412        /*
2413        window.setTimeout(function(){
2414            if ( img.width !== 0 && imageObj.succeeded === undefined ) {
2415                img.onload();
2416            }
2417        }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does.
2418         */
2419    }
2420
2421
2422    methods = {
2423        loadImage: function( src ) {
2424            var img, imageObj;
2425            if ( src && images[src] === undefined ) {
2426                img = new Image();
2427                if ( src.match(/data:image\/.*;base64,/i) ) {
2428                    img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
2429                    imageObj = images[src] = {
2430                        img: img
2431                    };
2432                    images.numTotal++;
2433                    setImageLoadHandlers(img, imageObj);
2434                } else if ( isSameOrigin( src ) || options.allowTaint ===  true ) {
2435                    imageObj = images[src] = {
2436                        img: img
2437                    };
2438                    images.numTotal++;
2439                    setImageLoadHandlers(img, imageObj);
2440                    img.src = src;
2441                } else if ( supportCORS && !options.allowTaint && options.useCORS ) {
2442                    // attempt to load with CORS
2443
2444                    img.crossOrigin = "anonymous";
2445                    imageObj = images[src] = {
2446                        img: img
2447                    };
2448                    images.numTotal++;
2449                    setImageLoadHandlers(img, imageObj);
2450                    img.src = src;
2451
2452                    // work around for https://bugs.webkit.org/show_bug.cgi?id=80028
2453                    img.customComplete = function () {
2454                        if (!this.img.complete) {
2455                            this.timer = window.setTimeout(this.img.customComplete, 100);
2456                        } else {
2457                            this.img.onerror();
2458                        }
2459                    }.bind(imageObj);
2460                    img.customComplete();
2461
2462                } else if ( options.proxy ) {
2463                    if(src.indexOf('flickr.com') == -1) {
2464                        imageObj = images[src] = {
2465                            img: img
2466                        };
2467                        images.numTotal++;
2468                        proxyGetImage( src, img, imageObj );
2469                    }
2470                }
2471            }
2472
2473        },
2474        cleanupDOM: function(cause) {
2475            var img, src;
2476            if (!images.cleanupDone) {
2477                if (cause && typeof cause === "string") {
2478                    h2clog("html2canvas: Cleanup because: " + cause);
2479                } else {
2480                    h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
2481                }
2482
2483                for (src in images) {
2484                    if (images.hasOwnProperty(src)) {
2485                        img = images[src];
2486                        if (typeof img === "object" && img.callbackname && img.succeeded === undefined) {
2487                            // cancel proxy image request
2488                            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)
2489                            try {
2490                                delete window[img.callbackname];  // for all browser that support this
2491                            } catch(ex) {}
2492                            if (img.script && img.script.parentNode) {
2493                                img.script.setAttribute("src", "about:blank");  // try to cancel running request
2494                                img.script.parentNode.removeChild(img.script);
2495                            }
2496                            images.numLoaded++;
2497                            images.numFailed++;
2498                            h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);
2499                        }
2500                    }
2501                }
2502
2503                // cancel any pending requests
2504                if(window.stop !== undefined) {
2505                    window.stop();
2506                } else if(document.execCommand !== undefined) {
2507                    document.execCommand("Stop", false);
2508                }
2509                if (document.close !== undefined) {
2510                    document.close();
2511                }
2512                images.cleanupDone = true;
2513                if (!(cause && typeof cause === "string")) {
2514                    start();
2515                }
2516            }
2517        },
2518        renderingDone: function() {
2519            if (timeoutTimer) {
2520                window.clearTimeout(timeoutTimer);
2521            }
2522        }
2523
2524    };
2525
2526    if (options.timeout > 0) {
2527        timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout);
2528    }
2529    h2clog('html2canvas: Preload starts: finding background-images');
2530    images.firstRun = true;
2531
2532    getImages( element );
2533
2534    h2clog('html2canvas: Preload: Finding images');
2535    // load <img> images
2536    for (i = 0; i < imgLen; i+=1){
2537        methods.loadImage( domImages[i].getAttribute( "src" ) );
2538    }
2539
2540    images.firstRun = false;
2541    h2clog('html2canvas: Preload: Done.');
2542    if ( images.numTotal === images.numLoaded ) {
2543        start();
2544    }
2545
2546    return methods;
2547
2548};
2549
2550
2551
2552
2553/*
2554  html2canvas v0.34 <http://html2canvas.hertzen.com>
2555  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2556  http://www.twitter.com/niklasvh
2557
2558  Released under MIT License
2559*/
2560function h2cRenderContext(width, height) {
2561    var storage = [];
2562    return {
2563        storage: storage,
2564        width: width,
2565        height: height,
2566        fillRect: function () {
2567            storage.push({
2568                type: "function",
2569                name: "fillRect",
2570                'arguments': arguments
2571            });
2572        },
2573        drawShape: function() {
2574          
2575            var shape = [];
2576            
2577            storage.push({
2578                type: "function",
2579                name: "drawShape",
2580                'arguments': shape
2581            });
2582          
2583            return {
2584                moveTo: function() {
2585                    shape.push({
2586                        name: "moveTo",
2587                        'arguments': arguments
2588                    });
2589                },
2590                lineTo: function() {
2591                    shape.push({
2592                        name: "lineTo",
2593                        'arguments': arguments
2594                    });
2595                },
2596                bezierCurveTo: function() {
2597                    shape.push({
2598                        name: "bezierCurveTo",
2599                        'arguments': arguments
2600                    });
2601                },
2602                quadraticCurveTo: function() {
2603                    shape.push({
2604                        name: "quadraticCurveTo",
2605                        'arguments': arguments
2606                    });
2607                }
2608            };
2609          
2610        },
2611        drawImage: function () {
2612            storage.push({
2613                type: "function",
2614                name: "drawImage",
2615                'arguments': arguments
2616            });
2617        },
2618        fillText: function () {
2619            storage.push({
2620                type: "function",
2621                name: "fillText",
2622                'arguments': arguments
2623            });
2624        },
2625        setVariable: function (variable, value) {
2626            storage.push({
2627                type: "variable",
2628                name: variable,
2629                'arguments': value
2630            });
2631        }
2632    };
2633}
2634
2635/*
2636  html2canvas v0.34 <http://html2canvas.hertzen.com>
2637  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2638  http://www.twitter.com/niklasvh
2639
2640  Released under MIT License
2641*/
2642_html2canvas.Renderer = function(parseQueue, options){
2643
2644
2645    var queue = [];
2646
2647    function sortZ(zStack){
2648        var subStacks = [],
2649        stackValues = [],
2650        zStackChildren = zStack.children,
2651        s,
2652        i,
2653        stackLen,
2654        zValue,
2655        zLen,
2656        stackChild,
2657        b,
2658        subStackLen;
2659
2660
2661        for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){
2662
2663            stackChild = zStackChildren[s];
2664
2665            if (stackChild.children && stackChild.children.length > 0){
2666                subStacks.push(stackChild);
2667                stackValues.push(stackChild.zindex);
2668            }else{
2669                queue.push(stackChild);
2670            }
2671
2672        }
2673
2674        stackValues.sort(function(a, b) {
2675            return a - b;
2676        });
2677
2678        for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){
2679            zValue = stackValues[i];
2680            for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){
2681
2682                if (subStacks[b].zindex === zValue){
2683                    stackChild = subStacks.splice(b, 1);
2684                    sortZ(stackChild[0]);
2685                    break;
2686
2687                }
2688            }
2689        }
2690
2691    }
2692
2693
2694    sortZ(parseQueue.zIndex);
2695    if ( typeof options._renderer._create !== "function" ) {
2696        throw new Error("Invalid renderer defined");
2697    }
2698    return options._renderer._create( parseQueue, options, document, queue, _html2canvas );
2699
2700};
2701
2702/*
2703  html2canvas v0.34 <http://html2canvas.hertzen.com>
2704  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2705  http://www.twitter.com/niklasvh
2706
2707  Released under MIT License
2708*/
2709
2710
2711html2canvas = function( elements, opts ) {
2712
2713    var queue,
2714    canvas,
2715    options = {
2716        // general
2717        logging: false,
2718        elements: elements,
2719
2720        // preload options
2721        proxy: "http://html2canvas.appspot.com/",
2722        timeout: 0,    // no timeout
2723        useCORS: false, // try to load images as CORS (where available), before falling back to proxy
2724        allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true
2725
2726        // parse options
2727        svgRendering: false, // use svg powered rendering where available (FF11+)
2728        iframeDefault: "default",
2729        ignoreElements: "IFRAME|OBJECT|PARAM",
2730        useOverflow: true,
2731        letterRendering: false,
2732
2733        // render options
2734
2735        flashcanvas: undefined, // path to flashcanvas
2736        width: null,
2737        height: null,
2738        taintTest: true, // do a taint test with all images before applying to canvas
2739    renderer: "Canvas"
2740    }, renderer;
2741
2742    options = _html2canvas.Util.Extend(opts, options);
2743
2744  if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) {
2745    options._renderer = _html2canvas.Renderer[options.renderer]( options );
2746  } else if (typeof options.renderer === "function") {
2747    options._renderer = options.renderer( options );
2748  } else {
2749    throw("Unknown renderer");
2750  }
2751
2752    _html2canvas.logging = options.logging;
2753    options.complete = function( images ) {
2754
2755        if (typeof options.onpreloaded === "function") {
2756            if ( options.onpreloaded( images ) === false ) {
2757                return;
2758            }
2759        }
2760        queue = _html2canvas.Parse( images, options );
2761
2762        if (typeof options.onparsed === "function") {
2763            if ( options.onparsed( queue ) === false ) {
2764                return;
2765            }
2766        }
2767
2768        canvas = _html2canvas.Renderer( queue, options );
2769
2770        if (typeof options.onrendered === "function") {
2771            options.onrendered( canvas );
2772        }
2773
2774
2775    };
2776
2777    // for pages without images, we still want this to be async, i.e. return methods before executing
2778    window.setTimeout( function(){
2779        _html2canvas.Preload( options );
2780    }, 0 );
2781
2782    return {
2783        render: function( queue, opts ) {
2784            return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
2785        },
2786        parse: function( images, opts ) {
2787            return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) );
2788        },
2789        preload: function( opts ) {
2790            return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) );
2791        },
2792        log: h2clog
2793    };
2794};
2795
2796html2canvas.log = h2clog; // for renderers
2797html2canvas.Renderer = {
2798    Canvas: undefined // We are assuming this will be used
2799};
2800
2801/*
2802  html2canvas v0.34 <http://html2canvas.hertzen.com>
2803  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
2804  http://www.twitter.com/niklasvh
2805
2806  Released under MIT License
2807*/
2808
2809
2810_html2canvas.Renderer.Canvas = function( options ) {
2811
2812    options = options || {};
2813
2814    var doc = document,
2815    canvas = options.canvas || doc.createElement('canvas'),
2816    usingFlashcanvas = false,
2817    _createCalled = false,
2818    canvasReadyToDraw = false,
2819    methods,
2820    flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
2821
2822
2823    if (canvas.getContext){
2824        h2clog("html2canvas: Renderer: using canvas renderer");
2825        canvasReadyToDraw = true;
2826    } else if ( options.flashcanvas !== undefined ){
2827        usingFlashcanvas = true;
2828        h2clog("html2canvas: Renderer: canvas not available, using flashcanvas");
2829        var script = doc.createElement("script");
2830        script.src = options.flashcanvas;
2831
2832        script.onload = (function(script, func){
2833            var intervalFunc;
2834
2835            if (script.onload === undefined) {
2836                // IE lack of support for script onload
2837
2838                if( script.onreadystatechange !== undefined ) {
2839
2840                    intervalFunc = function() {
2841                        if (script.readyState !== "loaded" && script.readyState !== "complete") {
2842                            window.setTimeout( intervalFunc, 250 );
2843
2844                        } else {
2845                            // it is loaded
2846                            func();
2847
2848                        }
2849
2850                    };
2851
2852                    window.setTimeout( intervalFunc, 250 );
2853
2854                } else {
2855                    h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded");
2856                }
2857
2858            } else {
2859                return func;
2860            }
2861
2862        })(script, function(){
2863
2864            if (typeof window.FlashCanvas !== "undefined") {
2865                h2clog("html2canvas: Renderer: Flashcanvas initialized");
2866                window.FlashCanvas.initElement( canvas );
2867
2868                canvasReadyToDraw = true;
2869                if ( _createCalled !== false ) {
2870                    methods._create.apply( null, _createCalled );
2871                }
2872            }
2873        });
2874
2875        doc.body.appendChild( script );
2876
2877    }
2878
2879    methods = {
2880        _create: function( zStack, options, doc, queue, _html2canvas ) {
2881
2882            if ( !canvasReadyToDraw ) {
2883                _createCalled = arguments;
2884                return canvas;
2885            }
2886
2887            var ctx = canvas.getContext("2d"),
2888            storageContext,
2889            i,
2890            queueLen,
2891            a,
2892            newCanvas,
2893            bounds,
2894            testCanvas = document.createElement("canvas"),
2895            hasCTX = ( testCanvas.getContext !== undefined ),
2896            storageLen,
2897            renderItem,
2898            testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {},
2899            safeImages = [],
2900            fstyle;
2901
2902            canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) );
2903            canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) );
2904
2905            fstyle = ctx.fillStyle;
2906            ctx.fillStyle = zStack.backgroundColor;
2907            ctx.fillRect(0, 0, canvas.width, canvas.height);
2908            ctx.fillStyle = fstyle;
2909
2910            if ( options.svgRendering && zStack.svgRender !== undefined ) {
2911                // TODO: enable async rendering to support this
2912                ctx.drawImage( zStack.svgRender, 0, 0 );
2913            } else {
2914                for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) {
2915
2916                    storageContext = queue.splice(0, 1)[0];
2917                    storageContext.canvasPosition = storageContext.canvasPosition || {};
2918
2919                    //this.canvasRenderContext(storageContext,parentctx);
2920
2921                    // set common settings for canvas
2922                    ctx.textBaseline = "bottom";
2923
2924                    if (storageContext.clip){
2925                        ctx.save();
2926                        ctx.beginPath();
2927                        // console.log(storageContext);
2928                        ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
2929                        ctx.clip();
2930
2931                    }
2932
2933                    if (storageContext.ctx.storage){
2934
2935                        for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
2936
2937                            renderItem = storageContext.ctx.storage[a];
2938
2939
2940                            switch(renderItem.type){
2941                                case "variable":
2942                                    ctx[renderItem.name] = renderItem['arguments'];
2943                                    break;
2944                                case "function":
2945                                    if (renderItem.name === "fillRect") {
2946
2947                                        if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize  && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) {
2948                                            ctx.fillRect.apply( ctx, renderItem['arguments'] );
2949                                        }
2950                                    } else if (renderItem.name === "drawShape") {
2951                                        
2952                                        ( function( args ) {
2953                                          
2954                                            var i, len = args.length;
2955                                            ctx.beginPath();
2956                                            for ( i = 0; i < len; i++ ) {   
2957                                                ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] );
2958                                            }
2959                                            ctx.closePath();
2960                                            ctx.fill();
2961                                        })( renderItem['arguments'] );
2962                                        
2963                                    } else if (renderItem.name === "fillText") {
2964                                        if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize  && renderItem['arguments'][2] < flashMaxSize) {
2965                                            ctx.fillText.apply( ctx, renderItem['arguments'] );
2966                                        }
2967                                    } else if (renderItem.name === "drawImage") {
2968
2969                                        if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
2970                                            if ( hasCTX && options.taintTest ) {
2971                                                if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) {
2972                                                    testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 );
2973                                                    try {
2974                                                        testctx.getImageData( 0, 0, 1, 1 );
2975                                                    } catch(e) {
2976                                                        testCanvas = doc.createElement("canvas");
2977                                                        testctx = testCanvas.getContext("2d");
2978                                                        continue;
2979                                                    }
2980
2981                                                    safeImages.push( renderItem['arguments'][ 0 ].src );
2982
2983                                                }
2984                                            }
2985                                            ctx.drawImage.apply( ctx, renderItem['arguments'] );
2986                                        }
2987                                    }
2988
2989
2990                                    break;
2991                                default:
2992
2993                            }
2994
2995                        }
2996
2997                    }
2998                    if (storageContext.clip){
2999                        ctx.restore();
3000                    }
3001
3002                }
3003            }
3004
3005            h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
3006
3007            queueLen = options.elements.length;
3008
3009            if (queueLen === 1) {
3010                if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) {
3011                    // crop image to the bounds of selected (single) element
3012                    bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] );
3013                    newCanvas = doc.createElement('canvas');
3014                    newCanvas.width = bounds.width;
3015                    newCanvas.height = bounds.height;
3016                    ctx = newCanvas.getContext("2d");
3017
3018                    ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height );
3019                    canvas = null;
3020                    return newCanvas;
3021                }
3022            } /*else {
3023        // TODO clip and resize multiple elements
3024
3025            for ( i = 0; i < queueLen; i+=1 ) {
3026                if (options.elements[ i ] instanceof Element) {
3027
3028                }
3029
3030            }
3031        }
3032        */
3033
3034
3035
3036            return canvas;
3037        }
3038    };
3039
3040    return methods;
3041
3042};
3043
3044/*
3045  html2canvas v0.34 <http://html2canvas.hertzen.com>
3046  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
3047  http://www.twitter.com/niklasvh
3048
3049  Released under MIT License
3050*/
3051
3052
3053// WARNING THIS file is outdated, and hasn't been tested in quite a while
3054
3055_html2canvas.Renderer.SVG = function( options ) {
3056
3057    options = options || {};
3058
3059    var doc = document,
3060    svgNS = "http://www.w3.org/2000/svg",
3061    svg = doc.createElementNS(svgNS, "svg"),
3062    xlinkNS = "http://www.w3.org/1999/xlink",
3063    defs = doc.createElementNS(svgNS, "defs"),
3064    i,
3065    a,
3066    queueLen,
3067    storageLen,
3068    storageContext,
3069    renderItem,
3070    el,
3071    settings = {},
3072    text,
3073    fontStyle,
3074    clipId = 0,
3075    methods;
3076
3077
3078    methods = {
3079        _create: function( zStack, options, doc, queue, _html2canvas ) {
3080            svg.setAttribute("version", "1.1");
3081            svg.setAttribute("baseProfile", "full");
3082
3083            svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height));
3084            svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px");
3085            svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px");
3086            svg.setAttribute("preserveAspectRatio", "none");
3087            svg.appendChild(defs);
3088
3089
3090
3091            for (i = 0, queueLen = queue.length; i < queueLen; i+=1){
3092
3093                storageContext = queue.splice(0, 1)[0];
3094                storageContext.canvasPosition = storageContext.canvasPosition || {};
3095
3096                //this.canvasRenderContext(storageContext,parentctx);
3097
3098
3099                /*
3100            if (storageContext.clip){
3101                ctx.save();
3102                ctx.beginPath();
3103                // console.log(storageContext);
3104                ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
3105                ctx.clip();
3106
3107            }*/
3108
3109                if (storageContext.ctx.storage){
3110
3111                    for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
3112
3113                        renderItem = storageContext.ctx.storage[a];
3114
3115
3116
3117                        switch(renderItem.type){
3118                            case "variable":
3119                                settings[renderItem.name] = renderItem['arguments'];
3120                                break;
3121                            case "function":
3122                                if (renderItem.name === "fillRect") {
3123
3124                                    el = doc.createElementNS(svgNS, "rect");
3125                                    el.setAttribute("x", renderItem['arguments'][0]);
3126                                    el.setAttribute("y", renderItem['arguments'][1]);
3127                                    el.setAttribute("width", renderItem['arguments'][2]);
3128                                    el.setAttribute("height", renderItem['arguments'][3]);
3129                                    el.setAttribute("fill",  settings.fillStyle);
3130                                    svg.appendChild(el);
3131
3132                                } else if(renderItem.name === "fillText") {
3133                                    el = doc.createElementNS(svgNS, "text");
3134
3135                                    fontStyle = settings.font.split(" ");
3136
3137                                    el.style.fontVariant = fontStyle.splice(0, 1)[0];
3138                                    el.style.fontWeight = fontStyle.splice(0, 1)[0];
3139                                    el.style.fontStyle = fontStyle.splice(0, 1)[0];
3140                                    el.style.fontSize = fontStyle.splice(0, 1)[0];
3141
3142                                    el.setAttribute("x", renderItem['arguments'][1]);
3143                                    el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3));
3144
3145                                    el.setAttribute("fill", settings.fillStyle);
3146
3147
3148
3149
3150                                    // TODO get proper baseline
3151                                    el.style.dominantBaseline = "text-before-edge";
3152                                    el.style.fontFamily = fontStyle.join(" ");
3153
3154                                    text = doc.createTextNode(renderItem['arguments'][0]);
3155                                    el.appendChild(text);
3156
3157
3158                                    svg.appendChild(el);
3159
3160
3161
3162                                } else if(renderItem.name === "drawImage") {
3163
3164                                    if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
3165
3166                                        // TODO check whether even any clipping is necessary for this particular image
3167                                        el = doc.createElementNS(svgNS, "clipPath");
3168                                        el.setAttribute("id", "clipId" + clipId);
3169
3170                                        text = doc.createElementNS(svgNS, "rect");
3171                                        text.setAttribute("x",  renderItem['arguments'][5] );
3172                                        text.setAttribute("y", renderItem['arguments'][6]);
3173
3174                                        text.setAttribute("width", renderItem['arguments'][3]);
3175                                        text.setAttribute("height", renderItem['arguments'][4]);
3176                                        el.appendChild(text);
3177                                        defs.appendChild(el);
3178
3179                                        el = doc.createElementNS(svgNS, "image");
3180                                        el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src);
3181                                        el.setAttribute("width", renderItem['arguments'][7]);
3182                                        el.setAttribute("height", renderItem['arguments'][8]);
3183                                        el.setAttribute("x", renderItem['arguments'][5]);
3184                                        el.setAttribute("y", renderItem['arguments'][6]);
3185                                        el.setAttribute("clip-path", "url(#clipId" + clipId + ")");
3186                                        // el.setAttribute("xlink:href", );
3187
3188
3189                                        el.setAttribute("preserveAspectRatio", "none");
3190
3191                                        svg.appendChild(el);
3192
3193
3194                                        clipId += 1;
3195                                    /*
3196                                    ctx.drawImage(
3197                                        renderItem['arguments'][0],
3198                                        renderItem['arguments'][1],
3199                                        renderItem['arguments'][2],
3200                                        renderItem['arguments'][3],
3201                                        renderItem['arguments'][4],
3202                                        renderItem['arguments'][5],
3203                                        renderItem['arguments'][6],
3204                                        renderItem['arguments'][7],
3205                                        renderItem['arguments'][8]
3206                                        );
3207                                        */
3208                                    }
3209                                }
3210
3211
3212
3213                                break;
3214                            default:
3215
3216                        }
3217
3218                    }
3219
3220                }
3221            /*
3222            if (storageContext.clip){
3223                ctx.restore();
3224            }
3225    */
3226
3227
3228
3229            }
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240            h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj");
3241
3242            return svg;
3243        }
3244    };
3245
3246    return methods;
3247
3248
3249};
3250
3251window.html2canvas = html2canvas;
3252}(window, document));
3253

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.