├── Leaflet └── dist │ ├── images │ ├── layers.png │ ├── marker.png │ ├── zoom-in.png │ ├── zoom-out.png │ ├── popup-close.png │ └── marker-shadow.png │ ├── leaflet.ie.css │ ├── leaflet.css │ └── leaflet.js ├── ajax └── saveMap.php ├── README ├── proxy.php ├── index.html └── js ├── jquery.plugin.html2canvas.js ├── flashcanvas.min.js ├── html2canvas.min.js └── html2canvas.js /Leaflet/dist/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/layers.png -------------------------------------------------------------------------------- /Leaflet/dist/images/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/marker.png -------------------------------------------------------------------------------- /Leaflet/dist/images/zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/zoom-in.png -------------------------------------------------------------------------------- /Leaflet/dist/images/zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/zoom-out.png -------------------------------------------------------------------------------- /Leaflet/dist/images/popup-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/popup-close.png -------------------------------------------------------------------------------- /Leaflet/dist/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tegansnyder/Leaflet-Save-Map-to-PNG/HEAD/Leaflet/dist/images/marker-shadow.png -------------------------------------------------------------------------------- /ajax/saveMap.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Saving a Leaflet Map to a PNG Example using Javascript and PHP 2 | ============================================================================= 3 | 4 | This is an example of taking a Leaflet map and saving it to a PNG. 5 | 6 | To run the source example unzip the files into a directory on your webserver. 7 | Make sure to CHMOD the savedMaps directory 777. 8 | 9 | Load it up in a web browser and click the "Save to PNG button". Give it a few seconds. 10 | 11 | 12 | ============================================================================= 13 | 14 | Many thanks to: 15 | 16 | html2canvas by Niklas von Hertzen 17 | https://github.com/niklasvh/html2canvas 18 | 19 | Jamund Ferguson 20 | http://j-query.blogspot.com/2011/02/save-base64-encoded-canvas-image-to-png.html -------------------------------------------------------------------------------- /Leaflet/dist/leaflet.ie.css: -------------------------------------------------------------------------------- 1 | .leaflet-tile { 2 | filter: inherit; 3 | } 4 | 5 | .leaflet-vml-shape { 6 | width: 1px; 7 | height: 1px; 8 | } 9 | .lvml { 10 | behavior: url(#default#VML); 11 | display: inline-block; 12 | position: absolute; 13 | } 14 | 15 | .leaflet-control { 16 | display: inline; 17 | } 18 | 19 | .leaflet-popup-tip { 20 | width: 21px; 21 | _width: 27px; 22 | margin: 0 auto; 23 | _margin-top: -3px; 24 | 25 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 26 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 27 | } 28 | .leaflet-popup-tip-container { 29 | margin-top: -1px; 30 | } 31 | .leaflet-popup-content-wrapper, .leaflet-popup-tip { 32 | border: 1px solid #bbb; 33 | } 34 | 35 | .leaflet-control-zoom { 36 | filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#3F000000',EndColorStr='#3F000000'); 37 | } 38 | .leaflet-control-zoom a { 39 | background-color: #eee; 40 | } 41 | .leaflet-control-zoom a:hover { 42 | background-color: #fff; 43 | } 44 | .leaflet-control-layers-toggle { 45 | } 46 | .leaflet-control-attribution, .leaflet-control-layers { 47 | background: white; 48 | } -------------------------------------------------------------------------------- /proxy.php: -------------------------------------------------------------------------------- 1 | $pathinfo, 53 | "error" => $error, 54 | "data" => $data, 55 | "mime_type" => $mime_type 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Save Leaflet Map to PNG 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 45 | 46 | 47 | 48 | 49 | Save this to a png 50 | 51 |
52 | 53 | 54 | 55 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /js/jquery.plugin.html2canvas.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license html2canvas v0.33 3 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 4 | http://www.twitter.com/niklasvh 5 | 6 | Released under MIT License 7 | */ 8 | /* 9 | * jQuery helper plugin for examples and tests 10 | */ 11 | (function( $ ){ 12 | $.fn.html2canvas = function(options) { 13 | if (options && options.profile && window.console && window.console.profile) { 14 | console.profile(); 15 | } 16 | var date = new Date(), 17 | $message = null, 18 | timeoutTimer = false, 19 | timer = date.getTime(); 20 | options = options || {}; 21 | options.elements = this; 22 | options.flashcanvas = "js/flashcanvas.min.js"; 23 | 24 | html2canvas.logging = options && options.logging; 25 | options.complete = function(images){ 26 | var queue = html2canvas.Parse(this[0], images, options), 27 | $canvas = $(html2canvas.Renderer(queue, options)), 28 | finishTime = new Date(); 29 | 30 | if (options && options.profile && window.console && window.console.profileEnd) { 31 | console.profileEnd(); 32 | } 33 | $canvas.css({ 34 | position: 'absolute', 35 | left: 0, 36 | top: 0, 37 | display: 'none' 38 | }).appendTo(document.body); 39 | 40 | $canvas.attr('id', 'savedMap'); 41 | 42 | manipulateCanvasFunction(document.getElementById('savedMap')); 43 | 44 | /* 45 | $canvas.siblings().toggle(); 46 | 47 | $(window).click(function(){ 48 | $canvas.toggle().siblings().toggle(); 49 | throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden")); 50 | }); 51 | throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms
",4000); 52 | 53 | */ 54 | 55 | // test if canvas is read-able 56 | try { 57 | $canvas[0].toDataURL(); 58 | } catch(e) { 59 | if ($canvas[0].nodeName.toLowerCase() === "canvas") { 60 | // TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed 61 | alert("Canvas is tainted, unable to read data"); 62 | } 63 | } 64 | 65 | }; 66 | html2canvas.Preload(this[0], options); 67 | 68 | function throwMessage(msg,duration){ 69 | window.clearTimeout(timeoutTimer); 70 | timeoutTimer = window.setTimeout(function(){ 71 | $message.fadeOut(function(){ 72 | $message.remove(); 73 | $message = null; 74 | }); 75 | },duration || 2000); 76 | if ($message) 77 | $message.remove(); 78 | $message = $('
').html(msg).css({ 79 | margin:0, 80 | padding:10, 81 | background: "#000", 82 | opacity:0.7, 83 | position:"fixed", 84 | top:10, 85 | right:10, 86 | fontFamily: 'Tahoma', 87 | color:'#fff', 88 | fontSize:12, 89 | borderRadius:12, 90 | width:'auto', 91 | height:'auto', 92 | textAlign:'center', 93 | textDecoration:'none', 94 | display:'none' 95 | }).appendTo(document.body).fadeIn(); 96 | html2canvas.log(msg); 97 | } 98 | }; 99 | })( jQuery ); 100 | 101 | -------------------------------------------------------------------------------- /Leaflet/dist/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-map-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-pane, 8 | .leaflet-overlay-pane, 9 | .leaflet-shadow-pane, 10 | .leaflet-marker-pane, 11 | .leaflet-popup-pane, 12 | .leaflet-overlay-pane svg, 13 | .leaflet-zoom-box, 14 | .leaflet-image-layer { /* TODO optimize classes */ 15 | position: absolute; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | } 20 | .leaflet-tile-pane, .leaflet-container { 21 | -webkit-transform: translate3d(0,0,0); 22 | } 23 | .leaflet-tile, 24 | .leaflet-marker-icon, 25 | .leaflet-marker-shadow { 26 | -moz-user-select: none; 27 | -webkit-user-select: none; 28 | user-select: none; 29 | } 30 | .leaflet-marker-icon, 31 | .leaflet-marker-shadow { 32 | display: block; 33 | } 34 | .leaflet-clickable { 35 | cursor: pointer; 36 | } 37 | .leaflet-container img { 38 | max-width: none !important; 39 | } 40 | 41 | .leaflet-tile-pane { z-index: 2; } 42 | 43 | .leaflet-objects-pane { z-index: 3; } 44 | .leaflet-overlay-pane { z-index: 4; } 45 | .leaflet-shadow-pane { z-index: 5; } 46 | .leaflet-marker-pane { z-index: 6; } 47 | .leaflet-popup-pane { z-index: 7; } 48 | 49 | .leaflet-zoom-box { 50 | width: 0; 51 | height: 0; 52 | } 53 | 54 | .leaflet-tile { 55 | visibility: hidden; 56 | } 57 | .leaflet-tile-loaded { 58 | visibility: inherit; 59 | } 60 | 61 | a.leaflet-active { 62 | outline: 2px solid orange; 63 | } 64 | 65 | 66 | /* Leaflet controls */ 67 | 68 | .leaflet-control { 69 | position: relative; 70 | z-index: 7; 71 | } 72 | .leaflet-top, 73 | .leaflet-bottom { 74 | position: absolute; 75 | } 76 | .leaflet-top { 77 | top: 0; 78 | } 79 | .leaflet-right { 80 | right: 0; 81 | } 82 | .leaflet-bottom { 83 | bottom: 0; 84 | } 85 | .leaflet-left { 86 | left: 0; 87 | } 88 | .leaflet-control { 89 | float: left; 90 | clear: both; 91 | } 92 | .leaflet-right .leaflet-control { 93 | float: right; 94 | } 95 | .leaflet-top .leaflet-control { 96 | margin-top: 10px; 97 | } 98 | .leaflet-bottom .leaflet-control { 99 | margin-bottom: 10px; 100 | } 101 | .leaflet-left .leaflet-control { 102 | margin-left: 10px; 103 | } 104 | .leaflet-right .leaflet-control { 105 | margin-right: 10px; 106 | } 107 | 108 | .leaflet-control-zoom, .leaflet-control-layers { 109 | -moz-border-radius: 7px; 110 | -webkit-border-radius: 7px; 111 | border-radius: 7px; 112 | } 113 | .leaflet-control-zoom { 114 | padding: 5px; 115 | background: rgba(0, 0, 0, 0.25); 116 | } 117 | .leaflet-control-zoom a { 118 | background-color: rgba(255, 255, 255, 0.75); 119 | } 120 | .leaflet-control-zoom a, .leaflet-control-layers a { 121 | background-position: 50% 50%; 122 | background-repeat: no-repeat; 123 | display: block; 124 | } 125 | .leaflet-control-zoom a { 126 | -moz-border-radius: 4px; 127 | -webkit-border-radius: 4px; 128 | border-radius: 4px; 129 | width: 19px; 130 | height: 19px; 131 | } 132 | .leaflet-control-zoom a:hover { 133 | background-color: #fff; 134 | } 135 | .leaflet-big-buttons .leaflet-control-zoom a { 136 | width: 27px; 137 | height: 27px; 138 | } 139 | .leaflet-control-zoom-in { 140 | background-image: url(images/zoom-in.png); 141 | margin-bottom: 5px; 142 | } 143 | .leaflet-control-zoom-out { 144 | background-image: url(images/zoom-out.png); 145 | } 146 | 147 | .leaflet-control-layers { 148 | -moz-box-shadow: 0 0 7px #999; 149 | -webkit-box-shadow: 0 0 7px #999; 150 | box-shadow: 0 0 7px #999; 151 | 152 | background: #f8f8f9; 153 | } 154 | .leaflet-control-layers a { 155 | background-image: url(images/layers.png); 156 | width: 36px; 157 | height: 36px; 158 | } 159 | .leaflet-big-buttons .leaflet-control-layers a { 160 | width: 44px; 161 | height: 44px; 162 | } 163 | .leaflet-control-layers .leaflet-control-layers-list, 164 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 165 | display: none; 166 | } 167 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 168 | display: block; 169 | position: relative; 170 | } 171 | .leaflet-control-layers-expanded { 172 | padding: 6px 10px 6px 6px; 173 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 174 | color: #333; 175 | background: #fff; 176 | } 177 | .leaflet-control-layers input { 178 | margin-top: 2px; 179 | position: relative; 180 | top: 1px; 181 | } 182 | .leaflet-control-layers label { 183 | display: block; 184 | } 185 | .leaflet-control-layers-separator { 186 | height: 0; 187 | border-top: 1px solid #ddd; 188 | margin: 5px -10px 5px -6px; 189 | } 190 | 191 | .leaflet-container .leaflet-control-attribution { 192 | margin: 0; 193 | padding: 0 5px; 194 | 195 | font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 196 | color: #333; 197 | 198 | background-color: rgba(255, 255, 255, 0.7); 199 | 200 | -moz-box-shadow: 0 0 7px #ccc; 201 | -webkit-box-shadow: 0 0 7px #ccc; 202 | box-shadow: 0 0 7px #ccc; 203 | } 204 | 205 | 206 | /* Fade animations */ 207 | 208 | .leaflet-fade-anim .leaflet-tile { 209 | opacity: 0; 210 | 211 | -webkit-transition: opacity 0.2s linear; 212 | -moz-transition: opacity 0.2s linear; 213 | -o-transition: opacity 0.2s linear; 214 | transition: opacity 0.2s linear; 215 | } 216 | .leaflet-fade-anim .leaflet-tile-loaded { 217 | opacity: 1; 218 | } 219 | 220 | .leaflet-fade-anim .leaflet-popup { 221 | opacity: 0; 222 | 223 | -webkit-transition: opacity 0.2s linear; 224 | -moz-transition: opacity 0.2s linear; 225 | -o-transition: opacity 0.2s linear; 226 | transition: opacity 0.2s linear; 227 | } 228 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 229 | opacity: 1; 230 | } 231 | 232 | .leaflet-zoom-anim .leaflet-tile { 233 | -webkit-transition: none; 234 | -moz-transition: none; 235 | -o-transition: none; 236 | transition: none; 237 | } 238 | 239 | .leaflet-zoom-anim .leaflet-objects-pane { 240 | visibility: hidden; 241 | } 242 | 243 | 244 | /* Popup layout */ 245 | 246 | .leaflet-popup { 247 | position: absolute; 248 | text-align: center; 249 | -webkit-transform: translate3d(0,0,0); 250 | } 251 | .leaflet-popup-content-wrapper { 252 | padding: 1px; 253 | text-align: left; 254 | } 255 | .leaflet-popup-content { 256 | margin: 19px; 257 | } 258 | .leaflet-popup-tip-container { 259 | margin: 0 auto; 260 | width: 40px; 261 | height: 16px; 262 | position: relative; 263 | overflow: hidden; 264 | } 265 | .leaflet-popup-tip { 266 | width: 15px; 267 | height: 15px; 268 | padding: 1px; 269 | 270 | margin: -8px auto 0; 271 | 272 | -moz-transform: rotate(45deg); 273 | -webkit-transform: rotate(45deg); 274 | -ms-transform: rotate(45deg); 275 | -o-transform: rotate(45deg); 276 | transform: rotate(45deg); 277 | } 278 | .leaflet-popup-close-button { 279 | position: absolute; 280 | top: 9px; 281 | right: 9px; 282 | 283 | width: 10px; 284 | height: 10px; 285 | 286 | overflow: hidden; 287 | } 288 | .leaflet-popup-content p { 289 | margin: 18px 0; 290 | } 291 | 292 | 293 | /* Visual appearance */ 294 | 295 | .leaflet-container { 296 | background: #ddd; 297 | } 298 | .leaflet-container a { 299 | color: #0078A8; 300 | } 301 | .leaflet-zoom-box { 302 | border: 2px dotted #05f; 303 | background: white; 304 | opacity: 0.5; 305 | } 306 | .leaflet-popup-content-wrapper, .leaflet-popup-tip { 307 | background: white; 308 | 309 | box-shadow: 0 1px 10px #888; 310 | -moz-box-shadow: 0 1px 10px #888; 311 | -webkit-box-shadow: 0 1px 14px #999; 312 | } 313 | .leaflet-popup-content-wrapper { 314 | -moz-border-radius: 20px; 315 | -webkit-border-radius: 20px; 316 | border-radius: 20px; 317 | } 318 | .leaflet-popup-content { 319 | font: 12px/1.4 "Helvetica Neue", Arial, Helvetica, sans-serif; 320 | } 321 | .leaflet-popup-close-button { 322 | background: white url(images/popup-close.png); 323 | } 324 | -------------------------------------------------------------------------------- /js/flashcanvas.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * FlashCanvas 3 | * 4 | * Copyright (c) 2009 Tim Cameron Ryan 5 | * Copyright (c) 2009-2011 FlashCanvas Project 6 | * Released under the MIT/X License 7 | */ 8 | window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&").replace(/0)return eval(this.B.CallFunction(''+a.join("�")+""))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR", 25 | 12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML=''; 26 | s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()}, 27 | setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+ 28 | "//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document); 29 | -------------------------------------------------------------------------------- /js/html2canvas.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | html2canvas v0.33 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(j,L,n){function C(a){l.logging&&j.console&&j.console.log&&j.console.log(a)}function S(a,b){var h=[];return{storage:h,width:a,height:b,fillRect:function(){h.push({type:"function",name:"fillRect",arguments:arguments})},drawImage:function(){h.push({type:"function",name:"drawImage",arguments:arguments})},fillText:function(){h.push({type:"function",name:"fillText",arguments:arguments})},setVariable:function(a,b){h.push({type:"variable",name:a,arguments:b})}}}var l={logging:!1};l.log=C;l.Util= 9 | {};l.Util.backgroundImage=function(a){if(/data:image\/.*;base64,/i.test(a)||/^(-webkit|-moz|linear-gradient|-o-)/.test(a))return a;a.toLowerCase().substr(0,5)==='url("'?(a=a.substr(5),a=a.substr(0,a.length-2)):(a=a.substr(4),a=a.substr(0,a.length-1));return a};l.Util.Bounds=function(a){var b={};if(a.getBoundingClientRect)return a=a.getBoundingClientRect(),b.top=a.top,b.bottom=a.bottom||a.top+a.height,b.left=a.left,b.width=a.width||a.right-a.left,b.height=a.height||a.bottom-a.top,b};l.Util.getCSS= 10 | function(a,b){return $(a).css(b)};l.Util.Extend=function(a,b){for(var h in a)a.hasOwnProperty(h)&&(b[h]=a[h]);return b};l.Util.Children=function(a){var b;try{b=$(a).contents()}catch(h){C("html2canvas.Util.Children failed with exception: "+h.message),b=[]}return b};l.Generate={};l.Generate.Gradient=function(a,b){function h(a){for(var d=-1,b="",r;d++26);return b};l.Generate.ListRoman=function(a){var b=["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"],h=[1E3,900,500,400,100,90,50,40,10,9,5,4,1],l="",j,e=b.length;if(a<=0||a>=4E3)return a;for(j=0;j=h[j];)a-=h[j],l+=b[j];return l};l.Parse=function(a,b,h){function I(c,g){var i=parseInt(t(c,g),10);return isNaN(i)?0:i}function F(c,g,a,H,d,m){m!=="transparent"&&(c.setVariable("fillStyle", 14 | m),c.fillRect(g,a,H,d),i+=1)}function e(c,g){switch(g){case "lowercase":return c.toLowerCase();case "capitalize":return c.replace(/(^|\s|:|-|\(|\))([a-z])/g,function(c,g,i){if(c.length>0)return g+i.toUpperCase()});case "uppercase":return c.toUpperCase();default:return c}}function A(c){return c.replace(/^\s*/g,"").replace(/\s*$/g,"")}function f(c,a,d){var d=d.ctx,H=t(c,"fontFamily"),b=t(c,"fontSize"),r=t(c,"color"),G=t(c,"textDecoration"),h=t(c,"textAlign"),s=t(c,"letterSpacing"),f,D,o=t(c,"fontWeight"), 15 | u=t(c,"fontStyle"),j=t(c,"fontVariant"),v=0;a.nodeValue=e(a.nodeValue,t(c,"textTransform"));if(A(a.nodeValue).length>0){if(G!=="none")if(m[H+"-"+b]!==n)D=m[H+"-"+b];else{D=k.createElement("div");var c=k.createElement("img"),p=k.createElement("span"),z;D.style.visibility="hidden";D.style.fontFamily=H;D.style.fontSize=b;D.style.margin=0;D.style.padding=0;x.appendChild(D);c.src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=";c.width=1;c.height=1;c.style.margin=0;c.style.padding= 16 | 0;c.style.verticalAlign="baseline";p.style.fontFamily=H;p.style.fontSize=b;p.style.margin=0;p.style.padding=0;p.appendChild(k.createTextNode("Hidden Text"));D.appendChild(p);D.appendChild(c);z=c.offsetTop-p.offsetTop+1;D.removeChild(p);D.appendChild(k.createTextNode("Hidden Text"));D.style.lineHeight="normal";c.style.verticalAlign="super";c={baseline:z,lineWidth:1,middle:c.offsetTop-D.offsetTop+1};m[H+"-"+b]=c;x.removeChild(D);D=c}h=h.replace(["-webkit-auto"],["auto"]);h=g.letterRendering===!1&&/^(left|right|justify|auto)$/.test(h)&& 17 | /^(normal|none)$/.test(s)?a.nodeValue.split(/(\b| )/):a.nodeValue.split("");switch(parseInt(o,10)){case 401:o="bold";break;case 400:o="normal"}d.setVariable("fillStyle",r);d.setVariable("font",u+" "+j+" "+o+" "+b+" "+H);d.setVariable("textAlign","left");H=a;for(b=0;b0&&(s.fillText(o,u,j),i+=1);switch(G){case "underline":F(d,f.left,Math.round(f.top+D.baseline+D.lineWidth),f.width,1,r);break;case "overline":F(d,f.left,f.top,f.width,1,r);break;case "line-through":F(d,f.left,Math.ceil(f.top+D.middle+D.lineWidth), 19 | f.width,1,r)}v+=h[b].length}}}function d(c){return(c=b[c])&&c.succeeded===!0?c.img:!1}function K(c,g){var a=Math.max(c.left,g.left),i=Math.max(c.top,g.top);return{left:a,top:i,width:Math.min(c.left+c.width,g.left+g.width)-a,height:Math.min(c.top+c.height,g.top+g.height)-i}}function w(c,g){if(!g)return this.zStack={zindex:0,children:[]};if(c!=="auto"){var a={zindex:c,children:[]};g.children.push(a);return a}return g}function y(c,g,a,i){for(var d=a.left,b=a.top,m=a.width,a=a.height,f,r,h,k,G,e=function(c){var g= 20 | [],a=["Top","Right","Bottom","Left"],i;for(i=0;i<4;i+=1)g.push({width:I(c,"border"+a[i]+"Width"),color:t(c,"border"+a[i]+"Color")});return g}(c),c=0;c<4;c+=1)if(f=e[c],f.width>0){r=d;h=b;k=m;G=a-e[2].width;switch(c){case 0:G=e[0].width;break;case 1:r=d+m-e[1].width;k=e[1].width;break;case 2:h=h+a-e[2].width;G=e[2].width;break;case 3:k=e[3].width}k={left:r,top:h,width:k,height:G};i&&(k=K(k,i));k.width>0&&k.height>0&&F(g,r,h,k.width,k.height,f.color)}return e}function r(c,g,a){var i=k.createElement("valuewrap"), 21 | d=["lineHeight","textAlign","fontFamily","color","fontSize","paddingLeft","paddingTop","width","height","border","borderLeftWidth","borderTopWidth"],b,m,r;b=0;for(m=d.length;b0&&(h=m-a);k-d>0&&(f=k-d);c.drawImage(g,h,f,b-h,r-f,a+h,d+f,b-h,r-f);i+=1}function O(c,g,a,i,d,b,r){var r=Math.min(g.height,r),m,h;a.left-=Math.ceil(a.left/g.width)*g.width;for(h=i+a.left;hb+i?b+i-h:g.width,M(c,g,h,d+a.top,m,r,i,d),h=Math.floor(h+g.width)}function z(c,a){var b=l.Util.Bounds(c),h=b.left, 24 | m=b.top,f=b.width,e=b.height,j,s=t(c,"backgroundColor"),z=t(c,"position"),B,o=t(c,"opacity"),u,q;a?P={}:(P={width:Math.max(Math.max(k.body.scrollWidth,k.documentElement.scrollWidth),Math.max(k.body.offsetWidth,k.documentElement.offsetWidth),Math.max(k.body.clientWidth,k.documentElement.clientWidth)),height:Math.max(Math.max(k.body.scrollHeight,k.documentElement.scrollHeight),Math.max(k.body.offsetHeight,k.documentElement.offsetHeight),Math.max(k.body.clientHeight,k.documentElement.clientHeight))}, 25 | a={opacity:1});B=w(t(c,"zIndex"),a.zIndex);u={ctx:S(P.width||f,P.height||e),zIndex:B,opacity:o*a.opacity,cssPosition:z};if(a.clip)u.clip=l.Util.Extend({},a.clip);if(g.useOverflow===!0&&/(hidden|scroll|auto)/.test(t(c,"overflow"))===!0&&/(BODY)/i.test(c.nodeName)===!1)u.clip=u.clip?K(u.clip,b):b;z=B.children.push(u);q=B.children[z-1].ctx;q.setVariable("globalAlpha",u.opacity);o=y(c,q,b,!1);u.borders=o;Q.test(c.nodeName)&&g.iframeDefault!=="transparent"&&(s=g.iframeDefault==="default"?"#efefef":g.iframeDefault); 26 | f={left:h+o[3].width,top:m+o[0].width,width:f-(o[1].width+o[3].width),height:e-(o[0].width+o[2].width)};u.clip&&(f=K(f,u.clip));if(f.height>0&&f.width>0){F(q,f.left,f.top,f.width,f.height,s);var v=f,p=t(c,"backgroundImage"),x=t(c,"backgroundRepeat").split(",")[0],n,E,J;!/data:image\/.*;base64,/i.test(p)&&!/^(-webkit|-moz|linear-gradient|-o-)/.test(p)&&(p=p.split(",")[0]);if(typeof p!=="undefined"&&/^(1|none)$/.test(p)===!1)if(p=l.Util.backgroundImage(p),s=d(p),e=G(c,v,s),s)switch(x){case "repeat-x":O(q, 27 | s,e,v.left,v.top,v.width,v.height);break;case "repeat-y":p=v.left;x=v.top;n=v.height;E=Math.min(s.width,v.width);e.top-=Math.ceil(e.top/s.height)*s.height;for(J=x+e.top;Jn+x?n+x-J:s.height,M(q,s,p+e.left,J,E,v,p,x),J=Math.floor(J+s.height);break;case "no-repeat":p=v.width-e.left;J=v.height-e.top;x=e.left;n=e.top;E=e.left+v.left;e=e.top+v.top;x<0?(x=Math.abs(x),E+=x,p=Math.min(v.width,s.width-x)):(p=Math.min(p,s.width),x=0);n<0?(n=Math.abs(n),e+=n,J=Math.min(v.height, 28 | s.height-n)):(J=Math.min(J,s.height),n=0);J>0&&p>0&&(q.drawImage(s,x,n,p,J,E,e,p,J),i+=1);break;default:e.top-=Math.ceil(e.top/s.height)*s.height;for(p=v.top+e.top;px+p?x+p-p:s.height,p0&&(e.top+=n),p=Math.floor(p+s.height)-n}else C("html2canvas: Error loading background:"+p)}switch(c.nodeName){case "IMG":u=c.getAttribute("src");(j=d(u))?(u=I(c,"paddingLeft"), 29 | f=I(c,"paddingTop"),s=I(c,"paddingRight"),e=I(c,"paddingBottom"),q.drawImage(j,0,0,j.width,j.height,h+u+o[3].width,m+f+o[0].width,b.width-(o[1].width+o[3].width+u+s),b.height-(o[0].width+o[2].width+f+e)),i+=1):C("html2canvas: Error loading :"+u);break;case "INPUT":/^(text|url|email|submit|button|reset)$/.test(c.type)&&c.value.length>0&&r(c,b,u);break;case "TEXTAREA":c.value.length>0&&r(c,b,u);break;case "SELECT":c.options.length>0&&r(c,b,u);break;case "LI":b=f;h=t(c,"listStylePosition");o=t(c, 30 | "listStyleType");m=t(c,"fontWeight");if(/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(o)){q=$(c).index()+1;switch(o){case "decimal":j=q;break;case "decimal-leading-zero":j=q.toString().length===1?"0"+q.toString():q.toString();break;case "upper-roman":j=l.Generate.ListRoman(q);break;case "lower-roman":j=l.Generate.ListRoman(q).toLowerCase();break;case "lower-alpha":j=l.Generate.ListAlpha(q).toLowerCase();break;case "upper-alpha":j= 31 | l.Generate.ListAlpha(q)}j+=". ";u=j;o=k.createElement("boundelement");o.style.display="inline";q=c.style.listStyleType;c.style.listStyleType="none";o.appendChild(k.createTextNode(u));c.insertBefore(o,c.firstChild);u=l.Util.Bounds(o);c.removeChild(o);c.style.listStyleType=q;switch(m){case 401:m="bold";break;case 400:m="normal"}N.setVariable("fillStyle",t(c,"color"));N.setVariable("font",t(c,"fontVariant")+" "+m+" "+t(c,"fontStyle")+" "+t(c,"fontSize")+" "+t(c,"fontFamily"));if(h==="inside")N.setVariable("textAlign", 32 | "left"),b=b.left,h=u.bottom,m=N,A(j).length>0&&(m.fillText(j,b,h),i+=1)}break;case "CANVAS":u=I(c,"paddingLeft"),f=I(c,"paddingTop"),s=I(c,"paddingRight"),e=I(c,"paddingBottom"),q.drawImage(c,0,0,c.width,c.height,h+u+o[3].width,m+f+o[0].width,b.width-(o[1].width+o[3].width+u+s),b.height-(o[0].width+o[2].width+f+e)),i+=1}return B.children[z-1]}function q(a,g){if(t(a,"display")!=="none"&&t(a,"visibility")!=="hidden"&&(g=z(a,g)||g,N=g.ctx,!Q.test(a.nodeName))){var i=l.Util.Children(a),b,d,h;b=0;for(h= 33 | i.length;b=d.numTotal&&(typeof f.complete==="function"&&f.complete(d),C("Finished loading images: # "+d.numTotal+" (failed: "+ 35 | d.numFailed+")"))}function I(a,i,b){var k,q=f.proxy,l;z.href=a;a=z.href;k="html2canvas_"+r++;b.callbackname=k;q+=q.indexOf("?")>-1?"&":"?";q+="url="+encodeURIComponent(a)+"&callback="+k;l=G.createElement("script");j[k]=function(a){a.substring(0,6)==="error:"?(b.succeeded=!1,d.numLoaded++,d.numFailed++,h()):(e(i,b),i.src=a);j[k]=n;try{delete j[k]}catch(g){}l.parentNode.removeChild(l);l=null;delete b.script;delete b.callbackname};l.setAttribute("type","text/javascript");l.setAttribute("src",q);b.script= 36 | l;j.document.body.appendChild(l)}function F(a){var i=l.Util.Children(a),b,f=i.length,e,r=!1;for(b=0;b0&&(B=j.setTimeout(w.cleanupDOM,f.timeout));C("html2canvas: Preload starts: finding background-images");d.firstRun=!0;F(a);C("html2canvas: Preload: Finding images");for(y=0;y0?(b.push(f),e.push(f.zindex)):A.push(f);e.sort(function(a,b){return a-b});a=0;for(d=e.length;a0&&m.arguments[7]){if(q&&e.taintTest&&B.indexOf(m.arguments[0].src)===-1){g.drawImage(m.arguments[0],0,0);try{g.getImageData(0,0,1,1)}catch(k){g=L.createElement("canvas");g=g.getContext("2d");continue}B.push(m.arguments[0].src)}b.drawImage.apply(b,m.arguments)}}}a.clip&& 45 | b.restore()}C("html2canvas: Renderer: Canvas renderer done - returning canvas obj");y=e.elements.length;return y===1&&typeof e.elements[0]==="object"&&e.elements[0].nodeName!=="BODY"&&d===!1?(B=l.Util.Bounds(e.elements[0]),q=w.createElement("canvas"),q.width=B.width,q.height=B.height,b=q.getContext("2d"),b.drawImage(f,B.left,B.top,B.width,B.height,0,0,B.width,B.height),delete f,q):f}function F(a){h(a.zIndex);var b=w.createElementNS("http://www.w3.org/2000/svg","svg"),d=w.createElementNS("http://www.w3.org/2000/svg", 46 | "defs"),f,j,l,n,g,i,m={},k,y=0;b.setAttribute("version","1.1");b.setAttribute("baseProfile","full");b.setAttribute("viewBox","0 0 "+Math.max(a.ctx.width,e.width)+" "+Math.max(a.ctx.height,e.height));b.setAttribute("width",Math.max(a.ctx.width,e.width)+"px");b.setAttribute("height",Math.max(a.ctx.height,e.height)+"px");b.setAttribute("preserveAspectRatio","none");b.appendChild(d);a=0;for(j=A.length;a0&&g.arguments[7]&&(i=w.createElementNS("http://www.w3.org/2000/svg", 49 | "clipPath"),i.setAttribute("id","clipId"+y),k=w.createElementNS("http://www.w3.org/2000/svg","rect"),k.setAttribute("x",g.arguments[5]),k.setAttribute("y",g.arguments[6]),k.setAttribute("width",g.arguments[3]),k.setAttribute("height",g.arguments[4]),i.appendChild(k),d.appendChild(i),i=w.createElementNS("http://www.w3.org/2000/svg","image"),i.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",g.arguments[0].src),i.setAttribute("width",g.arguments[0].width),i.setAttribute("height",g.arguments[0].height), 50 | i.setAttribute("x",g.arguments[5]-g.arguments[1]),i.setAttribute("y",g.arguments[6]-g.arguments[2]),i.setAttribute("clip-path","url(#clipId"+y+")"),i.setAttribute("preserveAspectRatio","none"),b.appendChild(i),y+=1)}}C("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj");return b}var e={width:null,height:null,renderer:"canvas",taintTest:!0},A=[],f,d=!1,K=2880,w=L,e=l.Util.Extend(b,e);switch(e.renderer.toLowerCase()){case "canvas":if(f=w.createElement("canvas"),f.getContext)return C("html2canvas: Renderer: using canvas renderer"), 51 | I(a);else{d=!0;C("html2canvas: Renderer: canvas not available, using flashcanvas");var y=w.createElement("script");y.src=e.flashcanvas;y.onload=function(a,b){var d;if(a.onload===n)a.onreadystatechange!==n?(d=function(){a.readyState!=="loaded"&&a.readyState!=="complete"?j.setTimeout(d,250):b()},j.setTimeout(d,250)):C("html2canvas: Renderer: Can't track when flashcanvas is loaded");else return b}(y,function(){typeof j.FlashCanvas!=="undefined"&&(C("html2canvas: Renderer: Flashcanvas initialized"),j.FlashCanvas.initElement(f), 52 | I(a))});w.body.appendChild(y);return f}case "svg":if(w.createElementNS)return C("html2canvas: Renderer: using SVG renderer"),F(a)}return this};j.html2canvas=l})(window,document); -------------------------------------------------------------------------------- /Leaflet/dist/leaflet.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010-2011, CloudMade, Vladimir Agafonkin 3 | Leaflet is a modern open-source JavaScript library for interactive maps. 4 | http://leaflet.cloudmade.com 5 | */ 6 | (function(a){a.L={VERSION:"0.3",ROOT_URL:a.L_ROOT_URL||function(){var a=document.getElementsByTagName("script"),b=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/,c,d,e,f;for(c=0,d=a.length;c0},removeEventListener:function(a,b,c){if(!this.hasEventListeners(a))return this;for(var d=0,e=this._leaflet_events,f=e[a].length;d=this.min.x&&c.x<=this.max.x&&b.y>=this.min.y&&c.y<=this.max.y},intersects:function(a){var b=this.min,c=this.max,d=a.min,e=a.max,f=e.x>=b.x&&d.x<=c.x,g=e.y>=b.y&&d.y<=c.y;return f&&g}}),L.Transformation=L.Class.extend({initialize:function(a,b,c,d){this._a=a,this._b=b,this._c=c,this._d=d},transform:function(a,b){return this._transform(a.clone(),b)},_transform:function(a,b){return b=b||1,a.x=b*(this._a*a.x+this._b),a.y=b*(this._c*a.y+this._d),a},untransform:function(a,b){return b=b||1,new L.Point((a.x/b-this._b)/this._a,(a.y/b-this._d)/this._c)}}),L.DomUtil={get:function(a){return typeof a=="string"?document.getElementById(a):a},getStyle:function(a,b){var c=a.style[b];!c&&a.currentStyle&&(c=a.currentStyle[b]);if(!c||c==="auto"){var d=document.defaultView.getComputedStyle(a,null);c=d?d[b]:null}return c==="auto"?null:c},getViewportOffset:function(a){var b=0,c=0,d=a,e=document.body;do{b+=d.offsetTop||0,c+=d.offsetLeft||0;if(d.offsetParent===e&&L.DomUtil.getStyle(d,"position")==="absolute")break;d=d.offsetParent}while(d);d=a;do{if(d===e)break;b-=d.scrollTop||0,c-=d.scrollLeft||0,d=d.parentNode}while(d);return new L.Point(c,b)},create:function(a,b,c){var d=document.createElement(a);return d.className=b,c&&c.appendChild(d),d},disableTextSelection:function(){document.selection&&document.selection.empty&&document.selection.empty(),this._onselectstart||(this._onselectstart=document.onselectstart,document.onselectstart=L.Util.falseFn)},enableTextSelection:function(){document.onselectstart=this._onselectstart,this._onselectstart=null},hasClass:function(a,b){return a.className.length>0&&RegExp("(^|\\s)"+b+"(\\s|$)").test(a.className)},addClass:function(a,b){L.DomUtil.hasClass(a,b)||(a.className+=(a.className?" ":"")+b)},removeClass:function(a,b){a.className=a.className.replace(/(\S+)\s*/g,function(a,c){return c===b?"":a}).replace(/^\s+/,"")},setOpacity:function(a,b){L.Browser.ie?a.style.filter="alpha(opacity="+Math.round(b*100)+")":a.style.opacity=b},testProp:function(a){var b=document.documentElement.style;for(var c=0;c=b.lat&&e.lat<=c.lat&&d.lng>=b.lng&&e.lng<=c.lng},intersects:function(a){var b=this._southWest,c=this._northEast,d=a.getSouthWest(),e=a.getNorthEast(),f=e.lat>=b.lat&&d.lat<=c.lat,g=e.lng>=b.lng&&d.lng<=c.lng;return f&&g},toBBoxString:function(){var a=this._southWest,b=this._northEast;return[a.lng,a.lat,b.lng,b.lat].join(",")}}),L.Projection={},L.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(a){var b=L.LatLng.DEG_TO_RAD,c=this.MAX_LATITUDE,d=Math.max(Math.min(c,a.lat),-c),e=a.lng*b,f=d*b;return f=Math.log(Math.tan(Math.PI/4+f/2)),new L.Point(e,f)},unproject:function(a,b){var c=L.LatLng.RAD_TO_DEG,d=a.x*c,e=(2*Math.atan(Math.exp(a.y))-Math.PI/2)*c;return new L.LatLng(e,d,b)}},L.Projection.LonLat={project:function(a){return new L.Point(a.lng,a.lat)},unproject:function(a,b){return new L.LatLng(a.y,a.x,b)}},L.CRS={latLngToPoint:function(a,b){var c=this.projection.project(a);return this.transformation._transform(c,b)},pointToLatLng:function(a,b,c){var d=this.transformation.untransform(a,b);return this.projection.unproject(d,c)},project:function(a){return this.projection.project(a)}},L.CRS.EPSG3857=L.Util.extend({},L.CRS,{code:"EPSG:3857",projection:L.Projection.SphericalMercator,transformation:new L.Transformation(.5/Math.PI,.5,-0.5/Math.PI,.5),project:function(a){var b=this.projection.project(a),c=6378137;return b.multiplyBy(c)}}),L.CRS.EPSG900913=L.Util.extend({},L.CRS.EPSG3857,{code:"EPSG:900913"}),L.CRS.EPSG4326=L.Util.extend({},L.CRS,{code:"EPSG:4326",projection:L.Projection.LonLat,transformation:new L.Transformation(1/360,.5,-1/360,.5)}),L.Map=L.Class.extend({includes:L.Mixin.Events,options:{crs:L.CRS.EPSG3857||L.CRS.EPSG4326,scale:function(a){return 256*Math.pow(2,a)},center:null,zoom:null,layers:[],dragging:!0,touchZoom:L.Browser.touch&&!L.Browser.android,scrollWheelZoom:!L.Browser.touch,doubleClickZoom:!0,boxZoom:!0,zoomControl:!0,attributionControl:!0,fadeAnimation:L.DomUtil.TRANSITION&&!L.Browser.android,zoomAnimation:L.DomUtil.TRANSITION&&!L.Browser.android&&!L.Browser.mobileOpera,trackResize:!0,closePopupOnClick:!0,worldCopyJump:!0},initialize:function(a,b){L.Util.setOptions(this,b),this._container=L.DomUtil.get(a);if(this._container._leaflet)throw Error("Map container is already initialized.");this._container._leaflet=!0,this._initLayout(),L.DomEvent&&(this._initEvents(),L.Handler&&this._initInteraction(),L.Control&&this._initControls()),this.options.maxBounds&&this.setMaxBounds(this.options.maxBounds);var c=this.options.center,d=this.options.zoom;c!==null&&d!==null&&this.setView(c,d,!0);var e=this.options.layers;e=e instanceof Array?e:[e],this._tileLayersNum=0,this._initLayers(e)},setView:function(a,b){return this._resetView(a,this._limitZoom(b)),this},setZoom:function(a){return this.setView(this.getCenter(),a)},zoomIn:function(){return this.setZoom(this._zoom+1)},zoomOut:function(){return this.setZoom(this._zoom-1)},fitBounds:function(a){var b=this.getBoundsZoom(a);return this.setView(a.getCenter(),b)},fitWorld:function(){var a=new L.LatLng(-60,-170),b=new L.LatLng(85,179);return this.fitBounds(new L.LatLngBounds(a,b))},panTo:function(a){return this.setView(a,this._zoom)},panBy:function(a){return this.fire("movestart"),this._rawPanBy(a),this.fire("move"),this.fire("moveend"),this},setMaxBounds:function(a){this.options.maxBounds=a;if(!a)return this._boundsMinZoom=null,this;var b=this.getBoundsZoom(a,!0);return this._boundsMinZoom=b,this._loaded&&(this._zoomf.x&&(g=f.x-d.x),c.y>e.y&&(h=e.y-c.y),c.xl&&--m>0)o=h*Math.sin(j),n=Math.PI/2-2*Math.atan(i*Math.pow((1-o)/(1+o),.5*h))-j,j+=n;return new L.LatLng(j*c,f,b)}},L.CRS.EPSG3395=L.Util.extend({},L.CRS,{code:"EPSG:3395",projection:L.Projection.Mercator,transformation:function(){var a=L.Projection.Mercator,b=a.R_MAJOR,c=a.R_MINOR;return new L.Transformation(.5/(Math.PI*b),.5,-0.5/(Math.PI*c),.5)}()}),L.TileLayer=L.Class.extend({includes:L.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",opacity:1,scheme:"xyz",continuousWorld:!1,noWrap:!1,zoomOffset:0,zoomReverse:!1,unloadInvisibleTiles:L.Browser.mobile,updateWhenIdle:L.Browser.mobile,reuseTiles:!1},initialize:function(a,b,c){L.Util.setOptions(this,b),this._url=a,this._urlParams=c,typeof this.options.subdomains=="string"&&(this.options.subdomains=this.options.subdomains.split(""))},onAdd:function(a,b){this._map=a,this._insertAtTheBottom=b,this._initContainer(),this._createTileProto(),a.on("viewreset",this._resetCallback,this),this.options.updateWhenIdle?a.on("moveend",this._update,this):(this._limitedUpdate=L.Util.limitExecByInterval(this._update,150,this),a.on("move",this._limitedUpdate,this)),this._reset(),this._update()},onRemove:function(a){this._map.getPanes().tilePane.removeChild(this._container),this._container=null,this._map.off("viewreset",this._resetCallback,this),this.options.updateWhenIdle?this._map.off("moveend",this._update,this):this._map.off("move",this._limitedUpdate,this)},getAttribution:function(){return this.options.attribution},setOpacity:function(a){this.options.opacity=a,this._setOpacity(a);if(L.Browser.webkit)for(var b in this._tiles)this._tiles.hasOwnProperty(b)&&(this._tiles[b].style.webkitTransform+=" translate(0,0)")},_setOpacity:function(a){a<1&&L.DomUtil.setOpacity(this._container,a)},_initContainer:function(){var a=this._map.getPanes().tilePane,b=a.firstChild;if(!this._container||a.empty)this._container=L.DomUtil.create("div","leaflet-layer"),this._insertAtTheBottom&&b?a.insertBefore(this._container,b):a.appendChild(this._container),this._setOpacity(this.options.opacity)},_resetCallback:function(a){this._reset(a.hard)},_reset:function(a){var b;for(b in this._tiles)this._tiles.hasOwnProperty(b)&&this.fire("tileunload",{tile:this._tiles[b]});this._tiles={},this.options.reuseTiles&&(this._unusedTiles=[]),a&&this._container&&(this._container.innerHTML=""),this._initContainer()},_update:function(){var a=this._map.getPixelBounds(),b=this._map.getZoom(),c=this.options.tileSize;if(b>this.options.maxZoom||ba.max.x||da.max.y)f=this._tiles[e],this.fire("tileunload",{tile:f,url:f.src}),f.parentNode===this._container&&this._container.removeChild(f),this.options.reuseTiles&&this._unusedTiles.push(this._tiles[e]),f.src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",delete this._tiles[e]}},_addTile:function(a,b){var c=this._getTilePos(a),d=this._map.getZoom(),e=a.x+":"+a.y,f=Math.pow(2,this._getOffsetZoom(d));if(!this.options.continuousWorld){if(!this.options.noWrap)a.x=(a.x%f+f)%f;else if(a.x<0||a.x>=f){this._tilesToLoad--;return}if(a.y<0||a.y>=f){this._tilesToLoad--;return}}var g=this._getTile();L.DomUtil.setPosition(g,c),this._tiles[e]=g,this.options.scheme==="tms"&&(a.y=f-a.y-1),this._loadTile(g,a,d),b.appendChild(g)},_getOffsetZoom:function(a){return a=this.options.zoomReverse?this.options.maxZoom-a:a,a+this.options.zoomOffset},_getTilePos:function(a){var b=this._map.getPixelOrigin(),c=this.options.tileSize;return a.multiplyBy(c).subtract(b)},getTileUrl:function(a,b){var c=this.options.subdomains,d=this.options.subdomains[(a.x+a.y)%c.length];return L.Util.template(this._url,L.Util.extend({s:d,z:this._getOffsetZoom(b),x:a.x,y:a.y},this._urlParams))},_createTileProto:function(){this._tileImg=L.DomUtil.create("img","leaflet-tile"),this._tileImg.galleryimg="no";var a=this.options.tileSize;this._tileImg.style.width=a+"px",this._tileImg.style.height=a+"px"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var a=this._unusedTiles.pop();return this._resetTile(a),a}return this._createTile()},_resetTile:function(a){},_createTile:function(){var a=this._tileImg.cloneNode(!1);return a.onselectstart=a.onmousemove=L.Util.falseFn,a},_loadTile:function(a,b,c){a._layer=this,a.onload=this._tileOnLoad,a.onerror=this._tileOnError,a.src=this.getTileUrl(b,c)},_tileOnLoad:function(a){var b=this._layer;this.className+=" leaflet-tile-loaded",b.fire("tileload",{tile:this,url:this.src}),b._tilesToLoad--,b._tilesToLoad||b.fire("load")},_tileOnError:function(a){var b=this._layer;b.fire("tileerror",{tile:this,url:this.src});var c=b.options.errorTileUrl;c&&(this.src=c)}}),L.TileLayer.WMS=L.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(a,b){this._url=a,this.wmsParams=L.Util.extend({},this.defaultWmsParams),this.wmsParams.width=this.wmsParams.height=this.options.tileSize;for(var c in b)this.options.hasOwnProperty(c)||(this.wmsParams[c]=b[c]);L.Util.setOptions(this,b)},onAdd:function(a){var b=parseFloat(this.wmsParams.version)<1.3?"srs":"crs";this.wmsParams[b]=a.options.crs.code,L.TileLayer.prototype.onAdd.call(this,a)},getTileUrl:function(a,b){var c=this.options.tileSize,d=a.multiplyBy(c),e=d.add(new L.Point(c,c)),f=this._map.unproject(d,this._zoom,!0),g=this._map.unproject(e,this._zoom,!0),h=this._map.options.crs.project(f),i=this._map.options.crs.project(g),j=[h.x,i.y,i.x,h.y].join(",");return this._url+L.Util.getParamString(this.wmsParams)+"&bbox="+j}}),L.TileLayer.Canvas=L.TileLayer.extend({options:{async:!1},initialize:function(a){L.Util.setOptions(this,a)},redraw:function(){for(var a in this._tiles){var b=this._tiles[a];this._redrawTile(b)}},_redrawTile:function(a){this.drawTile(a,a._tilePoint,a._zoom)},_createTileProto:function(){this._canvasProto=L.DomUtil.create("canvas","leaflet-tile");var a=this.options.tileSize;this._canvasProto.width=a,this._canvasProto.height=a},_createTile:function(){var a=this._canvasProto.cloneNode(!1);return a.onselectstart=a.onmousemove=L.Util.falseFn,a},_loadTile:function(a,b,c){a._layer=this,a._tilePoint=b,a._zoom=c,this.drawTile(a,b,c),this.options.async||this.tileDrawn(a)},drawTile:function(a,b,c){},tileDrawn:function(a){this._tileOnLoad.call(a)}}),L.ImageOverlay=L.Class.extend({includes:L.Mixin.Events,initialize:function(a,b){this._url=a,this._bounds=b},onAdd:function(a){this._map=a,this._image||this._initImage(),a.getPanes().overlayPane.appendChild(this._image),a.on("viewreset",this._reset,this),this._reset()},onRemove:function(a){a.getPanes().overlayPane.removeChild(this._image),a.off("viewreset",this._reset,this)},_initImage:function(){this._image=L.DomUtil.create("img","leaflet-image-layer"),this._image.style.visibility="hidden",L.Util.extend(this._image,{galleryimg:"no",onselectstart:L.Util.falseFn,onmousemove:L.Util.falseFn,onload:L.Util.bind(this._onImageLoad,this),src:this._url})},_reset:function(){var a=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),b=this._map.latLngToLayerPoint(this._bounds.getSouthEast()),c=b.subtract(a);L.DomUtil.setPosition(this._image,a),this._image.style.width=c.x+"px",this._image.style.height=c.y+"px"},_onImageLoad:function(){this._image.style.visibility="",this.fire("load")}}),L.Icon=L.Class.extend({iconUrl:L.ROOT_URL+"images/marker.png",shadowUrl:L.ROOT_URL+"images/marker-shadow.png",iconSize:new L.Point(25,41),shadowSize:new L.Point(41,41),iconAnchor:new L.Point(13,41),popupAnchor:new L.Point(0,-33),initialize:function(a){a&&(this.iconUrl=a)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(a){var b=this[a+"Size"],c=this[a+"Url"];if(!c&&a==="shadow")return null;var d;return c?d=this._createImg(c):d=this._createDiv(),d.className="leaflet-marker-"+a,d.style.marginLeft=-this.iconAnchor.x+"px",d.style.marginTop=-this.iconAnchor.y+"px",b&&(d.style.width=b.x+"px",d.style.height=b.y+"px"),d},_createImg:function(a){var b;return L.Browser.ie6?(b=document.createElement("div"),b.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+a+'")'):(b=document.createElement("img"),b.src=a),b},_createDiv:function(){return document.createElement("div")}}),L.Marker=L.Class.extend({includes:L.Mixin.Events,options:{icon:new L.Icon,title:"",clickable:!0,draggable:!1,zIndexOffset:0},initialize:function(a,b){L.Util.setOptions(this,b),this._latlng=a},onAdd:function(a){this._map=a,this._initIcon(),a.on("viewreset",this._reset,this),this._reset()},onRemove:function(a){this._removeIcon(),this.closePopup&&this.closePopup(),this._map=null,a.off("viewreset",this._reset,this)},getLatLng:function(){return this._latlng},setLatLng:function(a){this._latlng=a,this._icon&&(this._reset(),this._popup&&this._popup.setLatLng(this._latlng))},setZIndexOffset:function(a){this.options.zIndexOffset=a,this._icon&&this._reset()},setIcon:function(a){this._map&&this._removeIcon(),this.options.icon=a,this._map&&(this._initIcon(),this._reset())},_initIcon:function(){this._icon||(this._icon=this.options.icon.createIcon(),this.options.title&&(this._icon.title=this.options.title),this._initInteraction()),this._shadow||(this._shadow=this.options.icon.createShadow()),this._map._panes.markerPane.appendChild(this._icon),this._shadow&&this._map._panes.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this._map._panes.markerPane.removeChild(this._icon),this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._icon=this._shadow=null},_reset:function(){var a=this._map.latLngToLayerPoint(this._latlng).round();L.DomUtil.setPosition(this._icon,a),this._shadow&&L.DomUtil.setPosition(this._shadow,a),this._icon.style.zIndex=a.y+this.options.zIndexOffset},_initInteraction:function(){if(this.options.clickable){this._icon.className+=" leaflet-clickable",L.DomEvent.addListener(this._icon,"click",this._onMouseClick,this);var a=["dblclick","mousedown","mouseover","mouseout"];for(var b=0;bthis.options.maxWidth?this.options.maxWidth:af.x&&(d.x=c.x+this._containerWidth-f.x+e.x),c.y<0&&(d.y=c.y-e.y),c.y+a>f.y&&(d.y=c.y+a-f.y+e.y),(d.x||d.y)&&this._map.panBy(d)},_onCloseButtonClick:function(a){this._close(),L.DomEvent.stop(a)}}),L.Marker.include({openPopup:function(){return this._popup.setLatLng(this._latlng),this._map&&this._map.openPopup(this._popup),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(a,b){return b=L.Util.extend({offset:this.options.icon.popupAnchor},b),this._popup||this.on("click",this.openPopup,this),this._popup=new L.Popup(b,this),this._popup.setContent(a),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup)),this}}),L.Map.include({openPopup:function(a){return this.closePopup(),this._popup=a,this.addLayer(a),this.fire("popupopen",{popup:this._popup}),this},closePopup:function(){return this._popup&&(this.removeLayer(this._popup),this.fire("popupclose",{popup:this._popup}),this._popup=null),this}}),L.LayerGroup=L.Class.extend({initialize:function(a){this._layers={};if(a)for(var b=0,c=a.length;b')}}catch(a){return function(a){return document.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}()},_initPath:function(){this._container=L.Path._createElement("shape"),this._container.className+=" leaflet-vml-shape"+(this.options.clickable?" leaflet-clickable":""),this._container.coordsize="1 1",this._path=L.Path._createElement("path"),this._container.appendChild(this._path),this._map._pathRoot.appendChild(this._container)},_initStyle:function(){this.options.stroke?(this._stroke=L.Path._createElement("stroke"),this._stroke.endcap="round",this._container.appendChild(this._stroke)):this._container.stroked=!1,this.options.fill?(this._container.filled=!0,this._fill=L.Path._createElement("fill"),this._container.appendChild(this._fill)):this._container.filled=!1,this._updateStyle()},_updateStyle:function(){this.options.stroke&&(this._stroke.weight=this.options.weight+"px",this._stroke.color=this.options.color,this._stroke.opacity=this.options.opacity),this.options.fill&&(this._fill.color=this.options.fillColor||this.options.color,this._fill.opacity=this.options.fillOpacity)},_updatePath:function(){this._container.style.display="none",this._path.v=this.getPathString()+" ",this._container.style.display=""}}),L.Map.include(L.Browser.svg||!L.Browser.vml?{}:{_initPathRoot:function(){this._pathRoot||(this._pathRoot=document.createElement("div"),this._pathRoot.className="leaflet-vml-container",this._panes.overlayPane.appendChild(this._pathRoot),this.on("moveend",this._updatePathViewport),this._updatePathViewport())}}),L.Browser.canvas=function(){return!!document.createElement("canvas").getContext}(),L.Path=L.Path.SVG&&!window.L_PREFER_CANVAS||!L.Browser.canvas?L.Path:L.Path.extend({statics:{CANVAS:!0,SVG:!1},options:{updateOnMoveEnd:!0},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){this.options.stroke&&(this._ctx.lineWidth=this.options.weight,this._ctx.strokeStyle=this.options.color),this.options.fill&&(this._ctx.fillStyle=this.options.fillColor||this.options.color)},_drawPath:function(){var a,b,c,d,e,f;this._ctx.beginPath();for(a=0,c=this._parts.length;af&&(g=h,f=i);f>c&&(b[g]=1,this._simplifyDPStep(a,b,c,d,g),this._simplifyDPStep(a,b,c,g,e))},_reducePoints:function(a,b){var c=[a[0]];for(var d=1,e=0,f=a.length;db&&(c.push(a[d]),e=d);return eb.max.x&&(c|=2),a.yb.max.y&&(c|=8),c},_sqDist:function(a,b){var c=b.x-a.x,d=b.y-a.y;return c*c+d*d},_sqClosestPointOnSegment:function(a,b,c,d){var e=b.x,f=b.y,g=c.x-e,h=c.y-f,i=g*g+h*h,j;return i>0&&(j=((a.x-e)*g+(a.y-f)*h)/i,j>1?(e=c.x,f=c.y):j>0&&(e+=g*j,f+=h*j)),g=a.x-e,h=a.y-f,d?g*g+h*h:new L.Point(e,f)}},L.Polyline=L.Path.extend({initialize:function(a,b){L.Path.prototype.initialize.call(this,b),this._latlngs=a},options:{smoothFactor:1,noClip:!1,updateOnMoveEnd:!0},projectLatlngs:function(){this._originalPoints=[];for(var a=0,b=this._latlngs.length;aa.max.x||c.y-b>a.max.y||c.x+ba.y!=e.y>a.y&&a.x<(e.x-d.x)*(a.y-d.y)/(e.y-d.y)+d.x&&(b=!b)}return b}}:{}),L.Circle.include(L.Path.CANVAS?{_drawPath:function(){var a=this._point;this._ctx.beginPath(),this._ctx.arc(a.x,a.y,this._radius,0,Math.PI*2)},_containsPoint:function(a){var b=this._point,c=this.options.stroke?this.options.weight/2:0;return a.distanceTo(b)<=this._radius+c}}:{}),L.GeoJSON=L.FeatureGroup.extend({initialize:function(a,b){L.Util.setOptions(this,b),this._geojson=a,this._layers={},a&&this.addGeoJSON(a)},addGeoJSON:function(a){if(a.features){for(var b=0,c=a.features.length;b1)return;var b=a.touches&&a.touches.length===1?a.touches[0]:a,c=b.target;L.DomEvent.preventDefault(a),L.Browser.touch&&c.tagName.toLowerCase()==="a"&&(c.className+=" leaflet-active"),this._moved=!1;if(this._moving)return;L.Browser.touch||(L.DomUtil.disableTextSelection(),this._setMovingCursor()),this._startPos=this._newPos=L.DomUtil.getPosition(this._element),this._startPoint=new L.Point(b.clientX,b.clientY),L.DomEvent.addListener(document,L.Draggable.MOVE,this._onMove,this),L.DomEvent.addListener(document,L.Draggable.END,this._onUp,this)},_onMove:function(a){if(a.touches&&a.touches.length>1)return;L.DomEvent.preventDefault(a);var b=a.touches&&a.touches.length===1?a.touches[0]:a;this._moved||(this.fire("dragstart"),this._moved=!0),this._moving=!0;var c=new L.Point(b.clientX,b.clientY);this._newPos=this._startPos.add(c).subtract(this._startPoint),L.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)},_updatePosition:function(){this.fire("predrag"),L.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(a){if(a.changedTouches){var b=a.changedTouches[0],c=b.target,d=this._newPos&&this._newPos.distanceTo(this._startPos)||0;c.tagName.toLowerCase()==="a"&&(c.className=c.className.replace(" leaflet-active","")),d0&&c<=f,d=b}function l(a){e&&(g.type="dblclick",b(g),d=null)}var d,e=!1,f=250,g,h="_leaflet_",i="touchstart",j="touchend";a[h+i+c]=k,a[h+j+c]=l,a.addEventListener(i,k,!1),a.addEventListener(j,l,!1)},removeDoubleTapListener:function(a,b){var c="_leaflet_";a.removeEventListener(a,a[c+"touchstart"+b],!1),a.removeEventListener(a,a[c+"touchend"+b],!1)}}),L.Map.TouchZoom=L.Handler.extend({addHooks:function(){L.DomEvent.addListener(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){L.DomEvent.removeListener(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(a){if(!a.touches||a.touches.length!==2||this._map._animatingZoom)return;var b=this._map.mouseEventToLayerPoint(a.touches[0]),c=this._map.mouseEventToLayerPoint(a.touches[1]),d=this._map.containerPointToLayerPoint(this._map.getSize().divideBy(2));this._startCenter=b.add(c).divideBy(2,!0),this._startDist=b.distanceTo(c),this._moved=!1,this._zooming=!0,this._centerOffset=d.subtract(this._startCenter),L.DomEvent.addListener(document,"touchmove",this._onTouchMove,this),L.DomEvent.addListener(document,"touchend",this._onTouchEnd,this),L.DomEvent.preventDefault(a)},_onTouchMove:function(a){if(!a.touches||a.touches.length!==2)return;this._moved||(this._map._mapPane.className+=" leaflet-zoom-anim",this._map.fire("zoomstart").fire("movestart")._prepareTileBg(),this._moved=!0);var b=this._map.mouseEventToLayerPoint(a.touches[0]),c=this._map.mouseEventToLayerPoint(a.touches[1]);this._scale=b.distanceTo(c)/this._startDist,this._delta=b.add(c).divideBy(2,!0).subtract(this._startCenter),this._map._tileBg.style.webkitTransform=[L.DomUtil.getTranslateString(this._delta),L.DomUtil.getScaleString(this._scale,this._startCenter)].join(" "),L.DomEvent.preventDefault(a)},_onTouchEnd:function(a){if(!this._moved||!this._zooming)return;this._zooming=!1;var b=this._map.getZoom(),c=Math.log(this._scale)/Math.LN2,d=c>0?Math.ceil(c):Math.floor(c),e=this._map._limitZoom(b+d),f=e-b,g=this._centerOffset.subtract(this._delta).divideBy(this._scale),h=this._map.getPixelOrigin().add(this._startCenter).add(g),i=this._map.unproject(h);L.DomEvent.removeListener(document,"touchmove",this._onTouchMove),L.DomEvent.removeListener(document,"touchend",this._onTouchEnd);var j=Math.pow(2,f);this._map._runAnimation(i,e,j/this._scale,this._startCenter.add(g))}}),L.Map.BoxZoom=L.Handler.extend({initialize:function(a){this._map=a,this._container=a._container,this._pane=a._panes.overlayPane},addHooks:function(){L.DomEvent.addListener(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){L.DomEvent.removeListener(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(a){if(!a.shiftKey||a.which!==1&&a.button!==1)return!1;L.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(a),this._box=L.DomUtil.create("div","leaflet-zoom-box",this._pane),L.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",L.DomEvent.addListener(document,"mousemove",this._onMouseMove,this),L.DomEvent.addListener(document,"mouseup",this._onMouseUp,this),L.DomEvent.preventDefault(a)},_onMouseMove:function(a){var b=this._map.mouseEventToLayerPoint(a),c=b.x-this._startLayerPoint.x,d=b.y-this._startLayerPoint.y,e=Math.min(b.x,this._startLayerPoint.x),f=Math.min(b.y,this._startLayerPoint.y),g=new L.Point(e,f);L.DomUtil.setPosition(this._box,g),this._box.style.width=Math.abs(c)-4+"px",this._box.style.height=Math.abs(d)-4+"px"},_onMouseUp:function(a){this._pane.removeChild(this._box),this._container.style.cursor="",L.DomUtil.enableTextSelection(),L.DomEvent.removeListener(document,"mousemove",this._onMouseMove),L.DomEvent.removeListener(document,"mouseup",this._onMouseUp);var b=this._map.mouseEventToLayerPoint(a),c=new L.LatLngBounds(this._map.layerPointToLatLng(this._startLayerPoint),this._map.layerPointToLatLng(b));this._map.fitBounds(c)}}),L.Handler.MarkerDrag=L.Handler.extend({initialize:function(a){this._marker=a},addHooks:function(){var a=this._marker._icon;this._draggable||(this._draggable=new L.Draggable(a,a),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this)),this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(a){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(a){var b=L.DomUtil.getPosition(this._marker._icon);this._marker._shadow&&L.DomUtil.setPosition(this._marker._shadow,b),this._marker._latlng=this._marker._map.layerPointToLatLng(b),this._marker.fire("move").fire("drag")},_onDragEnd:function(){this._marker.fire("moveend").fire("dragend")}}),L.Control={},L.Control.Position={TOP_LEFT:"topLeft",TOP_RIGHT:"topRight",BOTTOM_LEFT:"bottomLeft",BOTTOM_RIGHT:"bottomRight"},L.Map.include({addControl:function(a){a.onAdd(this);var b=a.getPosition(),c=this._controlCorners[b],d=a.getContainer();return L.DomUtil.addClass(d,"leaflet-control"),b.indexOf("bottom")!==-1?c.insertBefore(d,c.firstChild):c.appendChild(d),this},removeControl:function(a){var b=a.getPosition(),c=this._controlCorners[b],d=a.getContainer();return c.removeChild(d),a.onRemove&&a.onRemove(this),this},_initControlPos:function(){var a=this._controlCorners={},b="leaflet-",c=b+"top",d=b+"bottom",e=b+"left",f=b+"right",g=L.DomUtil.create("div",b+"control-container",this._container);L.Browser.touch&&(g.className+=" "+b+"big-buttons"),a.topLeft=L.DomUtil.create("div",c+" "+e,g),a.topRight=L.DomUtil.create("div",c+" "+f,g),a.bottomLeft=L.DomUtil.create("div",d+" "+e,g),a.bottomRight=L.DomUtil.create("div",d+" "+f,g)}}),L.Control.Zoom=L.Class.extend({onAdd:function(a){this._map=a,this._container=L.DomUtil.create("div","leaflet-control-zoom"),this._zoomInButton=this._createButton("Zoom in","leaflet-control-zoom-in",this._map.zoomIn,this._map),this._zoomOutButton=this._createButton("Zoom out","leaflet-control-zoom-out",this._map.zoomOut,this._map),this._container.appendChild(this._zoomInButton),this._container.appendChild(this._zoomOutButton)},getContainer:function(){return this._container},getPosition:function(){return L.Control.Position.TOP_LEFT},_createButton:function(a,b,c,d){var e=document.createElement("a");return e.href="#",e.title=a,e.className=b,L.Browser.touch||L.DomEvent.disableClickPropagation(e),L.DomEvent.addListener(e,"click",L.DomEvent.preventDefault),L.DomEvent.addListener(e,"click",c,d),e}}),L.Control.Attribution=L.Class.extend({initialize:function(a){this._prefix=a||'Powered by Leaflet',this._attributions={}},onAdd:function(a){this._container=L.DomUtil.create("div","leaflet-control-attribution"),L.DomEvent.disableClickPropagation(this._container),this._map=a,this._update()},getPosition:function(){return L.Control.Position.BOTTOM_RIGHT},getContainer:function(){return this._container},setPrefix:function(a){this._prefix=a,this._update()},addAttribution:function(a){if(!a)return;this._attributions[a]||(this._attributions[a]=0),this._attributions[a]++,this._update()},removeAttribution:function(a){if(!a)return;this._attributions[a]--,this._update()},_update:function(){if(!this._map)return;var a=[];for(var b in this._attributions)this._attributions.hasOwnProperty(b)&&a.push(b);var c=[];this._prefix&&c.push(this._prefix),a.length&&c.push(a.join(", ")),this._container.innerHTML=c.join(" — ")}}),L.Control.Layers=L.Class.extend({options:{collapsed:!0},initialize:function(a,b,c){L.Util.setOptions(this,c),this._layers={};for(var d in a)a.hasOwnProperty(d)&&this._addLayer(a[d],d);for(d in b)b.hasOwnProperty(d)&&this._addLayer(b[d],d,!0)},onAdd:function(a){this._map=a,this._initLayout(),this._update()},getContainer:function(){return this._container},getPosition:function(){return L.Control.Position.TOP_RIGHT},addBaseLayer:function(a,b){return this._addLayer(a,b),this._update(),this},addOverlay:function(a,b){return this._addLayer(a,b,!0),this._update(),this},removeLayer:function(a){var b=L.Util.stamp(a);return delete this._layers[b],this._update(),this},_initLayout:function(){this._container=L.DomUtil.create("div","leaflet-control-layers"),L.Browser.touch||L.DomEvent.disableClickPropagation(this._container),this._form=L.DomUtil.create("form","leaflet-control-layers-list");if(this.options.collapsed){L.DomEvent.addListener(this._container,"mouseover",this._expand,this),L.DomEvent.addListener(this._container,"mouseout",this._collapse,this);var a=this._layersLink=L.DomUtil.create("a","leaflet-control-layers-toggle");a.href="#",a.title="Layers",L.Browser.touch?L.DomEvent.addListener(a,"click",this._expand,this):L.DomEvent.addListener(a,"focus",this._expand,this),this._map.on("movestart",this._collapse,this),this._container.appendChild(a)}else this._expand();this._baseLayersList=L.DomUtil.create("div","leaflet-control-layers-base",this._form),this._separator=L.DomUtil.create("div","leaflet-control-layers-separator",this._form),this._overlaysList=L.DomUtil.create("div","leaflet-control-layers-overlays",this._form),this._container.appendChild(this._form)},_addLayer:function(a,b,c){var d=L.Util.stamp(a);this._layers[d]={layer:a,name:b,overlay:c}},_update:function(){if(!this._container)return;this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var a=!1,b=!1;for(var c in this._layers)if(this._layers.hasOwnProperty(c)){var d=this._layers[c];this._addItem(d),b=b||d.overlay,a=a||!d.overlay}this._separator.style.display=b&&a?"":"none"},_addItem:function(a,b){var c=document.createElement("label"),d=document.createElement("input");a.overlay||(d.name="leaflet-base-layers"),d.type=a.overlay?"checkbox":"radio",d.checked=this._map.hasLayer(a.layer),d.layerId=L.Util.stamp(a.layer),L.DomEvent.addListener(d,"click",this._onInputClick,this);var e=document.createTextNode(" "+a.name);c.appendChild(d),c.appendChild(e);var f=a.overlay?this._overlaysList:this._baseLayersList;f.appendChild(c)},_onInputClick:function(){var a,b,c,d=this._form.getElementsByTagName("input"),e=d.length;for(a=0;a 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.33 12 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 13 | http://www.twitter.com/niklasvh 14 | 15 | Released under MIT License 16 | */ 17 | 18 | var html2canvas = {}; 19 | 20 | html2canvas.logging = false; 21 | 22 | function h2clog(a) { 23 | if (html2canvas.logging && window.console && window.console.log) { 24 | window.console.log(a); 25 | } 26 | } 27 | 28 | html2canvas.log = h2clog; // for compatibility with the jquery plugin 29 | 30 | html2canvas.Util = {}; 31 | 32 | html2canvas.Util.backgroundImage = function (src) { 33 | 34 | if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { 35 | return src; 36 | } 37 | 38 | if (src.toLowerCase().substr( 0, 5 ) === 'url("') { 39 | src = src.substr( 5 ); 40 | src = src.substr( 0, src.length - 2 ); 41 | } else { 42 | src = src.substr( 4 ); 43 | src = src.substr( 0, src.length - 1 ); 44 | } 45 | 46 | return src; 47 | }; 48 | 49 | html2canvas.Util.Bounds = function getBounds (el) { 50 | var clientRect, 51 | bounds = {}; 52 | 53 | if (el.getBoundingClientRect){ 54 | clientRect = el.getBoundingClientRect(); 55 | 56 | 57 | // TODO add scroll position to bounds, so no scrolling of window necessary 58 | bounds.top = clientRect.top; 59 | bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); 60 | bounds.left = clientRect.left; 61 | 62 | // older IE doesn't have width/height, but top/bottom instead 63 | bounds.width = clientRect.width || (clientRect.right - clientRect.left); 64 | bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); 65 | 66 | return bounds; 67 | 68 | } /*else{ 69 | 70 | 71 | p = $(el).offset(); 72 | 73 | return { 74 | left: p.left + getCSS(el,"borderLeftWidth", true), 75 | top: p.top + getCSS(el,"borderTopWidth", true), 76 | width:$(el).innerWidth(), 77 | height:$(el).innerHeight() 78 | }; 79 | 80 | 81 | } */ 82 | } 83 | 84 | html2canvas.Util.getCSS = function (el, attribute) { 85 | // return jQuery(el).css(attribute); 86 | /* 87 | var val, 88 | left, 89 | rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], 90 | style = el.style; 91 | 92 | if ( el.currentStyle ) { 93 | val = el.currentStyle[ attribute ]; 94 | } else if (window.getComputedStyle) { 95 | val = document.defaultView.getComputedStyle(el, null)[ attribute ]; 96 | } 97 | */ 98 | // Check if we are not dealing with pixels, (Opera has issues with this) 99 | // Ported from jQuery css.js 100 | // From the awesome hack by Dean Edwards 101 | // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 102 | 103 | // If we're not dealing with a regular pixel number 104 | // but a number that has a weird ending, we need to convert it to pixels 105 | 106 | // if ( !/^-?\d+(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) { 107 | /* 108 | // Remember the original values 109 | left = style.left; 110 | 111 | // Put in the new values to get a computed value out 112 | if ( rsLeft ) { 113 | el.runtimeStyle.left = el.currentStyle.left; 114 | } 115 | style.left = attribute === "fontSize" ? "1em" : (val || 0); 116 | val = style.pixelLeft + "px"; 117 | 118 | // Revert the changed values 119 | style.left = left; 120 | if ( rsLeft ) { 121 | el.runtimeStyle.left = rsLeft; 122 | }*/ 123 | // val = $(el).css(attribute); 124 | // } 125 | 126 | /* 127 | var val = $(el).css(attribute); 128 | 129 | if (val === "medium") { 130 | val = 3; 131 | }*/ 132 | 133 | return $(el).css(attribute); 134 | 135 | 136 | }; 137 | 138 | html2canvas.Util.Extend = function (options, defaults) { 139 | var key; 140 | for (key in options) { 141 | if (options.hasOwnProperty(key)) { 142 | defaults[key] = options[key]; 143 | } 144 | } 145 | return defaults; 146 | }; 147 | 148 | html2canvas.Util.Children = function(el) { 149 | // $(el).contents() !== el.childNodes, Opera / IE have issues with that 150 | var children; 151 | try { 152 | children = $(el).contents(); 153 | } catch (ex) { 154 | h2clog("html2canvas.Util.Children failed with exception: " + ex.message); 155 | children = []; 156 | } 157 | return children; 158 | }; 159 | 160 | /* 161 | html2canvas v0.33 162 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 163 | http://www.twitter.com/niklasvh 164 | 165 | Released under MIT License 166 | */ 167 | 168 | html2canvas.Generate = {}; 169 | 170 | 171 | 172 | html2canvas.Generate.Gradient = function(src, bounds) { 173 | var canvas = document.createElement('canvas'), 174 | ctx = canvas.getContext('2d'), 175 | tmp, 176 | p0 = 0, 177 | p1 = 0, 178 | p2 = 0, 179 | p3 = 0, 180 | steps = [], 181 | position, 182 | i, 183 | len, 184 | lingrad, 185 | increment, 186 | p, 187 | img; 188 | 189 | canvas.width = bounds.width; 190 | canvas.height = bounds.height; 191 | 192 | 193 | function getColors(input) { 194 | var j = -1, 195 | color = '', 196 | chr; 197 | 198 | while( j++ < input.length ) { 199 | chr = input.charAt( j ); 200 | if (chr === ')') { 201 | color += chr; 202 | steps.push( color ); 203 | color = ''; 204 | while (j++ < input.length && input.charAt( j ) !== ',') { 205 | } 206 | } else { 207 | color += chr; 208 | } 209 | } 210 | } 211 | 212 | if ( tmp = src.match(/-webkit-linear-gradient\((.*)\)/) ) { 213 | 214 | position = tmp[1].split( ",", 1 )[0]; 215 | getColors( tmp[1].substr( position.length + 2 ) ); 216 | position = position.split(' '); 217 | 218 | for (p = 0; p < position.length; p+=1) { 219 | 220 | switch(position[p]) { 221 | case 'top': 222 | p3 = bounds.height; 223 | break; 224 | 225 | case 'right': 226 | p0 = bounds.width; 227 | break; 228 | 229 | case 'bottom': 230 | p1 = bounds.height; 231 | break; 232 | 233 | case 'left': 234 | p2 = bounds.width; 235 | break; 236 | } 237 | 238 | } 239 | 240 | } else if (tmp = src.match(/-webkit-gradient\(linear, (\d+)[%]{0,1} (\d+)[%]{0,1}, (\d+)[%]{0,1} (\d+)[%]{0,1}, from\((.*)\), to\((.*)\)\)/)) { 241 | 242 | p0 = (tmp[1] * bounds.width) / 100; 243 | p1 = (tmp[2] * bounds.height) / 100; 244 | p2 = (tmp[3] * bounds.width) / 100; 245 | p3 = (tmp[4] * bounds.height) / 100; 246 | 247 | steps.push(tmp[5]); 248 | steps.push(tmp[6]); 249 | 250 | } else if (tmp = src.match(/-moz-linear-gradient\((\d+)[%]{0,1} (\d+)[%]{0,1}, (.*)\)/)) { 251 | 252 | p0 = (tmp[1] * bounds.width) / 100; 253 | p1 = (tmp[2] * bounds.width) / 100; 254 | p2 = bounds.width - p0; 255 | p3 = bounds.height - p1; 256 | getColors( tmp[3] ); 257 | 258 | } else { 259 | return; 260 | } 261 | 262 | lingrad = ctx.createLinearGradient( p0, p1, p2, p3 ); 263 | increment = 1 / (steps.length - 1); 264 | 265 | for (i = 0, len = steps.length; i < len; i+=1) { 266 | try { 267 | lingrad.addColorStop(increment * i, steps[i]); 268 | } 269 | catch(e) { 270 | h2clog(['failed to add color stop: ', e, '; tried to add: ', steps[i], '; stop: ', i, '; in: ', src]); 271 | } 272 | } 273 | 274 | ctx.fillStyle = lingrad; 275 | 276 | // draw shapes 277 | ctx.fillRect(0, 0, bounds.width,bounds.height); 278 | 279 | img = new Image(); 280 | img.src = canvas.toDataURL(); 281 | 282 | return img; 283 | 284 | } 285 | 286 | html2canvas.Generate.ListAlpha = function(number) { 287 | var tmp = "", 288 | modulus; 289 | 290 | do { 291 | modulus = number % 26; 292 | tmp = String.fromCharCode((modulus) + 64) + tmp; 293 | number = number / 26; 294 | }while((number*26) > 26); 295 | 296 | return tmp; 297 | } 298 | 299 | html2canvas.Generate.ListRoman = function(number) { 300 | var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], 301 | decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], 302 | roman = "", 303 | v, 304 | len = romanArray.length; 305 | 306 | if (number <= 0 || number >= 4000) { 307 | return number; 308 | } 309 | 310 | for (v=0; v < len; v+=1) { 311 | while (number >= decimal[v]) { 312 | number -= decimal[v]; 313 | roman += romanArray[v]; 314 | } 315 | } 316 | 317 | return roman; 318 | 319 | } 320 | 321 | /* 322 | html2canvas v0.33 323 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 324 | http://www.twitter.com/niklasvh 325 | 326 | Released under MIT License 327 | */ 328 | 329 | /* 330 | * New function for traversing elements 331 | */ 332 | 333 | html2canvas.Parse = function (element, images, opts) { 334 | window.scroll(0,0); 335 | opts = opts || {}; 336 | 337 | // select body by default 338 | if (element === undefined) { 339 | element = document.body; 340 | } 341 | 342 | 343 | var support = { 344 | rangeBounds: false 345 | 346 | }, 347 | options = { 348 | iframeDefault: "default", 349 | ignoreElements: "IFRAME|OBJECT|PARAM", 350 | useOverflow: true, 351 | letterRendering: false 352 | }, 353 | needReorder = false, 354 | numDraws = 0, 355 | fontData = {}, 356 | doc = element.ownerDocument, 357 | ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), 358 | body = doc.body, 359 | r, 360 | testElement, 361 | rangeBounds, 362 | rangeHeight, 363 | stack, 364 | ctx, 365 | docDim, 366 | i, 367 | children, 368 | childrenLen; 369 | 370 | options = html2canvas.Util.Extend(opts, options); 371 | 372 | images = images || {}; 373 | 374 | // Test whether we can use ranges to measure bounding boxes 375 | // Opera doesn't provide valid bounds.height/bottom even though it supports the method. 376 | 377 | 378 | if (doc.createRange) { 379 | r = doc.createRange(); 380 | //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); 381 | if (r.getBoundingClientRect){ 382 | testElement = doc.createElement('boundtest'); 383 | testElement.style.height = "123px"; 384 | testElement.style.display = "block"; 385 | body.appendChild(testElement); 386 | 387 | r.selectNode(testElement); 388 | rangeBounds = r.getBoundingClientRect(); 389 | rangeHeight = rangeBounds.height; 390 | 391 | if (rangeHeight === 123) { 392 | support.rangeBounds = true; 393 | } 394 | body.removeChild(testElement); 395 | 396 | 397 | } 398 | 399 | } 400 | 401 | 402 | /* 403 | var rootStack = new this.storageContext($(document).width(),$(document).height()); 404 | rootStack.opacity = this.getCSS(this.element,"opacity"); 405 | var stack = this.newElement(this.element,rootStack); 406 | 407 | 408 | this.parseElement(this.element,stack); 409 | */ 410 | 411 | 412 | function docSize(){ 413 | 414 | return { 415 | width: Math.max( 416 | Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), 417 | Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), 418 | Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) 419 | ), 420 | height: Math.max( 421 | Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), 422 | Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), 423 | Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) 424 | ) 425 | }; 426 | 427 | } 428 | 429 | var getCSS = html2canvas.Util.getCSS; 430 | function getCSSInt(element, attribute) { 431 | var val = parseInt(getCSS(element, attribute), 10); 432 | return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html 433 | } 434 | 435 | // Drawing a rectangle 436 | function renderRect (ctx, x, y, w, h, bgcolor) { 437 | if (bgcolor !=="transparent"){ 438 | ctx.setVariable("fillStyle", bgcolor); 439 | ctx.fillRect (x, y, w, h); 440 | numDraws+=1; 441 | } 442 | } 443 | 444 | 445 | function textTransform (text, transform) { 446 | switch(transform){ 447 | case "lowercase": 448 | return text.toLowerCase(); 449 | 450 | case "capitalize": 451 | return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { 452 | if (m.length > 0) { 453 | return p1 + p2.toUpperCase(); 454 | } 455 | } ); 456 | 457 | case "uppercase": 458 | return text.toUpperCase(); 459 | 460 | default: 461 | return text; 462 | 463 | } 464 | 465 | } 466 | 467 | function trimText (text) { 468 | return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); 469 | } 470 | 471 | function fontMetrics (font, fontSize) { 472 | 473 | if (fontData[font + "-" + fontSize] !== undefined) { 474 | return fontData[font + "-" + fontSize]; 475 | } 476 | 477 | 478 | var container = doc.createElement('div'), 479 | img = doc.createElement('img'), 480 | span = doc.createElement('span'), 481 | baseline, 482 | middle, 483 | metricsObj; 484 | 485 | 486 | container.style.visibility = "hidden"; 487 | container.style.fontFamily = font; 488 | container.style.fontSize = fontSize; 489 | container.style.margin = 0; 490 | container.style.padding = 0; 491 | 492 | body.appendChild(container); 493 | 494 | 495 | 496 | // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) 497 | img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; 498 | img.width = 1; 499 | img.height = 1; 500 | 501 | img.style.margin = 0; 502 | img.style.padding = 0; 503 | img.style.verticalAlign = "baseline"; 504 | 505 | span.style.fontFamily = font; 506 | span.style.fontSize = fontSize; 507 | span.style.margin = 0; 508 | span.style.padding = 0; 509 | 510 | 511 | 512 | 513 | span.appendChild(doc.createTextNode('Hidden Text')); 514 | container.appendChild(span); 515 | container.appendChild(img); 516 | baseline = (img.offsetTop - span.offsetTop) + 1; 517 | 518 | container.removeChild(span); 519 | container.appendChild(doc.createTextNode('Hidden Text')); 520 | 521 | container.style.lineHeight = "normal"; 522 | img.style.verticalAlign = "super"; 523 | 524 | middle = (img.offsetTop-container.offsetTop) + 1; 525 | metricsObj = { 526 | baseline: baseline, 527 | lineWidth: 1, 528 | middle: middle 529 | }; 530 | 531 | 532 | fontData[font + "-" + fontSize] = metricsObj; 533 | 534 | body.removeChild(container); 535 | 536 | return metricsObj; 537 | 538 | } 539 | 540 | 541 | function drawText(currentText, x, y, ctx){ 542 | if (trimText(currentText).length>0) { 543 | ctx.fillText(currentText,x,y); 544 | numDraws+=1; 545 | } 546 | } 547 | 548 | 549 | function renderText(el, textNode, stack) { 550 | var ctx = stack.ctx, 551 | family = getCSS(el, "fontFamily"), 552 | size = getCSS(el, "fontSize"), 553 | color = getCSS(el, "color"), 554 | text_decoration = getCSS(el, "textDecoration"), 555 | text_align = getCSS(el, "textAlign"), 556 | letter_spacing = getCSS(el, "letterSpacing"), 557 | bounds, 558 | text, 559 | metrics, 560 | renderList, 561 | bold = getCSS(el, "fontWeight"), 562 | font_style = getCSS(el, "fontStyle"), 563 | font_variant = getCSS(el, "fontVariant"), 564 | align = false, 565 | newTextNode, 566 | textValue, 567 | textOffset = 0, 568 | oldTextNode, 569 | c, 570 | range, 571 | parent, 572 | wrapElement, 573 | backupText; 574 | 575 | // apply text-transform:ation to the text 576 | 577 | 578 | 579 | textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); 580 | text = trimText(textNode.nodeValue); 581 | 582 | if (text.length>0){ 583 | 584 | if (text_decoration !== "none"){ 585 | metrics = fontMetrics(family, size); 586 | } 587 | 588 | text_align = text_align.replace(["-webkit-auto"],["auto"]); 589 | 590 | if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ 591 | // this.setContextVariable(ctx,"textAlign",text_align); 592 | renderList = textNode.nodeValue.split(/(\b| )/); 593 | 594 | }else{ 595 | // this.setContextVariable(ctx,"textAlign","left"); 596 | renderList = textNode.nodeValue.split(""); 597 | } 598 | 599 | switch(parseInt(bold, 10)){ 600 | case 401: 601 | bold = "bold"; 602 | break; 603 | case 400: 604 | bold = "normal"; 605 | break; 606 | } 607 | 608 | ctx.setVariable("fillStyle", color); 609 | 610 | /* 611 | need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand 612 | to properly work in Firefox 613 | */ 614 | ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); 615 | 616 | if (align){ 617 | ctx.setVariable("textAlign", "right"); 618 | }else{ 619 | ctx.setVariable("textAlign", "left"); 620 | } 621 | 622 | 623 | /* 624 | if (stack.clip){ 625 | ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); 626 | ctx.clip(); 627 | } 628 | */ 629 | 630 | 631 | oldTextNode = textNode; 632 | 633 | 634 | for (c=0; c < renderList.length; c+=1) { 635 | textValue = null; 636 | 637 | 638 | 639 | if (support.rangeBounds){ 640 | // getBoundingClientRect is supported for ranges 641 | if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { 642 | textValue = renderList[c]; 643 | if (doc.createRange){ 644 | range = doc.createRange(); 645 | 646 | range.setStart(textNode, textOffset); 647 | range.setEnd(textNode, textOffset + textValue.length); 648 | }else{ 649 | // TODO add IE support 650 | range = body.createTextRange(); 651 | } 652 | 653 | if (range.getBoundingClientRect()) { 654 | bounds = range.getBoundingClientRect(); 655 | }else{ 656 | bounds = {}; 657 | } 658 | 659 | } 660 | }else{ 661 | // it isn't supported, so let's wrap it inside an element instead and get the bounds there 662 | 663 | // IE 9 bug 664 | if (typeof oldTextNode.nodeValue !== "string" ){ 665 | continue; 666 | } 667 | 668 | newTextNode = oldTextNode.splitText(renderList[c].length); 669 | 670 | parent = oldTextNode.parentNode; 671 | wrapElement = doc.createElement('wrapper'); 672 | backupText = oldTextNode.cloneNode(true); 673 | 674 | wrapElement.appendChild(oldTextNode.cloneNode(true)); 675 | parent.replaceChild(wrapElement, oldTextNode); 676 | 677 | bounds = html2canvas.Util.Bounds(wrapElement); 678 | 679 | textValue = oldTextNode.nodeValue; 680 | 681 | oldTextNode = newTextNode; 682 | parent.replaceChild(backupText, wrapElement); 683 | 684 | 685 | } 686 | 687 | if (textValue !== null){ 688 | drawText(textValue, bounds.left, bounds.bottom, ctx); 689 | } 690 | 691 | switch(text_decoration) { 692 | case "underline": 693 | // Draws a line at the baseline of the font 694 | // 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 695 | renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); 696 | break; 697 | case "overline": 698 | renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); 699 | break; 700 | case "line-through": 701 | // TODO try and find exact position for line-through 702 | renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); 703 | break; 704 | 705 | } 706 | 707 | 708 | 709 | 710 | 711 | textOffset += renderList[c].length; 712 | 713 | } 714 | 715 | 716 | 717 | } 718 | 719 | } 720 | 721 | function listPosition (element, val) { 722 | var boundElement = doc.createElement( "boundelement" ), 723 | type, 724 | bounds; 725 | 726 | boundElement.style.display = "inline"; 727 | //boundElement.style.width = "1px"; 728 | //boundElement.style.height = "1px"; 729 | 730 | type = element.style.listStyleType; 731 | element.style.listStyleType = "none"; 732 | 733 | boundElement.appendChild( doc.createTextNode( val ) ); 734 | 735 | 736 | element.insertBefore(boundElement, element.firstChild); 737 | 738 | 739 | bounds = html2canvas.Util.Bounds( boundElement ); 740 | element.removeChild( boundElement ); 741 | element.style.listStyleType = type; 742 | return bounds; 743 | 744 | } 745 | 746 | 747 | function renderListItem(element, stack, elBounds) { 748 | 749 | 750 | var position = getCSS(element, "listStylePosition"), 751 | x, 752 | y, 753 | type = getCSS(element, "listStyleType"), 754 | currentIndex, 755 | text, 756 | listBounds, 757 | bold = getCSS(element, "fontWeight"); 758 | 759 | if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { 760 | 761 | // TODO remove jQuery dependency 762 | currentIndex = $(element).index()+1; 763 | 764 | switch(type){ 765 | case "decimal": 766 | text = currentIndex; 767 | break; 768 | case "decimal-leading-zero": 769 | if (currentIndex.toString().length === 1){ 770 | text = currentIndex = "0" + currentIndex.toString(); 771 | }else{ 772 | text = currentIndex.toString(); 773 | } 774 | break; 775 | case "upper-roman": 776 | text = html2canvas.Generate.ListRoman( currentIndex ); 777 | break; 778 | case "lower-roman": 779 | text = html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); 780 | break; 781 | case "lower-alpha": 782 | text = html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); 783 | break; 784 | case "upper-alpha": 785 | text = html2canvas.Generate.ListAlpha( currentIndex ); 786 | break; 787 | } 788 | 789 | 790 | text += ". "; 791 | listBounds = listPosition(element, text); 792 | 793 | 794 | 795 | switch(bold){ 796 | case 401: 797 | bold = "bold"; 798 | break; 799 | case 400: 800 | bold = "normal"; 801 | break; 802 | } 803 | 804 | 805 | 806 | 807 | ctx.setVariable( "fillStyle", getCSS(element, "color") ); 808 | ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") ); 809 | 810 | 811 | if ( position === "inside" ) { 812 | ctx.setVariable("textAlign", "left"); 813 | // this.setFont(stack.ctx, element, false); 814 | x = elBounds.left; 815 | 816 | }else{ 817 | return; 818 | /* 819 | TODO really need to figure out some more accurate way to try and find the position. 820 | 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 821 | may display it whatever way it feels like. 822 | "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" 823 | */ 824 | ctx.setVariable("textAlign", "right"); 825 | // this.setFont(stack.ctx, element, true); 826 | x = elBounds.left - 10; 827 | } 828 | 829 | y = listBounds.bottom; 830 | 831 | drawText(text, x, y, ctx) 832 | 833 | 834 | } 835 | 836 | 837 | } 838 | 839 | function loadImage (src){ 840 | var img = images[src]; 841 | if (img && img.succeeded === true) { 842 | return img.img; 843 | } else { 844 | return false; 845 | } 846 | } 847 | 848 | 849 | 850 | 851 | 852 | 853 | function clipBounds(src, dst){ 854 | 855 | var x = Math.max(src.left, dst.left), 856 | y = Math.max(src.top, dst.top), 857 | x2 = Math.min((src.left + src.width), (dst.left + dst.width)), 858 | y2 = Math.min((src.top + src.height), (dst.top + dst.height)); 859 | 860 | return { 861 | left:x, 862 | top:y, 863 | width:x2-x, 864 | height:y2-y 865 | }; 866 | 867 | } 868 | 869 | function setZ(zIndex, parentZ){ 870 | // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them 871 | 872 | if (!parentZ){ 873 | this.zStack = h2czContext(0); 874 | return this.zStack; 875 | } 876 | 877 | if (zIndex !== "auto"){ 878 | needReorder = true; 879 | var newContext = h2czContext(zIndex); 880 | parentZ.children.push(newContext); 881 | return newContext; 882 | 883 | } 884 | 885 | return parentZ; 886 | 887 | } 888 | 889 | function renderBorders(el, ctx, bounds, clip){ 890 | 891 | /* 892 | * TODO add support for different border-style's than solid 893 | */ 894 | 895 | var x = bounds.left, 896 | y = bounds.top, 897 | w = bounds.width, 898 | h = bounds.height, 899 | borderSide, 900 | borderData, 901 | bx, 902 | by, 903 | bw, 904 | bh, 905 | borderBounds, 906 | borders = (function(el){ 907 | var borders = [], 908 | sides = ["Top","Right","Bottom","Left"], 909 | s; 910 | 911 | for (s = 0; s < 4; s+=1){ 912 | borders.push({ 913 | width: getCSSInt(el, 'border' + sides[s] + 'Width'), 914 | color: getCSS(el, 'border' + sides[s] + 'Color') 915 | }); 916 | } 917 | 918 | return borders; 919 | 920 | }(el)); 921 | 922 | 923 | for (borderSide = 0; borderSide < 4; borderSide+=1){ 924 | borderData = borders[borderSide]; 925 | 926 | if (borderData.width>0){ 927 | bx = x; 928 | by = y; 929 | bw = w; 930 | bh = h - (borders[2].width); 931 | 932 | switch(borderSide){ 933 | case 0: 934 | // top border 935 | bh = borders[0].width; 936 | break; 937 | case 1: 938 | // right border 939 | bx = x + w - (borders[1].width); 940 | bw = borders[1].width; 941 | break; 942 | case 2: 943 | // bottom border 944 | by = (by + h) - (borders[2].width); 945 | bh = borders[2].width; 946 | break; 947 | case 3: 948 | // left border 949 | bw = borders[3].width; 950 | break; 951 | } 952 | 953 | borderBounds = { 954 | left:bx, 955 | top:by, 956 | width: bw, 957 | height:bh 958 | }; 959 | 960 | if (clip){ 961 | borderBounds = clipBounds(borderBounds, clip); 962 | } 963 | 964 | 965 | if (borderBounds.width>0 && borderBounds.height>0){ 966 | renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); 967 | } 968 | 969 | 970 | } 971 | } 972 | 973 | return borders; 974 | 975 | } 976 | 977 | 978 | function renderFormValue (el, bounds, stack){ 979 | 980 | var valueWrap = doc.createElement('valuewrap'), 981 | cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], 982 | i, 983 | textValue, 984 | textNode, 985 | arrLen, 986 | style; 987 | 988 | for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ 989 | style = cssArr[i]; 990 | 991 | try { 992 | valueWrap.style[style] = getCSS(el, style); 993 | } catch( e ) { 994 | // Older IE has issues with "border" 995 | h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); 996 | } 997 | } 998 | 999 | 1000 | valueWrap.style.borderColor = "black"; 1001 | valueWrap.style.borderStyle = "solid"; 1002 | valueWrap.style.display = "block"; 1003 | valueWrap.style.position = "absolute"; 1004 | if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ 1005 | valueWrap.style.lineHeight = getCSS(el, "height"); 1006 | } 1007 | 1008 | 1009 | valueWrap.style.top = bounds.top + "px"; 1010 | valueWrap.style.left = bounds.left + "px"; 1011 | 1012 | if (el.nodeName === "SELECT"){ 1013 | // TODO increase accuracy of text position 1014 | textValue = el.options[el.selectedIndex].text; 1015 | } else{ 1016 | textValue = el.value; 1017 | } 1018 | textNode = doc.createTextNode(textValue); 1019 | 1020 | valueWrap.appendChild(textNode); 1021 | body.appendChild(valueWrap); 1022 | 1023 | 1024 | renderText(el, textNode, stack); 1025 | body.removeChild(valueWrap); 1026 | 1027 | 1028 | 1029 | } 1030 | 1031 | 1032 | 1033 | function getBackgroundPosition(el, bounds, image){ 1034 | // TODO add support for multi image backgrounds 1035 | 1036 | var bgposition = (function( bgp ){ 1037 | 1038 | if (bgp !== undefined) { 1039 | return (bgp.split(",")[0] || "0 0").split(" "); 1040 | } else { 1041 | // Older IE uses -x and -y 1042 | return [ getCSS(el, "backgroundPositionX"), getCSS(el, "backgroundPositionY") ]; 1043 | } 1044 | 1045 | 1046 | })( getCSS(el, "backgroundPosition") ), 1047 | topPos, 1048 | left, 1049 | percentage, 1050 | val; 1051 | 1052 | if (bgposition.length === 1){ 1053 | val = bgposition; 1054 | 1055 | bgposition = []; 1056 | 1057 | bgposition[0] = val; 1058 | bgposition[1] = val; 1059 | } 1060 | 1061 | 1062 | 1063 | if (bgposition[0].toString().indexOf("%") !== -1){ 1064 | percentage = (parseFloat(bgposition[0])/100); 1065 | left = ((bounds.width * percentage)-(image.width*percentage)); 1066 | 1067 | }else{ 1068 | left = parseInt(bgposition[0],10); 1069 | } 1070 | 1071 | if (bgposition[1].toString().indexOf("%") !== -1){ 1072 | 1073 | percentage = (parseFloat(bgposition[1])/100); 1074 | topPos = ((bounds.height * percentage)-(image.height*percentage)); 1075 | }else{ 1076 | topPos = parseInt(bgposition[1],10); 1077 | } 1078 | 1079 | 1080 | 1081 | 1082 | return { 1083 | top: topPos, 1084 | left: left 1085 | }; 1086 | 1087 | } 1088 | 1089 | function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) { 1090 | ctx.drawImage( 1091 | image, 1092 | sx, //sx 1093 | sy, //sy 1094 | sw, //sw 1095 | sh, //sh 1096 | dx, //dx 1097 | dy, // dy 1098 | dw, //dw 1099 | dh //dh 1100 | ); 1101 | numDraws+=1; 1102 | 1103 | } 1104 | 1105 | 1106 | function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ 1107 | var sourceX = 0, 1108 | sourceY=0; 1109 | if (elx-x>0){ 1110 | sourceX = elx-x; 1111 | } 1112 | 1113 | if (ely-y>0){ 1114 | sourceY = ely-y; 1115 | } 1116 | 1117 | renderImage( 1118 | ctx, 1119 | image, 1120 | sourceX, // source X 1121 | sourceY, // source Y 1122 | width-sourceX, // source Width 1123 | height-sourceY, // source Height 1124 | x+sourceX, // destination X 1125 | y+sourceY, // destination Y 1126 | width-sourceX, // destination width 1127 | height-sourceY // destination height 1128 | ); 1129 | } 1130 | 1131 | 1132 | function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ 1133 | 1134 | var height, 1135 | width = Math.min(image.width,w),bgy; 1136 | 1137 | bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1138 | 1139 | 1140 | for(bgy=(y+bgp.top);bgyh+y){ 1144 | height = (h+y)-bgy; 1145 | }else{ 1146 | height = image.height; 1147 | } 1148 | renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y); 1149 | 1150 | bgy = Math.floor(bgy+image.height); 1151 | 1152 | } 1153 | } 1154 | 1155 | function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ 1156 | 1157 | var height = Math.min(image.height,h), 1158 | width,bgx; 1159 | 1160 | 1161 | bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; 1162 | 1163 | 1164 | for (bgx=(x+bgp.left);bgxw+x){ 1167 | width = (w+x)-bgx; 1168 | }else{ 1169 | width = image.width; 1170 | } 1171 | 1172 | renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y); 1173 | 1174 | bgx = Math.floor(bgx+image.width); 1175 | 1176 | 1177 | } 1178 | } 1179 | 1180 | function renderBackground(el,bounds,ctx){ 1181 | 1182 | // TODO add support for multi background-images 1183 | var background_image = getCSS(el, "backgroundImage"), 1184 | background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], 1185 | image, 1186 | bgp, 1187 | bgy, 1188 | bgw, 1189 | bgsx, 1190 | bgsy, 1191 | bgdx, 1192 | bgdy, 1193 | bgh, 1194 | h, 1195 | height, 1196 | add; 1197 | 1198 | // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){ 1199 | 1200 | if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { 1201 | background_image = background_image.split(",")[0]; 1202 | } 1203 | 1204 | if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { 1205 | background_image = html2canvas.Util.backgroundImage( background_image ); 1206 | image = loadImage( background_image ); 1207 | 1208 | 1209 | bgp = getBackgroundPosition(el, bounds, image); 1210 | 1211 | 1212 | if ( image ){ 1213 | switch ( background_repeat ) { 1214 | 1215 | case "repeat-x": 1216 | renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1217 | break; 1218 | 1219 | case "repeat-y": 1220 | renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1221 | break; 1222 | 1223 | case "no-repeat": 1224 | /* 1225 | this.drawBackgroundRepeat( 1226 | ctx, 1227 | image, 1228 | bgp.left+bounds.left, // sx 1229 | bgp.top+bounds.top, // sy 1230 | Math.min(bounds.width,image.width), 1231 | Math.min(bounds.height,image.height), 1232 | bounds.left, 1233 | bounds.top 1234 | );*/ 1235 | 1236 | 1237 | // console.log($(el).css('background-image')); 1238 | bgw = bounds.width - bgp.left; 1239 | bgh = bounds.height - bgp.top; 1240 | bgsx = bgp.left; 1241 | bgsy = bgp.top; 1242 | bgdx = bgp.left+bounds.left; 1243 | bgdy = bgp.top+bounds.top; 1244 | 1245 | // 1246 | // bgw = Math.min(bgw,image.width); 1247 | // bgh = Math.min(bgh,image.height); 1248 | 1249 | if (bgsx<0){ 1250 | bgsx = Math.abs(bgsx); 1251 | bgdx += bgsx; 1252 | bgw = Math.min(bounds.width,image.width-bgsx); 1253 | }else{ 1254 | bgw = Math.min(bgw,image.width); 1255 | bgsx = 0; 1256 | } 1257 | 1258 | if (bgsy<0){ 1259 | bgsy = Math.abs(bgsy); 1260 | bgdy += bgsy; 1261 | // bgh = bgh-bgsy; 1262 | bgh = Math.min(bounds.height,image.height-bgsy); 1263 | }else{ 1264 | bgh = Math.min(bgh,image.height); 1265 | bgsy = 0; 1266 | } 1267 | 1268 | 1269 | // bgh = Math.abs(bgh); 1270 | // bgw = Math.abs(bgw); 1271 | if (bgh>0 && bgw > 0){ 1272 | renderImage( 1273 | ctx, 1274 | image, 1275 | bgsx, // source X : 0 1276 | bgsy, // source Y : 1695 1277 | bgw, // source Width : 18 1278 | bgh, // source Height : 1677 1279 | bgdx, // destination X :906 1280 | bgdy, // destination Y : 1020 1281 | bgw, // destination width : 18 1282 | bgh // destination height : 1677 1283 | ); 1284 | 1285 | // ctx.drawImage(image,(bounds.left+bgp.left),(bounds.top+bgp.top)); 1286 | 1287 | } 1288 | break; 1289 | default: 1290 | 1291 | 1292 | 1293 | bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1294 | 1295 | 1296 | for(bgy=(bounds.top+bgp.top);bgyh+bgy){ 1304 | height = (h+bgy)-bgy; 1305 | }else{ 1306 | height = image.height; 1307 | } 1308 | // console.log(height); 1309 | 1310 | if (bgy0){ 1320 | bgp.top += add; 1321 | } 1322 | bgy = Math.floor(bgy+image.height)-add; 1323 | } 1324 | break; 1325 | 1326 | 1327 | } 1328 | }else{ 1329 | h2clog("html2canvas: Error loading background:" + background_image); 1330 | //console.log(images); 1331 | } 1332 | 1333 | } 1334 | } 1335 | 1336 | 1337 | 1338 | function renderElement(el, parentStack){ 1339 | 1340 | var bounds = html2canvas.Util.Bounds(el), 1341 | x = bounds.left, 1342 | y = bounds.top, 1343 | w = bounds.width, 1344 | h = bounds.height, 1345 | image, 1346 | bgcolor = getCSS(el, "backgroundColor"), 1347 | cssPosition = getCSS(el, "position"), 1348 | zindex, 1349 | opacity = getCSS(el, "opacity"), 1350 | stack, 1351 | stackLength, 1352 | borders, 1353 | ctx, 1354 | bgbounds, 1355 | imgSrc, 1356 | paddingLeft, 1357 | paddingTop, 1358 | paddingRight, 1359 | paddingBottom; 1360 | 1361 | if (!parentStack){ 1362 | docDim = docSize(); 1363 | parentStack = { 1364 | opacity: 1 1365 | }; 1366 | }else{ 1367 | docDim = {}; 1368 | } 1369 | 1370 | 1371 | //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode); 1372 | 1373 | zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); 1374 | 1375 | 1376 | 1377 | stack = { 1378 | ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), 1379 | zIndex: zindex, 1380 | opacity: opacity * parentStack.opacity, 1381 | cssPosition: cssPosition 1382 | }; 1383 | 1384 | 1385 | 1386 | // TODO correct overflow for absolute content residing under a static position 1387 | 1388 | if (parentStack.clip){ 1389 | stack.clip = html2canvas.Util.Extend( {}, parentStack.clip ); 1390 | //stack.clip = parentStack.clip; 1391 | // stack.clip.height = stack.clip.height - parentStack.borders[2].width; 1392 | } 1393 | 1394 | 1395 | if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){ 1396 | if (stack.clip){ 1397 | stack.clip = clipBounds(stack.clip, bounds); 1398 | }else{ 1399 | stack.clip = bounds; 1400 | } 1401 | } 1402 | 1403 | 1404 | stackLength = zindex.children.push(stack); 1405 | 1406 | ctx = zindex.children[stackLength-1].ctx; 1407 | 1408 | ctx.setVariable("globalAlpha", stack.opacity); 1409 | 1410 | // draw element borders 1411 | borders = renderBorders(el, ctx, bounds, false); 1412 | stack.borders = borders; 1413 | 1414 | 1415 | // let's modify clip area for child elements, so borders dont get overwritten 1416 | 1417 | /* 1418 | if (stack.clip){ 1419 | stack.clip.width = stack.clip.width-(borders[1].width); 1420 | stack.clip.height = stack.clip.height-(borders[2].width); 1421 | } 1422 | */ 1423 | if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ 1424 | if (options.iframeDefault === "default"){ 1425 | bgcolor = "#efefef"; 1426 | }else{ 1427 | bgcolor = options.iframeDefault; 1428 | } 1429 | } 1430 | 1431 | // draw base element bgcolor 1432 | 1433 | bgbounds = { 1434 | left: x + borders[3].width, 1435 | top: y + borders[0].width, 1436 | width: w - (borders[1].width + borders[3].width), 1437 | height: h - (borders[0].width + borders[2].width) 1438 | }; 1439 | 1440 | //if (this.withinBounds(stack.clip,bgbounds)){ 1441 | 1442 | if (stack.clip){ 1443 | bgbounds = clipBounds(bgbounds, stack.clip); 1444 | 1445 | //} 1446 | 1447 | } 1448 | 1449 | 1450 | if (bgbounds.height > 0 && bgbounds.width > 0){ 1451 | renderRect( 1452 | ctx, 1453 | bgbounds.left, 1454 | bgbounds.top, 1455 | bgbounds.width, 1456 | bgbounds.height, 1457 | bgcolor 1458 | ); 1459 | 1460 | renderBackground(el, bgbounds, ctx); 1461 | } 1462 | 1463 | switch(el.nodeName){ 1464 | case "IMG": 1465 | imgSrc = el.getAttribute('src'); 1466 | image = loadImage(imgSrc); 1467 | if (image){ 1468 | 1469 | paddingLeft = getCSSInt(el, 'paddingLeft'); 1470 | paddingTop = getCSSInt(el, 'paddingTop'); 1471 | paddingRight = getCSSInt(el, 'paddingRight'); 1472 | paddingBottom = getCSSInt(el, 'paddingBottom'); 1473 | 1474 | 1475 | renderImage( 1476 | ctx, 1477 | image, 1478 | 0, //sx 1479 | 0, //sy 1480 | image.width, //sw 1481 | image.height, //sh 1482 | x + paddingLeft + borders[3].width, //dx 1483 | y + paddingTop + borders[0].width, // dy 1484 | bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 1485 | bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 1486 | ); 1487 | 1488 | }else{ 1489 | h2clog("html2canvas: Error loading :" + imgSrc); 1490 | } 1491 | break; 1492 | case "INPUT": 1493 | // TODO add all relevant type's, i.e. HTML5 new stuff 1494 | // todo add support for placeholder attribute for browsers which support it 1495 | if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ 1496 | 1497 | renderFormValue(el, bounds, stack); 1498 | 1499 | 1500 | /* 1501 | this just doesn't work well enough 1502 | 1503 | this.newText(el,{ 1504 | nodeValue:el.value, 1505 | splitText: function(){ 1506 | return this; 1507 | }, 1508 | formValue:true 1509 | },stack); 1510 | */ 1511 | } 1512 | break; 1513 | case "TEXTAREA": 1514 | if (el.value.length > 0){ 1515 | renderFormValue(el, bounds, stack); 1516 | } 1517 | break; 1518 | case "SELECT": 1519 | if (el.options.length > 0){ 1520 | renderFormValue(el, bounds, stack); 1521 | } 1522 | break; 1523 | case "LI": 1524 | renderListItem(el, stack, bgbounds); 1525 | break; 1526 | case "CANVAS": 1527 | paddingLeft = getCSSInt(el, 'paddingLeft'); 1528 | paddingTop = getCSSInt(el, 'paddingTop'); 1529 | paddingRight = getCSSInt(el, 'paddingRight'); 1530 | paddingBottom = getCSSInt(el, 'paddingBottom'); 1531 | renderImage( 1532 | ctx, 1533 | el, 1534 | 0, //sx 1535 | 0, //sy 1536 | el.width, //sw 1537 | el.height, //sh 1538 | x + paddingLeft + borders[3].width, //dx 1539 | y + paddingTop + borders[0].width, // dy 1540 | bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 1541 | bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 1542 | ); 1543 | break; 1544 | } 1545 | 1546 | return zindex.children[stackLength - 1]; 1547 | } 1548 | 1549 | 1550 | 1551 | function parseElement (el, stack) { 1552 | 1553 | // skip hidden elements and their children 1554 | if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden") { 1555 | 1556 | stack = renderElement(el, stack) || stack; 1557 | 1558 | ctx = stack.ctx; 1559 | 1560 | if ( !ignoreElementsRegExp.test( el.nodeName ) ) { 1561 | var elementChildren = html2canvas.Util.Children( el ), 1562 | i, 1563 | node, 1564 | childrenLen; 1565 | for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { 1566 | node = elementChildren[i]; 1567 | 1568 | if ( node.nodeType === 1 ) { 1569 | parseElement(node, stack); 1570 | }else if ( node.nodeType === 3 ) { 1571 | renderText(el, node, stack); 1572 | } 1573 | 1574 | } 1575 | 1576 | } 1577 | } 1578 | } 1579 | 1580 | stack = renderElement(element, null); 1581 | 1582 | // parse every child element 1583 | for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ 1584 | parseElement(children[i], stack); 1585 | } 1586 | 1587 | 1588 | stack.backgroundColor = getCSS( body, "backgroundColor" ); 1589 | 1590 | return stack; 1591 | 1592 | }; 1593 | 1594 | function h2czContext(zindex) { 1595 | return { 1596 | zindex: zindex, 1597 | children: [] 1598 | }; 1599 | }; 1600 | 1601 | /* 1602 | html2canvas v0.33 1603 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 1604 | http://www.twitter.com/niklasvh 1605 | 1606 | Released under MIT License 1607 | */ 1608 | 1609 | html2canvas.Preload = function(element, opts){ 1610 | 1611 | var options = { 1612 | proxy: "http://html2canvas.appspot.com/", 1613 | timeout: 0, // no timeout 1614 | useCORS: false, // try to load images as CORS (where available), before falling back to proxy 1615 | allowTaint: false // whether to allow images to taint the canvas, won't need proxy if set to true 1616 | }, 1617 | images = { 1618 | numLoaded: 0, // also failed are counted here 1619 | numFailed: 0, 1620 | numTotal: 0, 1621 | cleanupDone: false 1622 | }, 1623 | pageOrigin, 1624 | methods, 1625 | i, 1626 | count = 0, 1627 | doc = element.ownerDocument, 1628 | domImages = doc.images, // TODO probably should limit it to images present in the element only 1629 | imgLen = domImages.length, 1630 | link = doc.createElement("a"), 1631 | supportCORS = (function( img ){ 1632 | return (img.crossOrigin !== undefined); 1633 | })(new Image()), 1634 | timeoutTimer; 1635 | 1636 | link.href = window.location.href; 1637 | pageOrigin = link.protocol + link.host; 1638 | opts = opts || {}; 1639 | 1640 | options = html2canvas.Util.Extend(opts, options); 1641 | 1642 | 1643 | 1644 | element = element || doc.body; 1645 | 1646 | function isSameOrigin(url){ 1647 | link.href = url; 1648 | link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ 1649 | var origin = link.protocol + link.host; 1650 | return (origin === pageOrigin); 1651 | } 1652 | 1653 | function start(){ 1654 | h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); 1655 | if (!images.firstRun && images.numLoaded >= images.numTotal){ 1656 | 1657 | /* 1658 | this.log('Finished loading '+this.imagesLoaded+' images, Started parsing'); 1659 | this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow; 1660 | document.getElementsByTagName('body')[0].style.overflow = "hidden"; 1661 | */ 1662 | if (typeof options.complete === "function"){ 1663 | options.complete(images); 1664 | } 1665 | 1666 | h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); 1667 | } 1668 | } 1669 | 1670 | // TODO modify proxy to serve images with CORS enabled, where available 1671 | function proxyGetImage(url, img, imageObj){ 1672 | var callback_name, 1673 | scriptUrl = options.proxy, 1674 | script; 1675 | 1676 | link.href = url; 1677 | url = link.href; // work around for pages with base href="" set - WARNING: this may change the url 1678 | 1679 | callback_name = 'html2canvas_' + (count++); 1680 | imageObj.callbackname = callback_name; 1681 | 1682 | if (scriptUrl.indexOf("?") > -1) { 1683 | scriptUrl += "&"; 1684 | } else { 1685 | scriptUrl += "?"; 1686 | } 1687 | scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; 1688 | script = doc.createElement("script"); 1689 | 1690 | window[callback_name] = function(a){ 1691 | if (a.substring(0,6) === "error:"){ 1692 | imageObj.succeeded = false; 1693 | images.numLoaded++; 1694 | images.numFailed++; 1695 | start(); 1696 | } else { 1697 | setImageLoadHandlers(img, imageObj); 1698 | img.src = a; 1699 | } 1700 | 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) 1701 | try { 1702 | delete window[callback_name]; // for all browser that support this 1703 | } catch(ex) {} 1704 | script.parentNode.removeChild(script); 1705 | script = null; 1706 | delete imageObj.script; 1707 | delete imageObj.callbackname; 1708 | }; 1709 | 1710 | script.setAttribute("type", "text/javascript"); 1711 | script.setAttribute("src", scriptUrl); 1712 | imageObj.script = script; 1713 | window.document.body.appendChild(script); 1714 | 1715 | } 1716 | 1717 | function getImages (el) { 1718 | 1719 | 1720 | 1721 | // if (!this.ignoreRe.test(el.nodeName)){ 1722 | // 1723 | 1724 | var contents = html2canvas.Util.Children(el), 1725 | i, 1726 | contentsLen = contents.length, 1727 | background_image, 1728 | src, 1729 | img, 1730 | elNodeType = false; 1731 | 1732 | for (i = 0; i < contentsLen; i+=1 ){ 1733 | // var ignRe = new RegExp("("+this.ignoreElements+")"); 1734 | // if (!ignRe.test(element.nodeName)){ 1735 | getImages(contents[i]); 1736 | // } 1737 | } 1738 | 1739 | // } 1740 | try { 1741 | elNodeType = el.nodeType; 1742 | } catch (ex) { 1743 | elNodeType = false; 1744 | h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); 1745 | } 1746 | 1747 | if (elNodeType === 1 || elNodeType === undefined){ 1748 | 1749 | // opera throws exception on external-content.html 1750 | try { 1751 | background_image = html2canvas.Util.getCSS(el, 'backgroundImage'); 1752 | }catch(e) { 1753 | h2clog("html2canvas: failed to get background-image - Exception: " + e.message); 1754 | } 1755 | if ( background_image && background_image !== "1" && background_image !== "none" ) { 1756 | 1757 | // TODO add multi image background support 1758 | 1759 | if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") { 1760 | 1761 | img = html2canvas.Generate.Gradient( background_image, html2canvas.Util.Bounds( el ) ); 1762 | 1763 | if ( img !== undefined ){ 1764 | images[background_image] = { 1765 | img: img, 1766 | succeeded: true 1767 | }; 1768 | images.numTotal++; 1769 | images.numLoaded++; 1770 | start(); 1771 | 1772 | } 1773 | 1774 | } else { 1775 | src = html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); 1776 | methods.loadImage(src); 1777 | } 1778 | 1779 | /* 1780 | 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"){ 1781 | // TODO add multi image background support 1782 | src = html2canvas.Util.backgroundImage(background_image.split(",")[0]); 1783 | methods.loadImage(src); */ 1784 | } 1785 | } 1786 | } 1787 | 1788 | function setImageLoadHandlers(img, imageObj) { 1789 | img.onload = function() { 1790 | if ( imageObj.timer !== undefined ) { 1791 | // CORS succeeded 1792 | window.clearTimeout( imageObj.timer ); 1793 | } 1794 | images.numLoaded++; 1795 | imageObj.succeeded = true; 1796 | start(); 1797 | }; 1798 | img.onerror = function() { 1799 | 1800 | if (img.crossOrigin === "anonymous") { 1801 | // CORS failed 1802 | window.clearTimeout( imageObj.timer ); 1803 | 1804 | // let's try with proxy instead 1805 | if ( options.proxy ) { 1806 | var src = img.src; 1807 | img = new Image(); 1808 | imageObj.img = img; 1809 | img.src = src; 1810 | 1811 | proxyGetImage( img.src, img, imageObj ); 1812 | return; 1813 | } 1814 | } 1815 | 1816 | 1817 | images.numLoaded++; 1818 | images.numFailed++; 1819 | imageObj.succeeded = false; 1820 | start(); 1821 | 1822 | }; 1823 | } 1824 | 1825 | // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 1826 | function isComplete() { 1827 | if (!this.img.complete) { 1828 | this.timer = window.setTimeout(this.img.customComplete, 100) 1829 | } else { 1830 | this.img.onerror(); 1831 | } 1832 | } 1833 | 1834 | methods = { 1835 | loadImage: function( src ) { 1836 | var img, imageObj; 1837 | if ( src && images[src] === undefined ) { 1838 | img = new Image(); 1839 | if ( src.match(/data:image\/.*;base64,/i) ) { 1840 | img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); 1841 | imageObj = images[src] = { img: img }; 1842 | images.numTotal++; 1843 | setImageLoadHandlers(img, imageObj); 1844 | } else if ( isSameOrigin( src ) || options.allowTaint === true ) { 1845 | imageObj = images[src] = { img: img }; 1846 | images.numTotal++; 1847 | setImageLoadHandlers(img, imageObj); 1848 | img.src = src; 1849 | } else if ( supportCORS && !options.allowTaint && options.useCORS ) { 1850 | // attempt to load with CORS 1851 | 1852 | img.crossOrigin = "anonymous"; 1853 | imageObj = images[src] = { img: img }; 1854 | images.numTotal++; 1855 | setImageLoadHandlers(img, imageObj); 1856 | img.src = src; 1857 | 1858 | img.customComplete = isComplete.bind(imageObj); 1859 | img.customComplete(); 1860 | 1861 | } else if ( options.proxy ) { 1862 | imageObj = images[src] = { img: img }; 1863 | images.numTotal++; 1864 | proxyGetImage( src, img, imageObj ); 1865 | } 1866 | } 1867 | 1868 | }, 1869 | cleanupDOM: function(cause) { 1870 | var img, src; 1871 | if (!images.cleanupDone) { 1872 | if (cause && typeof cause === "string") { 1873 | h2clog("html2canvas: Cleanup because: " + cause); 1874 | } else { 1875 | h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); 1876 | } 1877 | 1878 | for (src in images) { 1879 | if (images.hasOwnProperty(src)) { 1880 | img = images[src]; 1881 | if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { 1882 | // cancel proxy image request 1883 | 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) 1884 | try { 1885 | delete window[img.callbackname]; // for all browser that support this 1886 | } catch(ex) {} 1887 | if (img.script && img.script.parentNode) { 1888 | img.script.setAttribute("src", "about:blank"); // try to cancel running request 1889 | img.script.parentNode.removeChild(img.script); 1890 | } 1891 | images.numLoaded++; 1892 | images.numFailed++; 1893 | h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); 1894 | } 1895 | } 1896 | } 1897 | 1898 | // cancel any pending requests 1899 | if(window.stop !== undefined) { 1900 | window.stop(); 1901 | } else if(document.execCommand !== undefined) { 1902 | document.execCommand("Stop", false); 1903 | } 1904 | if (document.close !== undefined) { 1905 | document.close(); 1906 | } 1907 | images.cleanupDone = true; 1908 | if (!(cause && typeof cause === "string")) { 1909 | start(); 1910 | } 1911 | } 1912 | }, 1913 | renderingDone: function() { 1914 | if (timeoutTimer) { 1915 | window.clearTimeout(timeoutTimer); 1916 | } 1917 | } 1918 | 1919 | }; 1920 | 1921 | if (options.timeout > 0) { 1922 | timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); 1923 | } 1924 | h2clog('html2canvas: Preload starts: finding background-images'); 1925 | images.firstRun = true; 1926 | 1927 | getImages( element ); 1928 | 1929 | h2clog('html2canvas: Preload: Finding images'); 1930 | // load images 1931 | for (i = 0; i < imgLen; i+=1){ 1932 | methods.loadImage( domImages[i].getAttribute( "src" ) ); 1933 | } 1934 | 1935 | images.firstRun = false; 1936 | h2clog('html2canvas: Preload: Done.'); 1937 | if ( images.numTotal === images.numLoaded ) { 1938 | start(); 1939 | } 1940 | 1941 | return methods; 1942 | 1943 | }; 1944 | 1945 | 1946 | 1947 | 1948 | /* 1949 | html2canvas v0.33 1950 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 1951 | http://www.twitter.com/niklasvh 1952 | 1953 | Released under MIT License 1954 | */ 1955 | function h2cRenderContext(width, height) { 1956 | var storage = []; 1957 | return { 1958 | storage: storage, 1959 | width: width, 1960 | height: height, 1961 | fillRect: function () { 1962 | storage.push({ 1963 | type: "function", 1964 | name: "fillRect", 1965 | 'arguments': arguments 1966 | }); 1967 | }, 1968 | drawImage: function () { 1969 | storage.push({ 1970 | type: "function", 1971 | name: "drawImage", 1972 | 'arguments': arguments 1973 | }); 1974 | }, 1975 | fillText: function () { 1976 | storage.push({ 1977 | type: "function", 1978 | name: "fillText", 1979 | 'arguments': arguments 1980 | }); 1981 | }, 1982 | setVariable: function (variable, value) { 1983 | storage.push({ 1984 | type: "variable", 1985 | name: variable, 1986 | 'arguments': value 1987 | }); 1988 | } 1989 | }; 1990 | } 1991 | 1992 | /* 1993 | html2canvas v0.33 1994 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 1995 | http://www.twitter.com/niklasvh 1996 | 1997 | Released under MIT License 1998 | */ 1999 | html2canvas.Renderer = function(parseQueue, opts){ 2000 | 2001 | 2002 | var options = { 2003 | "width": null, 2004 | "height": null, 2005 | "renderer": "canvas", 2006 | "taintTest": true // do a taint test with all images before applying to canvas 2007 | }, 2008 | queue = [], 2009 | canvas, 2010 | usingFlashcanvas = false, 2011 | flashMaxSize = 2880, // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata 2012 | doc = document; 2013 | 2014 | options = html2canvas.Util.Extend(opts, options); 2015 | 2016 | 2017 | 2018 | function sortZ(zStack){ 2019 | var subStacks = [], 2020 | stackValues = [], 2021 | zStackChildren = zStack.children, 2022 | s, 2023 | i, 2024 | stackLen, 2025 | zValue, 2026 | zLen, 2027 | stackChild, 2028 | b, 2029 | subStackLen; 2030 | 2031 | 2032 | for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ 2033 | 2034 | stackChild = zStackChildren[s]; 2035 | 2036 | if (stackChild.children && stackChild.children.length > 0){ 2037 | subStacks.push(stackChild); 2038 | stackValues.push(stackChild.zindex); 2039 | }else{ 2040 | queue.push(stackChild); 2041 | } 2042 | 2043 | } 2044 | 2045 | stackValues.sort(function(a, b) { 2046 | return a - b; 2047 | }); 2048 | 2049 | for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ 2050 | zValue = stackValues[i]; 2051 | for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ 2052 | 2053 | if (subStacks[b].zindex === zValue){ 2054 | stackChild = subStacks.splice(b, 1); 2055 | sortZ(stackChild[0]); 2056 | break; 2057 | 2058 | } 2059 | } 2060 | } 2061 | 2062 | } 2063 | 2064 | function canvasRenderer(zStack){ 2065 | 2066 | sortZ(zStack.zIndex); 2067 | 2068 | 2069 | var ctx = canvas.getContext("2d"), 2070 | storageContext, 2071 | i, 2072 | queueLen, 2073 | a, 2074 | newCanvas, 2075 | bounds, 2076 | testCanvas = document.createElement("canvas"), 2077 | hasCTX = ( testCanvas.getContext !== undefined ), 2078 | storageLen, 2079 | renderItem, 2080 | testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {}, 2081 | safeImages = [], 2082 | fstyle; 2083 | 2084 | canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) ); 2085 | canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) ); 2086 | 2087 | fstyle = ctx.fillStyle; 2088 | ctx.fillStyle = zStack.backgroundColor; 2089 | ctx.fillRect(0, 0, canvas.width, canvas.height); 2090 | ctx.fillStyle = fstyle; 2091 | 2092 | for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ 2093 | 2094 | storageContext = queue.splice(0, 1)[0]; 2095 | storageContext.canvasPosition = storageContext.canvasPosition || {}; 2096 | 2097 | //this.canvasRenderContext(storageContext,parentctx); 2098 | 2099 | // set common settings for canvas 2100 | ctx.textBaseline = "bottom"; 2101 | 2102 | if (storageContext.clip){ 2103 | ctx.save(); 2104 | ctx.beginPath(); 2105 | // console.log(storageContext); 2106 | ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); 2107 | ctx.clip(); 2108 | 2109 | } 2110 | 2111 | if (storageContext.ctx.storage){ 2112 | 2113 | for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ 2114 | 2115 | renderItem = storageContext.ctx.storage[a]; 2116 | 2117 | 2118 | switch(renderItem.type){ 2119 | case "variable": 2120 | ctx[renderItem.name] = renderItem['arguments']; 2121 | break; 2122 | case "function": 2123 | if (renderItem.name === "fillRect") { 2124 | 2125 | if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { 2126 | ctx.fillRect.apply( ctx, renderItem['arguments'] ); 2127 | } 2128 | }else if(renderItem.name === "fillText") { 2129 | if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { 2130 | ctx.fillText.apply( ctx, renderItem['arguments'] ); 2131 | } 2132 | }else if(renderItem.name === "drawImage") { 2133 | 2134 | if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ 2135 | if ( hasCTX && options.taintTest ) { 2136 | if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { 2137 | testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); 2138 | try { 2139 | testctx.getImageData( 0, 0, 1, 1 ); 2140 | } catch(e) { 2141 | testCanvas = document.createElement("canvas"); 2142 | testctx = testCanvas.getContext("2d"); 2143 | continue; 2144 | } 2145 | 2146 | safeImages.push( renderItem['arguments'][ 0 ].src ); 2147 | 2148 | } 2149 | } 2150 | ctx.drawImage.apply( ctx, renderItem['arguments'] ); 2151 | } 2152 | } 2153 | 2154 | 2155 | break; 2156 | default: 2157 | 2158 | } 2159 | 2160 | } 2161 | 2162 | } 2163 | if (storageContext.clip){ 2164 | ctx.restore(); 2165 | } 2166 | 2167 | 2168 | 2169 | 2170 | } 2171 | h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); 2172 | 2173 | // this.canvasRenderStorage(queue,this.ctx); 2174 | queueLen = options.elements.length; 2175 | 2176 | if (queueLen === 1) { 2177 | if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) { 2178 | // crop image to the bounds of selected (single) element 2179 | bounds = html2canvas.Util.Bounds( options.elements[ 0 ] ); 2180 | newCanvas = doc.createElement('canvas'); 2181 | newCanvas.width = bounds.width; 2182 | newCanvas.height = bounds.height; 2183 | ctx = newCanvas.getContext("2d"); 2184 | 2185 | ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height ); 2186 | delete canvas; 2187 | return newCanvas; 2188 | } 2189 | } else { 2190 | // TODO clip and resize multiple elements 2191 | /* 2192 | for ( i = 0; i < queueLen; i+=1 ) { 2193 | if (options.elements[ i ] instanceof Element) { 2194 | 2195 | } 2196 | 2197 | }*/ 2198 | } 2199 | 2200 | 2201 | 2202 | 2203 | return canvas; 2204 | } 2205 | 2206 | function svgRenderer(zStack){ 2207 | sortZ(zStack.zIndex); 2208 | 2209 | var svgNS = "http://www.w3.org/2000/svg", 2210 | svg = doc.createElementNS(svgNS, "svg"), 2211 | xlinkNS = "http://www.w3.org/1999/xlink", 2212 | defs = doc.createElementNS(svgNS, "defs"), 2213 | i, 2214 | a, 2215 | queueLen, 2216 | storageLen, 2217 | storageContext, 2218 | renderItem, 2219 | el, 2220 | settings = {}, 2221 | text, 2222 | fontStyle, 2223 | clipId = 0; 2224 | 2225 | svg.setAttribute("version", "1.1"); 2226 | svg.setAttribute("baseProfile", "full"); 2227 | 2228 | svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height)); 2229 | svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px"); 2230 | svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px"); 2231 | svg.setAttribute("preserveAspectRatio", "none"); 2232 | svg.appendChild(defs); 2233 | 2234 | 2235 | 2236 | for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ 2237 | 2238 | storageContext = queue.splice(0, 1)[0]; 2239 | storageContext.canvasPosition = storageContext.canvasPosition || {}; 2240 | 2241 | //this.canvasRenderContext(storageContext,parentctx); 2242 | 2243 | 2244 | /* 2245 | if (storageContext.clip){ 2246 | ctx.save(); 2247 | ctx.beginPath(); 2248 | // console.log(storageContext); 2249 | ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); 2250 | ctx.clip(); 2251 | 2252 | }*/ 2253 | 2254 | if (storageContext.ctx.storage){ 2255 | 2256 | for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ 2257 | 2258 | renderItem = storageContext.ctx.storage[a]; 2259 | 2260 | 2261 | 2262 | switch(renderItem.type){ 2263 | case "variable": 2264 | settings[renderItem.name] = renderItem['arguments']; 2265 | break; 2266 | case "function": 2267 | if (renderItem.name === "fillRect") { 2268 | 2269 | el = doc.createElementNS(svgNS, "rect"); 2270 | el.setAttribute("x", renderItem['arguments'][0]); 2271 | el.setAttribute("y", renderItem['arguments'][1]); 2272 | el.setAttribute("width", renderItem['arguments'][2]); 2273 | el.setAttribute("height", renderItem['arguments'][3]); 2274 | el.setAttribute("fill", settings.fillStyle); 2275 | svg.appendChild(el); 2276 | 2277 | } else if(renderItem.name === "fillText") { 2278 | el = doc.createElementNS(svgNS, "text"); 2279 | 2280 | fontStyle = settings.font.split(" "); 2281 | 2282 | el.style.fontVariant = fontStyle.splice(0, 1)[0]; 2283 | el.style.fontWeight = fontStyle.splice(0, 1)[0]; 2284 | el.style.fontStyle = fontStyle.splice(0, 1)[0]; 2285 | el.style.fontSize = fontStyle.splice(0, 1)[0]; 2286 | 2287 | el.setAttribute("x", renderItem['arguments'][1]); 2288 | el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3)); 2289 | 2290 | el.setAttribute("fill", settings.fillStyle); 2291 | 2292 | 2293 | 2294 | 2295 | // TODO get proper baseline 2296 | el.style.dominantBaseline = "text-before-edge"; 2297 | el.style.fontFamily = fontStyle.join(" "); 2298 | 2299 | text = doc.createTextNode(renderItem['arguments'][0]); 2300 | el.appendChild(text); 2301 | 2302 | 2303 | svg.appendChild(el); 2304 | 2305 | 2306 | 2307 | } else if(renderItem.name === "drawImage") { 2308 | 2309 | if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ 2310 | 2311 | // TODO check whether even any clipping is necessary for this particular image 2312 | el = doc.createElementNS(svgNS, "clipPath"); 2313 | el.setAttribute("id", "clipId" + clipId); 2314 | 2315 | text = doc.createElementNS(svgNS, "rect"); 2316 | text.setAttribute("x", renderItem['arguments'][5] ); 2317 | text.setAttribute("y", renderItem['arguments'][6]); 2318 | 2319 | text.setAttribute("width", renderItem['arguments'][3]); 2320 | text.setAttribute("height", renderItem['arguments'][4]); 2321 | el.appendChild(text); 2322 | defs.appendChild(el); 2323 | 2324 | el = doc.createElementNS(svgNS, "image"); 2325 | el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); 2326 | el.setAttribute("width", renderItem['arguments'][0].width); 2327 | el.setAttribute("height", renderItem['arguments'][0].height); 2328 | el.setAttribute("x", renderItem['arguments'][5] - renderItem['arguments'][1]); 2329 | el.setAttribute("y", renderItem['arguments'][6] - renderItem['arguments'][2]); 2330 | el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); 2331 | // el.setAttribute("xlink:href", ); 2332 | 2333 | 2334 | el.setAttribute("preserveAspectRatio", "none"); 2335 | 2336 | svg.appendChild(el); 2337 | 2338 | 2339 | clipId += 1; 2340 | /* 2341 | ctx.drawImage( 2342 | renderItem['arguments'][0], 2343 | renderItem['arguments'][1], 2344 | renderItem['arguments'][2], 2345 | renderItem['arguments'][3], 2346 | renderItem['arguments'][4], 2347 | renderItem['arguments'][5], 2348 | renderItem['arguments'][6], 2349 | renderItem['arguments'][7], 2350 | renderItem['arguments'][8] 2351 | ); 2352 | */ 2353 | } 2354 | } 2355 | 2356 | 2357 | 2358 | break; 2359 | default: 2360 | 2361 | } 2362 | 2363 | } 2364 | 2365 | } 2366 | /* 2367 | if (storageContext.clip){ 2368 | ctx.restore(); 2369 | } 2370 | */ 2371 | 2372 | 2373 | 2374 | } 2375 | 2376 | 2377 | 2378 | 2379 | 2380 | 2381 | 2382 | 2383 | 2384 | 2385 | h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); 2386 | 2387 | return svg; 2388 | 2389 | } 2390 | 2391 | 2392 | //this.each(this.opts.renderOrder.split(" "),function(i,renderer){ 2393 | 2394 | //options.renderer = "svg"; 2395 | 2396 | switch(options.renderer.toLowerCase()){ 2397 | case "canvas": 2398 | canvas = doc.createElement('canvas'); 2399 | if (canvas.getContext){ 2400 | h2clog("html2canvas: Renderer: using canvas renderer"); 2401 | return canvasRenderer(parseQueue); 2402 | } else { 2403 | usingFlashcanvas = true; 2404 | h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); 2405 | var script = doc.createElement("script"); 2406 | script.src = options.flashcanvas; 2407 | 2408 | script.onload = (function(script, func){ 2409 | var intervalFunc; 2410 | 2411 | if (script.onload === undefined) { 2412 | // IE lack of support for script onload 2413 | 2414 | if( script.onreadystatechange !== undefined ) { 2415 | 2416 | intervalFunc = function() { 2417 | if (script.readyState !== "loaded" && script.readyState !== "complete") { 2418 | window.setTimeout( intervalFunc, 250 ); 2419 | 2420 | } else { 2421 | // it is loaded 2422 | func(); 2423 | 2424 | } 2425 | 2426 | }; 2427 | 2428 | window.setTimeout( intervalFunc, 250 ); 2429 | 2430 | } else { 2431 | h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded"); 2432 | 2433 | } 2434 | 2435 | } else { 2436 | return func; 2437 | } 2438 | 2439 | })(script, function(){ 2440 | 2441 | if (typeof window.FlashCanvas !== "undefined") { 2442 | h2clog("html2canvas: Renderer: Flashcanvas initialized"); 2443 | window.FlashCanvas.initElement( canvas ); 2444 | canvasRenderer(parseQueue); 2445 | } 2446 | }); 2447 | 2448 | doc.body.appendChild( script ); 2449 | 2450 | return canvas; 2451 | } 2452 | break; 2453 | case "svg": 2454 | if (doc.createElementNS){ 2455 | h2clog("html2canvas: Renderer: using SVG renderer"); 2456 | return svgRenderer(parseQueue); 2457 | } 2458 | break; 2459 | 2460 | } 2461 | 2462 | 2463 | 2464 | //}); 2465 | 2466 | 2467 | return this; 2468 | 2469 | 2470 | 2471 | }; 2472 | 2473 | /* 2474 | html2canvas v0.33 2475 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2476 | http://www.twitter.com/niklasvh 2477 | 2478 | Released under MIT License 2479 | */ 2480 | window.html2canvas = html2canvas; 2481 | }(window, document)); 2482 | 2483 | --------------------------------------------------------------------------------