Compare commits

...

513 Commits

Author SHA1 Message Date
Niklas von Hertzen
bebb353b3f Merge pull request #703 from niklasvh/travis-fix
Lower travis nodejs version back to 0.10
2015-10-20 23:37:35 +03:00
Niklas von Hertzen
eb5ac1122c Lower travis nodejs version back to 0.10 2015-10-20 23:16:49 +03:00
Niklas von Hertzen
ae97dd9a3d Remove npm bower dependency 2015-10-19 23:13:05 +03:00
Niklas von Hertzen
11fdc501b1 Fix package dependency versions 2015-10-19 02:57:40 +03:00
Niklas von Hertzen
4df19968b5 0.5.0-beta2 2015-10-19 01:49:34 +03:00
Niklas von Hertzen
9ab7f8cdb1 Update travis npm deployment 2015-10-19 01:44:08 +03:00
Niklas von Hertzen
5c5531fd47 Update node version for travis 2015-10-19 01:34:38 +03:00
Niklas von Hertzen
e88ac871a3 Remove Promise polyfill 2015-10-19 01:25:03 +03:00
Niklas von Hertzen
2a2ad9bb65 0.5.0-beta1 2015-10-19 01:00:58 +03:00
Niklas von Hertzen
81e60975cc Update build/deploy workflow 2015-10-19 01:00:04 +03:00
Niklas von Hertzen
a0669300c4 Update grunt-mocha-phantomjs to 2.0.0 2015-10-18 23:58:10 +03:00
Niklas von Hertzen
ba9758cf14 Reformat mocha text shadow test names 2015-10-18 23:57:55 +03:00
Niklas von Hertzen
aa05241ff8 Fix gradient tests 2015-10-18 23:57:41 +03:00
Niklas von Hertzen
5b4a6c26ee Add another border-radius test 2015-10-18 23:15:51 +03:00
Niklas von Hertzen
364a8aac1c Merge remote-tracking branch 'origin/pr/645' 2015-10-18 23:15:12 +03:00
Niklas von Hertzen
46078acf71 Fix #688 fatal exception on unmatched color stop 2015-10-18 16:51:53 +03:00
Niklas von Hertzen
4b37909f09 Fix support for requirejs/amd 2015-08-30 02:27:38 +03:00
hyojin.kwak
90f9eeba83 Fix #599 2015-06-10 16:13:17 +09:00
Niklas von Hertzen
98ee30643a Fix iOS scrolling issue on clone 2015-03-30 00:58:50 +03:00
Niklas von Hertzen
a49c3a2320 Correctly handle named colors in gradients 2015-03-29 23:20:17 +03:00
Niklas von Hertzen
4b80102e77 Merge pull request #545 from cornedor/ie-transforms
Transform matrix for IE
2015-03-08 01:03:22 +02:00
Corné Dorrestijn
9201cf7e95 Accept matrix3d and convert it 2015-03-05 15:52:23 +01:00
Niklas von Hertzen
c2baf42145 Fix fail on transparent colors in linear gradients 2015-03-01 17:43:18 +02:00
Niklas von Hertzen
d9a9615ed7 Add acid2 test case 2015-03-01 17:12:09 +02:00
Niklas von Hertzen
585a96a918 Preserve scrolling positions of nodes on clone (#511) 2015-02-28 16:51:52 +02:00
Niklas von Hertzen
9a0d43d60b Bump v0.5.0-alpha2 2015-02-03 22:11:20 +02:00
Niklas von Hertzen
3671de81f9 Fix module name casing 2015-02-03 21:39:57 +02:00
Niklas von Hertzen
f3b6df267e Switch build to use browserify (#502) 2015-02-03 21:34:05 +02:00
Niklas von Hertzen
60619dca72 Fix #517 2015-01-26 22:55:10 +02:00
Niklas von Hertzen
ed299b3db1 Remove dead code 2015-01-20 19:51:52 +02:00
Niklas von Hertzen
c7e484af89 Add npm bower postpublish script 2015-01-19 23:52:50 +02:00
Niklas von Hertzen
19ecd8bd7f Remove version from bower.json 2015-01-19 23:39:18 +02:00
Niklas von Hertzen
edb113c230 Make html2canvas requireable in nodejs 2015-01-19 23:33:29 +02:00
Niklas von Hertzen
33cd2c5ef6 Move Changelog away from Readme 2015-01-19 22:52:04 +02:00
Niklas von Hertzen
3400354d78 Remove old test results 2015-01-19 22:51:45 +02:00
Niklas von Hertzen
fc01263f68 Add comment regarding IE9 clone node 2015-01-19 22:30:10 +02:00
Niklas von Hertzen
399ae9f33d Fix #503 2015-01-19 22:28:10 +02:00
Niklas von Hertzen
db88784655 Add test for adding class prior to rendering (#508) 2015-01-19 20:31:31 +02:00
Niklas von Hertzen
77c73c478f Fix iframe proxy 2015-01-18 21:38:05 +02:00
Niklas von Hertzen
37b39ec716 Add task for running mocha tests with webdriver for testing proxies 2015-01-18 14:31:53 +02:00
Niklas von Hertzen
6d53461a68 Update bluebird to 2.7.1 2015-01-17 22:12:48 +02:00
Niklas von Hertzen
33844129e9 Update .gitignore 2015-01-17 22:12:28 +02:00
Niklas von Hertzen
0df71b34f3 Add travis notification hook for gitter 2015-01-13 23:18:58 +02:00
Niklas von Hertzen
3996449e88 Add gitter badge to README 2015-01-13 23:13:00 +02:00
Niklas von Hertzen
2699670bf1 Improve text-shadow tests 2015-01-13 22:50:57 +02:00
Niklas von Hertzen
7a58ad019f Fix background rendering regression #496 2015-01-10 20:53:06 +02:00
Niklas von Hertzen
9b372a4399 Correctly clone <select> and <textarea> value property 2015-01-05 22:58:36 +02:00
Niklas von Hertzen
498527918c Correctly render input[type="password"] value 2015-01-05 21:36:53 +02:00
Niklas von Hertzen
e363f3813e Rename mocha/index.html to mocha/parsing.html 2015-01-05 21:22:58 +02:00
Niklas von Hertzen
433dc98177 Reduce render count for multiple render mocha test 2015-01-05 20:54:07 +02:00
Niklas von Hertzen
b6a73b635a Merge pull request #495 from usmonster/update-promise-polyfill
Temporarily add manual update to es6-promise polyfill
2014-12-29 21:35:22 +02:00
Usman Akeju
e2713cd6f9 Temporarily add manual update to es6-promise polyfill
This replaces an old and outdated Promise polyfill that was included
with previous versions of html2canvas, and which consistently overwrote
native browser implementations when it shouldn't have. It is essentially
a (slightly modified) paste of the most current minified version of
es6-promise, found here:
https://es6-promises.s3.amazonaws.com/es6-promise-2.0.1.min.js

The plan is to eventually include this polyfill as a submodule
dependency, but the build process for es6-promise is a bit
cumbersome/opaque/painful at the moment, so this manual update was an
easier interim solution.

Please note that I had to manually add the actual call to
`ES6Promise.polyfill()` to load the polyfill--this will become
unnecessarry in future versions of es6-promise. Note also that I had to
manually pass `window` to the polyfill's IIFE, since `this` is an empty
object in the current scope.

Ref niklasvh/html2canvas#494.
2014-12-29 16:35:39 +01:00
Niklas von Hertzen
4b771d558a Increase mocha timeout for multiple-render tests 2014-12-24 17:45:04 +02:00
Niklas von Hertzen
2f5f9f6e59 Allow multiple renders to occur simultaneously 2014-12-24 17:34:31 +02:00
Niklas von Hertzen
60368d4670 Update build instructions with grunt and uglifyjs details 2014-12-24 17:24:07 +02:00
Niklas von Hertzen
f5e318d968 Manually call toString for colors 2014-12-13 19:23:00 +02:00
Niklas von Hertzen
612e59c3d3 Implement border-inset color transforms 2014-12-13 19:00:29 +02:00
Niklas von Hertzen
fcbcb9bfaa Add color object to accept array of rgb(a) 2014-12-13 18:30:52 +02:00
Niklas von Hertzen
3b0352a3d7 Use Color objects for colors 2014-12-13 18:10:41 +02:00
Niklas von Hertzen
313c227a1f Add color parsing 2014-12-13 17:24:54 +02:00
Niklas von Hertzen
893ce74a33 Implement checkbox and radio input element rendering 2014-12-06 18:17:04 +02:00
Niklas von Hertzen
e4329c4d37 Fix phantomjs scrolling test failure 2014-12-06 16:19:38 +02:00
Niklas von Hertzen
069140974b Don't scroll owner document after cloned document load 2014-11-30 14:23:52 +02:00
Niklas von Hertzen
1e826e32ae Fix scroll to top with pages using hashtag anchors 2014-11-30 14:16:02 +02:00
Niklas von Hertzen
0579804cbb Merge pull request #478 from ClickWithMeNow/master
add classname to html2canvas iframe
2014-11-21 00:31:01 +02:00
Dan Alstadt
f15666e156 adding submodules 2014-11-20 14:31:21 -06:00
Dan Alstadt
d4cb7e8868 add html2canvas-container classname to iframe 2014-11-20 14:27:34 -06:00
Niklas von Hertzen
657eb983cf Add options.background option 2014-11-04 21:53:26 +02:00
Niklas von Hertzen
fc800bff9d Partial fix for borders 2014-11-04 21:40:09 +02:00
Niklas von Hertzen
36648afc3d Add test case for border-style: inset 2014-11-03 23:49:39 +02:00
Niklas von Hertzen
aa3aafbc0c Fix race condition for content load 2014-11-03 23:29:57 +02:00
Niklas von Hertzen
525b5c4f36 Fix font rendering for IE with multiple inherited fonts 2014-11-02 22:15:48 +02:00
Niklas von Hertzen
706b2abb19 Update readme with information regarding submodules 2014-11-02 20:58:01 +02:00
Niklas von Hertzen
6b1d116a5e Add build warning regarding missing source files 2014-11-02 20:57:48 +02:00
Niklas von Hertzen
772767d0d9 Disable transitions for cloned page 2014-11-02 20:26:25 +02:00
Niklas von Hertzen
7ebd191488 Fix form placeholder/value positioning for scrolled pages 2014-11-02 19:18:39 +02:00
Niklas von Hertzen
6ece2a3d5a Fix svg matching performance issue 2014-11-02 18:54:39 +02:00
Niklas von Hertzen
1793b802b1 Add onclone callback option to allow modifying cloned document prior to rendering 2014-10-26 19:10:09 +02:00
Niklas von Hertzen
8184b7cf51 Refactor test 2014-10-19 22:38:08 +03:00
Niklas von Hertzen
04bdb48cba Fix layer ordering with multiple stacks on same auto z-index 2014-10-19 22:29:34 +03:00
Niklas von Hertzen
7e13231807 Fix canvas cropping with type: 'view' 2014-10-15 20:28:26 +03:00
Niklas von Hertzen
199685ebd1 Disable animations on rendered page 2014-10-12 20:44:39 +03:00
Niklas von Hertzen
444869e3ca Implement css clipping for rect() (with pixel values) 2014-10-12 20:32:47 +03:00
Niklas von Hertzen
717f69d99a Don't remove frame container in tests 2014-10-12 20:32:10 +03:00
Niklas von Hertzen
541f6a62d8 Expand clip test case 2014-10-12 20:32:00 +03:00
Niklas von Hertzen
d8dcbdedd8 Add test case for css clip 2014-10-07 20:11:46 +03:00
Niklas von Hertzen
5712b621ca Correctly clip content that has border-radius (#399) 2014-10-07 19:11:24 +03:00
Niklas von Hertzen
6073928978 Fix origin check for IE (#460) 2014-10-07 18:59:26 +03:00
Niklas von Hertzen
e103ad8219 Fix pseudoelement rendering (with nth-child selectors etc.) 2014-10-06 22:46:43 +03:00
Niklas von Hertzen
d5430070a2 Ignore exports and module assignments within scope 2014-09-29 19:33:01 +03:00
Niklas von Hertzen
f3d45e005e Don't leak punycode to global scope 2014-09-29 19:15:50 +03:00
Niklas von Hertzen
b60b4b2a45 Add option to use existing canvas for rendering 2014-09-28 22:51:11 +03:00
Niklas von Hertzen
bd1abe1857 Provide fallbacks for html parsing 2014-09-28 00:11:54 +03:00
Niklas von Hertzen
8a3d1d7f22 Fix firefox cross-origin iframe rendering 2014-09-27 23:03:57 +03:00
Niklas von Hertzen
e6d31ada4a Don't load javascript on cloned content by default 2014-09-27 21:20:08 +03:00
Niklas von Hertzen
19777c6623 Add simpler api for rendering external urls 2014-09-27 21:07:25 +03:00
Niklas von Hertzen
33d84c82b0 Add simpler api for rendering external urls 2014-09-27 20:37:12 +03:00
Niklas von Hertzen
1e19832171 Add google maps test 2014-09-27 20:20:08 +03:00
Niklas von Hertzen
fa659ad1df Fix background-size with background-repeat x and y as well (#447) 2014-09-27 18:03:18 +03:00
Niklas von Hertzen
f517a35781 Clone and render canvas content correctly 2014-09-27 18:00:14 +03:00
Niklas von Hertzen
3f3424e49c Fix: Render background-size correctly with no-repeat #447 2014-09-27 17:02:46 +03:00
Niklas von Hertzen
1d8a316f13 Correctly handle overflow content 2014-09-27 16:54:53 +03:00
Niklas von Hertzen
b1f948bb60 Fix z-index stacking with fixed position 2014-09-21 11:44:11 +03:00
Niklas von Hertzen
24d9a22556 Correctly apply canvas background color 2014-09-21 11:24:20 +03:00
Niklas von Hertzen
7ee2f411b0 Continue render with failed svg images 2014-09-20 21:54:03 +03:00
Niklas von Hertzen
9406f76a18 Fix test retry logic 2014-09-20 21:09:18 +03:00
Niklas von Hertzen
6092629679 Add proxy server to travis task 2014-09-20 20:54:43 +03:00
Niklas von Hertzen
349d0a300c Fix tests to use local proxy 2014-09-20 20:25:48 +03:00
Niklas von Hertzen
0a7df6d9b9 Correctly use native rendering with inline <svg> nodes if possible 2014-09-20 20:21:25 +03:00
Niklas von Hertzen
70241a789d Fallback to jsonp proxy if browser doesn't support cors xhr/image 2014-09-20 19:53:22 +03:00
Niklas von Hertzen
075c836d16 Fix missing jsonp feature with grunt-contrib-connect 2014-09-20 18:25:28 +03:00
Niklas von Hertzen
c9993a7237 Revert back to grunt-contrib-connect 2014-09-20 18:18:39 +03:00
Niklas von Hertzen
3b8d4dece2 Use correct window size for cloned iframe 2014-09-20 18:01:57 +03:00
Niklas von Hertzen
440120b087 Merge branch 'ddrscott-fix-doc-measurements' 2014-09-20 17:40:27 +03:00
Niklas von Hertzen
e80fe312ee Merge branch 'fix-doc-measurements' of https://github.com/ddrscott/html2canvas into ddrscott-fix-doc-measurements
Conflicts:
	dist/html2canvas.js
	dist/html2canvas.min.js
	src/core.js
2014-09-20 17:40:09 +03:00
Niklas von Hertzen
b141c9f0d1 Begin implementing cross-origin iframes 2014-09-20 17:36:15 +03:00
Niklas von Hertzen
3a3a61e316 Fix tests 2014-09-18 22:25:37 +03:00
Niklas von Hertzen
6c08c3fa04 Simplify webdriver tests 2014-09-17 00:11:47 +03:00
Niklas von Hertzen
d4c9a41873 Add preliminary support for same-origin iframes 2014-09-16 20:12:13 +03:00
Niklas von Hertzen
6347e7f043 Use correct document context for canvas render 2014-09-14 20:14:38 +03:00
Niklas von Hertzen
9220eb6def Update examples to v0.5 api 2014-09-14 20:04:37 +03:00
Niklas von Hertzen
af965cc3a6 Add fontawesome test 2014-09-14 19:45:58 +03:00
Niklas von Hertzen
9d088fa431 Use punycode to parse unicode characters correctly 2014-09-14 19:32:26 +03:00
Niklas von Hertzen
b8d3688c29 upd 2014-09-14 18:55:14 +03:00
Niklas von Hertzen
9907149513 Don't parse/render input hidden nodes 2014-09-14 17:44:55 +03:00
Niklas von Hertzen
645fcd60b3 Ensure webdriver session is closed before exiting 2014-09-09 19:54:13 +03:00
Niklas von Hertzen
0325a9b836 Add atob polyfill for ie9 for SVG base64 rendering 2014-09-09 18:42:00 +03:00
Niklas von Hertzen
36052c2765 Add support for inline, base64 and node svg's 2014-09-08 23:44:10 +03:00
Niklas von Hertzen
281efd2d5e Use correct build with ci and default grunt tasks 2014-09-08 23:43:00 +03:00
Niklas von Hertzen
44b958beaf Default to element dimension if image size cannot be determined (vector images) 2014-09-08 21:25:21 +03:00
Niklas von Hertzen
2a020e5a21 Allow tainting images to be drawn if option enabled 2014-09-08 21:24:26 +03:00
Niklas von Hertzen
c20e679f2c Prefer native svg rendering if available 2014-09-08 21:16:30 +03:00
Scott Pierce
52c669fe5b Merge branch 'master' of https://github.com/niklasvh/html2canvas
Conflicts:
	build/html2canvas.min.js
	dist/html2canvas.js
	src/core.js
2014-09-04 16:24:23 -05:00
Scott Pierce
382c16a522 use cloned document for measurements 2014-09-04 14:02:44 -05:00
Niklas von Hertzen
ba9d5201cf Add svg rendering with fabric.js 2014-09-04 20:50:31 +03:00
Niklas von Hertzen
2e2d722e3d Fix rendering for safari 6 2014-09-04 19:29:42 +03:00
Niklas von Hertzen
b9edf0b1c5 Add indentation rule to jshintrc 2014-09-04 18:47:17 +03:00
Niklas von Hertzen
07f793b0ed Preliminary support for svg rendering 2014-09-04 18:47:06 +03:00
Niklas von Hertzen
180b624cb3 reformat testing 2014-09-04 18:46:17 +03:00
Niklas von Hertzen
b2280bc8ec rename build folder to dist 2014-09-01 20:22:56 +03:00
Niklas von Hertzen
f3f92ab425 Fix travis segfault 2014-08-27 21:35:48 +03:00
Niklas von Hertzen
503add6e2f Refactor webdriver test running 2014-08-26 21:03:54 +03:00
Niklas von Hertzen
d2bfb810d4 Update webdriver configs to GruntFile 2014-08-26 20:01:53 +03:00
Niklas von Hertzen
649cdd4e37 Update readme regarding 0.5 2014-08-25 18:23:18 +03:00
Niklas von Hertzen
73a34493ac Queue webdriver tests 2014-05-18 23:31:49 +03:00
Niklas von Hertzen
ce1c4c84f5 Fix CSS gradients fail to render when non-vendor prefix property is included #388 2014-05-18 23:20:45 +03:00
Niklas von Hertzen
44beaf2989 Update webdriver browser versions to test 2014-05-18 21:37:56 +03:00
Niklas von Hertzen
d716210509 Update travis webdriver settings 2014-05-18 21:17:59 +03:00
Niklas von Hertzen
be5d1f8665 Don't use proxy for cors test 2014-05-18 21:17:46 +03:00
Niklas von Hertzen
340b125b19 Replace saucelabs travis script with sauce_connect addon 2014-05-18 20:31:53 +03:00
Niklas von Hertzen
ad1f0d418c Add travis env parameters to webdriver tests 2014-05-18 20:25:51 +03:00
Niklas von Hertzen
08373f0bd4 Update travis webdriver host details 2014-05-18 19:28:50 +03:00
Niklas von Hertzen
6959058560 Fix linear gradient rendering 2014-05-18 19:14:22 +03:00
Niklas von Hertzen
d6ed6c0194 Fallback to DummyImageContainer if no suitable ImageContainer found 2014-05-18 17:48:32 +03:00
Niklas von Hertzen
9ee87339a3 Fix cors loading of images 2014-05-18 17:40:01 +03:00
Niklas von Hertzen
b7595e19e9 Fix webdriver tests 2014-05-18 17:39:24 +03:00
Niklas von Hertzen
281e6bbedf Use backgroundColor from documentElement if rendered element lacks backgroundColor 2014-04-06 21:43:20 +03:00
Niklas von Hertzen
fee91055b2 Fix bug with iframe not being loaded in-time 2014-03-24 19:05:32 +02:00
Niklas von Hertzen
650ead63e5 Fix transform nesting 2014-03-15 14:30:26 +02:00
Niklas von Hertzen
b35fcaeaf9 Move non-canvas supported fallback to seperate file 2014-03-15 13:22:55 +02:00
Niklas von Hertzen
81c22866bc Don't fail on broken images 2014-03-15 13:20:05 +02:00
Niklas von Hertzen
25d892f525 Fix IE9 https origin check bug 2014-03-15 13:01:04 +02:00
Niklas von Hertzen
9db1ecfdfc Fix Qunit tests 2014-03-10 19:56:23 +02:00
Niklas von Hertzen
12d85e3c04 Fail with a Promise.reject when no canvas support 2014-03-10 18:46:25 +02:00
Niklas von Hertzen
3101f2007a Fix Firefox DOM clone not being ready 2014-03-05 22:24:53 +02:00
Niklas von Hertzen
85b77ca49f Fix cropping bug 2014-03-05 19:19:24 +02:00
Niklas von Hertzen
bb8c5a973b Limit crop to the maximum size of outputted canvas 2014-03-04 21:54:24 +02:00
Niklas von Hertzen
0187fcab42 Fix internal method name for spec 2014-03-04 20:42:57 +02:00
Niklas von Hertzen
cfe4137bcc Account for negative bounds in cropping 2014-03-04 20:42:34 +02:00
Niklas von Hertzen
d2c3378c3e Correctly crop content 2014-03-03 21:19:28 +02:00
Niklas von Hertzen
95f4bcea0a Render input elements correctly 2014-03-02 19:51:46 +02:00
Niklas von Hertzen
9bae5b610a Fix IE misalignment of content after scroll 2014-03-02 18:03:30 +02:00
Niklas von Hertzen
7ce46e95cd Fix logging in IE9 2014-03-02 16:55:40 +02:00
Niklas von Hertzen
84c1dc6283 Remove script tags from cloned DOM 2014-03-02 16:03:22 +02:00
Niklas von Hertzen
15ca3381eb Fix text rendering for IE and Opera 2014-03-02 16:00:59 +02:00
Niklas von Hertzen
18d95d669b Partial implementation for transforms 2014-02-23 17:35:46 +02:00
Niklas von Hertzen
5137e5f35a Render correct element 2014-02-23 17:02:49 +02:00
Niklas von Hertzen
314d26f1f1 Hide iframe container from view during render 2014-02-23 16:26:23 +02:00
Niklas von Hertzen
82e5a8a7c0 Fix image loading for cross-origin resources 2014-02-17 00:04:09 +02:00
Niklas von Hertzen
9af96d3812 Fix render ordering of nodes that form fake stacking contexts 2014-02-15 00:33:09 +02:00
Niklas von Hertzen
6f2a775841 Fix background position rendering 2014-02-15 00:31:48 +02:00
Niklas von Hertzen
f2b662801e Add another z-index test 2014-02-15 00:30:38 +02:00
Niklas von Hertzen
60587c72bf Fix unit tests 2014-02-15 00:30:28 +02:00
Niklas von Hertzen
d9d516d27e Force doctype for iframe 2014-02-10 19:53:13 +02:00
Niklas von Hertzen
899d5321d4 Fix pseudoelement rendering for Firefox 2014-02-08 17:42:40 +02:00
Niklas von Hertzen
5d20493f46 Add support for pseudoelements 2014-02-08 16:52:41 +02:00
Niklas von Hertzen
b5891c49b4 Correctly assign image loading method for <img> elements 2014-02-08 14:07:20 +02:00
Niklas von Hertzen
467ff87482 Switch to using Promises 2014-02-03 19:42:42 +02:00
Niklas von Hertzen
9beae48cf0 Start implementing background gradients 2014-02-01 21:48:30 +02:00
Niklas von Hertzen
17731169e9 Add support for single text-shadow 2014-02-01 20:03:23 +02:00
Niklas von Hertzen
e27c41efd3 Add font metrics and text-decorations 2014-02-01 19:36:43 +02:00
Niklas von Hertzen
1f90defbfa Fix text rendering for Opera/IE 2014-02-01 19:11:59 +02:00
Niklas von Hertzen
b4bb34c95b Move NodeParser to seperate file 2014-02-01 18:52:53 +02:00
Niklas von Hertzen
64668fe694 ios window size and scroll fixes 2014-02-01 18:32:05 +02:00
Niklas von Hertzen
9ebae161e2 Fix IE issues 2014-01-26 20:43:50 +02:00
Niklas von Hertzen
729bc88d1f Increase logging 2014-01-26 20:43:36 +02:00
Niklas von Hertzen
b1c2f03ae9 Add option to only render current view 2014-01-26 20:42:57 +02:00
Niklas von Hertzen
74cb3466ec Draw <img> elements 2014-01-26 18:10:04 +02:00
Niklas von Hertzen
2afdcaff35 Add Promise polyfill 2014-01-26 16:06:39 +02:00
Niklas von Hertzen
1070cec852 Implement background rendering 2014-01-26 16:06:16 +02:00
Niklas von Hertzen
ba9a33b1bc Add work around for chrome related background-image issues in iframe 2014-01-26 15:50:13 +02:00
Niklas von Hertzen
f474542382 Begin implementing background-image rendering 2014-01-21 21:41:00 +02:00
Niklas von Hertzen
0cb259f6cd Cache backgroundImages request for nodes 2014-01-20 22:42:58 +02:00
Niklas von Hertzen
1a7f5732bf Filter nodes and render correct size correctly 2014-01-20 22:42:39 +02:00
Niklas von Hertzen
2b8389cb64 Make image loading to work on top of Promises/polyfill 2014-01-19 21:05:07 +02:00
Niklas von Hertzen
8b8c080841 0.5.0 rewrite 2014-01-19 18:04:27 +02:00
Niklas von Hertzen
6201e09118 Merge branch 'patch-1' of github.com:bensho/html2canvas into bensho-patch-1 2013-12-23 16:33:58 +02:00
Niklas von Hertzen
517fd8cd1d Refactor background parsing 2013-12-23 16:07:49 +02:00
Niklas von Hertzen
e228fc57ce Merge branch 'patch-2' of github.com:brcontainer/html2canvas into brcontainer-patch-2 2013-12-23 15:32:18 +02:00
Niklas von Hertzen
46cc8b6975 Fix z-index ordering bug 2013-12-23 15:27:46 +02:00
Niklas von Hertzen
443fd17a12 Add all js files to grunt watch 2013-12-17 20:01:03 +02:00
Guilherme Nascimento
0b213eecef Fix bug in WebKitGTK+, backgroundRepeat return "wrong" values 2013-12-06 09:49:37 -02:00
Ben Sholds
ae1a15f7c5 Update pseudoelements.html 2013-11-25 16:39:35 -05:00
Ben Sholds
cea3005056 Fix invalid selector exception with empty class values
After removing :before and :after pseudo selectors, a class name may be empty, causing an invalid selector string when joined. Remove empty elements before calling querySelectorAll.
2013-11-25 09:48:12 -05:00
Niklas von Hertzen
1d4b1753d6 Merge pull request #297 from Grom-S/patch-1
incorect z-index sorting fixed
2013-11-24 03:32:57 -08:00
Grom-S
f00b23a9ec incorect z-index sorting fixed
If you omit compare function in javascript `sort()` method it will sort  lexicographically (in dictionary order) according to the string conversion of each element. 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters

For example 
[0, 9, 8, 10].sort()
will place element with z-index 10 on 2nd position of the array
2013-11-21 20:37:40 +02:00
Niklas von Hertzen
e9afe03960 Implement background-size cover/contain 2013-11-12 19:35:28 +02:00
Niklas von Hertzen
57d20a9794 Fix missing background color bug 2013-11-12 19:34:20 +02:00
Niklas von Hertzen
35c5ca3340 Add tests for background-size 2013-11-12 19:33:56 +02:00
Niklas von Hertzen
7cc7f80ee2 Add dom depth information to render queue 2013-10-27 22:08:12 +02:00
Niklas von Hertzen
4c75d819a4 Merge pull request #270 from ssafejava/parseSpeed
Speed parsing by as much as 50% & add async parsing option.
2013-09-22 11:32:25 -07:00
Niklas von Hertzen
838f91e156 Merge pull request #269 from ssafejava/jshint
Move jshint rules into jshintrc and include from Gruntfile.
2013-09-17 23:29:37 -07:00
ssafejava
8bea01b81d Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.

This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.

After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.

Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 13:38:21 +08:00
ssafejava
e115180731 Add async parsing option.
In my testing, the major time sink is parsing. This commit adds a setTimeout() around parsing
of each item so control can return to the browser. This increases the total time it takes to finish
a screenshot but will not freeze the browser when it does. This is a good option when e.g. doing
error reporting, where you might not want to freeze the browser while sending debugging information
back to your server.
2013-09-18 13:38:21 +08:00
ssafejava
e782efa614 Move jshint rules into jshintrc and include from Gruntfile. 2013-09-18 13:37:06 +08:00
Niklas von Hertzen
806cd60474 Add links to readme 2013-09-07 22:07:27 +03:00
Niklas von Hertzen
0515765788 Release version 0.4.1 2013-09-07 21:29:31 +03:00
Niklas von Hertzen
0fd25f048d Add bower.json 2013-09-07 21:24:41 +03:00
Niklas von Hertzen
14ff672c6e Disable screenshot storing 2013-09-07 21:10:06 +03:00
Niklas von Hertzen
38fad5ac17 Update travis key 2013-09-07 00:10:53 +03:00
Niklas von Hertzen
a31de83368 update token 2013-09-07 00:05:47 +03:00
Niklas von Hertzen
1fb3b53fc0 update certificate 2013-09-06 23:57:47 +03:00
Niklas von Hertzen
4d465116da update travis.yml key decrypt argument 2013-09-06 23:48:18 +03:00
Niklas von Hertzen
74ce2c5062 update travis.yml secret 2013-09-06 23:26:35 +03:00
Niklas von Hertzen
fbeb6e72f2 Update test certificate 2013-09-06 23:11:04 +03:00
Niklas von Hertzen
c097f11ce3 Store webdriver screenshots to google drive 2013-09-06 22:30:24 +03:00
Niklas von Hertzen
b6ebf2acf6 Remove unnecessary custom complete event checker 2013-09-04 20:17:38 +03:00
Niklas von Hertzen
e9c3d9d332 Fix background color rendering layering with no documentElement color 2013-09-04 20:03:16 +03:00
Niklas von Hertzen
c232da2595 Fix grunt watch build order 2013-09-04 20:02:20 +03:00
Niklas von Hertzen
c759600c06 Refactoring 2013-09-04 19:29:12 +03:00
Niklas von Hertzen
5f45968154 Merge pull request #260 from arrix/develop
negative z-index
2013-08-15 12:15:04 -07:00
arrix
feb2fd0a63 test case for body bgcolor and negative z-index #256 2013-08-16 00:26:40 +08:00
arrix
fb944d9381 test case for negative z-index below text 2013-08-15 23:31:48 +08:00
arrix
564634ba97 stacking context creator should be at the bottom
passes z-index12. issue #256
2013-08-15 23:24:01 +08:00
arrix
dd7468c446 use solid border in z-index tests 2013-08-15 22:31:32 +08:00
Niklas von Hertzen
eb00650b02 fix text 2013-08-14 21:56:43 +03:00
Niklas von Hertzen
1d03a5f9a4 z-index test case for negative z-indexes 2013-08-14 21:56:32 +03:00
arrix
ea7d6b485d preserve stacking nesting with special ancestor. issue #256 2013-08-15 02:12:38 +08:00
Niklas von Hertzen
fd4fd95429 Test case for static position inside position relative 2013-08-13 19:59:54 +03:00
Niklas von Hertzen
10b40821e5 first implementation for matrix transforms 2013-08-06 21:11:08 +03:00
Niklas von Hertzen
518dd702a2 list all tests in index.html 2013-08-06 21:10:46 +03:00
Niklas von Hertzen
056953f2c1 Fix cropping of canvas for float sized elements 2013-08-06 19:15:46 +03:00
Niklas von Hertzen
9a57a08c72 Refactoring logging and gradients 2013-08-06 18:55:04 +03:00
Niklas von Hertzen
26a81da2f0 Ignore transforms if non found 2013-08-06 18:20:20 +03:00
Niklas von Hertzen
57028ab423 initial commit for transforms 2013-08-06 18:17:33 +03:00
Niklas von Hertzen
c9e2fc27c8 Refactoring 2013-08-06 17:55:13 +03:00
Niklas von Hertzen
2777a3e079 Refactoring 2013-08-06 17:46:47 +03:00
arrix
02ab96dc5f passes z-index8 non-positioned element with opactiy < 1
The MDN article Understanding_z_index/Stacking_and_float is wrong about this
2013-08-06 10:34:13 +08:00
arrix
65746bd2e3 coding optimizations 2013-08-06 10:01:20 +08:00
arrix
16d3bef255 z-index overhaul. relative above static; negative z-index
1. when stacking without z-index, positioned > floated > normal flow
2. supports negative z-index
3. new stacking context formed when opacity < 1 (standard)
4. new stacking context formed for position:fixed (no standard yet, done in mobile webkit and chrome 22+)
2013-08-06 03:36:12 +08:00
Niklas von Hertzen
0277c34310 first transform test 2013-08-04 19:21:00 +03:00
Niklas von Hertzen
f35ef0fe6f Refactor 2013-08-04 18:33:18 +03:00
Niklas von Hertzen
2c8dd18d55 Fix build order 2013-08-04 18:33:02 +03:00
Niklas von Hertzen
6b5f31eef0 Add tests for text shadow 2013-08-04 17:00:41 +03:00
Niklas von Hertzen
832b9ee934 add grunt watch 2013-08-04 16:42:12 +03:00
Niklas von Hertzen
73698e8ceb Update readme 2013-08-04 16:41:41 +03:00
Niklas von Hertzen
37fbd3f90e Fix border rendering bug 2013-08-04 16:41:36 +03:00
Niklas von Hertzen
5300f20b78 use element instead of element-array in examples 2013-08-04 16:03:47 +03:00
Niklas von Hertzen
f0e234a1d8 Remove jQuery from examples 2013-08-04 15:58:42 +03:00
Niklas von Hertzen
30163ab16f Update readme 2013-08-04 15:51:16 +03:00
Niklas von Hertzen
407145da94 Update package.json 2013-08-04 15:50:54 +03:00
Niklas von Hertzen
c5e6eaa849 Change iframe test url 2013-08-04 15:41:46 +03:00
Niklas von Hertzen
2d39cd0719 fix shadow parsing for IE9 2013-08-04 15:41:00 +03:00
Niklas von Hertzen
ebd7828dc8 Update sauce-connect logging 2013-08-04 15:14:34 +03:00
Niklas von Hertzen
877367d499 Merge branch 'arrix-child_textnodes' into develop 2013-08-04 14:36:55 +03:00
arrix
fd888bde8d fixes #251. inline text in top element 2013-08-02 14:36:42 +08:00
arrix
7d2e12c3dd added test case for #251 inline text in top element 2013-08-02 10:11:07 +08:00
Niklas von Hertzen
a7d3e9c2a2 Merge branch 'master' of git://github.com/fdn/html2canvas into fdn-master 2013-06-23 19:27:53 +03:00
Guerric Sloan
f49e147b2f Added qunit tests for text-shadow 2013-06-18 23:47:08 -07:00
Niklas von Hertzen
a902f92a14 remove svg rendering 2013-06-15 11:53:28 +03:00
Guerric Sloan
e1573f8aed Parse out multiple text-shadow values and only honor the first one. 2013-06-12 16:48:23 -07:00
Guerric Sloan
655779743b Better text-shadow parsing 2013-06-12 15:48:00 -07:00
Guerric Sloan
1a30167f6a Basic implementation of text-shadow 2013-06-12 14:54:46 -07:00
Niklas von Hertzen
2c58c56fbe add iframe test 2013-05-31 18:07:57 +03:00
Niklas von Hertzen
288b851d05 revert image smooth disabling 2013-05-29 22:50:01 +03:00
Niklas von Hertzen
0afb0fae0e disable image smoothing 2013-05-29 22:31:00 +03:00
Niklas von Hertzen
a4702423cc adjust logging for results 2013-05-29 22:28:42 +03:00
Niklas von Hertzen
f4aef61e5a update readme 2013-05-29 22:28:32 +03:00
Niklas von Hertzen
cb210b2e61 delete .DS_Store 2013-05-29 22:14:50 +03:00
Niklas von Hertzen
a80ef26c42 http->https 2013-05-29 22:13:22 +03:00
Niklas von Hertzen
2580b48d16 compare results from db 2013-05-29 22:01:07 +03:00
Niklas von Hertzen
87d7894d71 fix test result report posting 2013-05-29 21:16:28 +03:00
Niklas von Hertzen
7842768707 encrypt api key only 2013-05-29 20:52:27 +03:00
Niklas von Hertzen
403908c7da add logging for test reporting 2013-05-29 20:32:58 +03:00
Niklas von Hertzen
8c8128b80a add test result storing 2013-05-29 20:12:37 +03:00
Niklas von Hertzen
0d4b6ba665 setup travis config 2013-05-29 18:41:15 +03:00
Niklas von Hertzen
b91fd9bc87 use sync-webdriver for selenium tests 2013-05-29 18:01:28 +03:00
Niklas von Hertzen
62d27c20c3 fix typo 2013-05-21 23:23:56 +03:00
Niklas von Hertzen
e811effe2a update download link 2013-05-21 23:23:31 +03:00
Niklas von Hertzen
6156e28721 Merge pull request #214 from felfert/master
Fixed #207
2013-05-21 12:32:26 -07:00
Fritz Elfert
2b000f0061 Fixed #207 2013-05-19 22:34:57 +02:00
Niklas von Hertzen
843db27f72 Merge pull request #190 from cobexer/zIndex-1
added test for zIndex -1 element used as background
2013-04-15 07:44:47 -07:00
Obexer Christoph
bbdfe8a035 added test for zIndex -1 element used as background 2013-04-15 13:08:54 +02:00
Niklas von Hertzen
9da3bd7769 Merge pull request #189 from felfert/master
Fixed parsing of generic INPUT elements on IE9
2013-04-09 12:24:32 -07:00
Fritz Elfert
85fa81ad95 - Fix parsing of input fields on IE9 2013-04-07 21:56:49 +02:00
Niklas von Hertzen
a5d74bcfd9 Merge pull request #188 from felfert/master
Fetch images of current element only
2013-04-07 10:52:26 -07:00
Fritz Elfert
cf735a9fa1 - Fixed rendering of ExtJS 4.2 windows. 2013-04-06 00:30:16 +02:00
Fritz Elfert
9b051b8749 - Fetch images of current element only 2013-04-05 18:06:37 +02:00
Niklas von Hertzen
822311ed0c Merge branch 'develop' of github.com:niklasvh/html2canvas into develop 2013-04-02 18:34:49 +03:00
Niklas von Hertzen
c39225ceac add idea files to gitignore 2013-04-02 18:31:18 +03:00
Niklas von Hertzen
763125ce6d Merge pull request #180 from djfarrelly/master
Upgraded to Grunt 0.4.0 and added Safari for Mac bug fix.
2013-03-24 07:52:45 -07:00
Dan Farrelly
5ac4b42e33 Added reference to build shortcut on readme.md 2013-03-23 20:06:31 -04:00
Dan Farrelly
cacb9a468f Modified markdown function. Added grunt build task.
Markdown format output currently commented out.
2013-03-23 20:00:01 -04:00
Dan Farrelly
8623e4014b Upgrade to Grunt 0.4.0 complete. Safari on OSX bug fix. 2013-03-22 14:57:38 -04:00
Dan Farrelly
2d95a761b0 Upgraded to Grunt 0.4.0. 2013-03-21 22:40:09 -04:00
Niklas von Hertzen
b2df50a858 Allow element node to be passed 2013-01-30 20:12:25 +02:00
Niklas von Hertzen
8ddf10fc04 Fix taint testing 2013-01-12 22:28:00 +02:00
Niklas von Hertzen
410537456a Reordered grunt tasks 2013-01-12 19:01:24 +02:00
Niklas von Hertzen
3aa7d69cc7 updated examples 2013-01-12 01:41:09 +02:00
Niklas von Hertzen
7a3dd7572c updated readme 2013-01-12 01:34:00 +02:00
Niklas von Hertzen
1b37c5d1ea Refactored preload 2013-01-11 23:01:29 +02:00
Niklas von Hertzen
67850f2cee Refactoring 2013-01-11 22:50:46 +02:00
Niklas von Hertzen
88dd1e41c0 Moved pseudoelement rendering to parse.js 2013-01-11 22:36:23 +02:00
Niklas von Hertzen
554185ed4a pseudoelement tests 2013-01-11 22:33:40 +02:00
Niklas von Hertzen
222dfa84b7 remove legacy code 2013-01-11 20:38:33 +02:00
Niklas von Hertzen
9b0c32c62c Fix firefox pseudoelement bug for images 2013-01-11 20:36:07 +02:00
Niklas von Hertzen
2bb926c7d0 Fix webkit-gradient() parsing 2013-01-11 20:31:34 +02:00
Niklas von Hertzen
861a18f977 Fix Chrome background-repeat parse error 2013-01-11 19:51:53 +02:00
Niklas von Hertzen
cf15c4a59f Updated readme 2013-01-11 19:39:35 +02:00
Niklas von Hertzen
2f3f27b672 Fix crash for browsers that don't support placeholder attribute 2013-01-11 19:34:56 +02:00
Niklas von Hertzen
3032dc6ce0 Fix empty content getting rendered on firefox/IE 2013-01-11 19:31:05 +02:00
Andy Edinborough
2b0db917e3 don't process pseudo elements for hidden elements; cleanup pseudo elements after render 2013-01-11 10:46:53 -06:00
Andy Edinborough
d73e53fbf0 Merge remote-tracking branch 'niklasvh/develop' into develop 2013-01-11 09:51:59 -06:00
Niklas von Hertzen
6d29cc5df3 chinese word rendering 2013-01-04 23:47:59 +02:00
Andy Edinborough
dc21fab450 missing braces 2013-01-03 17:10:57 -06:00
Andy Edinborough
5492d80135 simply resize images to background-size 2013-01-03 17:09:23 -06:00
Niklas von Hertzen
a313524aa4 refactored renderer 2013-01-03 23:15:06 +02:00
Andy Edinborough
3edf9fa743 Merge remote-tracking branch 'niklasvh/develop' into develop
Conflicts:
	src/Parse.js
	src/Preload.js
2013-01-03 15:07:38 -06:00
Niklas von Hertzen
7d7deca342 updated results 2013-01-03 22:30:52 +02:00
Niklas von Hertzen
053a0a4787 switched background rendering to use patterns 2013-01-03 22:25:35 +02:00
Niklas von Hertzen
65b4bdf282 background clipping support 2013-01-03 20:34:47 +02:00
Niklas von Hertzen
56780565f4 refactored border radius calculation 2013-01-03 18:37:27 +02:00
Andy Edinborough
55ed0ffde0 render placeholder text 2013-01-02 17:03:16 -06:00
Andy Edinborough
7da4326885 background-size fixes
generated gradients need a unique key (the same value can generate a
different image based on background-size); fix so that a single value
specified for background-size yields a scaled height as the second
parameter
2013-01-02 14:51:03 -06:00
Andy Edinborough
eb57b61859 backgroundPosition should use backgroundSize as a baseline for %'s 2013-01-02 13:45:58 -06:00
Niklas von Hertzen
bb73d3c15e initial border-radius rendering 2013-01-02 21:26:24 +02:00
Andy Edinborough
9b5ae9e191 support for pseudo elements 2013-01-02 12:58:48 -06:00
Andy Edinborough
2557a83dbe remove todo comment 2013-01-02 10:51:47 -06:00
Andy Edinborough
67ccb33dd5 add tests for resizeBounds 2013-01-02 10:47:36 -06:00
Andy Edinborough
85706166cc trimText regexp needs /g 2013-01-02 10:40:56 -06:00
Andy Edinborough
57d6003b65 backgroundSize needs trimmed before second split
also move trimText to Util
2013-01-02 10:39:00 -06:00
Andy Edinborough
9ce03d6e86 working background-size 2013-01-02 10:10:03 -06:00
Andy Edinborough
3d3f923ed8 implementing background-size; use parseBackgroundImage in render 2013-01-01 10:42:22 -06:00
Niklas von Hertzen
42abcfe5fc added border radius test 2012-12-31 20:10:18 +02:00
Andy Edinborough
473ff45267 Merge remote-tracking branch 'niklasvh/develop' into develop
Conflicts:
	src/Core.js
	src/Parse.js
	src/Util.js
2012-12-30 17:25:31 -06:00
Niklas von Hertzen
496c8488bd Moved font metrics to seperate file 2012-12-30 21:26:25 +02:00
Andy Edinborough
df0f436e66 initial support for backgroundSize 2012-12-30 12:30:48 -06:00
Andy Edinborough
6ce619f0c0 refactor Preload 2012-12-30 12:20:35 -06:00
Andy Edinborough
3774f3655c update test names 2012-12-30 11:22:47 -06:00
Andy Edinborough
0c66766d55 replace definition property, w/ args[]
Also fix reference for preloader
2012-12-30 11:21:44 -06:00
Niklas von Hertzen
ba9ace71ba improved text-decoration: overline accuracy 2012-12-30 16:46:31 +02:00
Niklas von Hertzen
b19c200c6c renamed results to readme 2012-12-30 16:30:28 +02:00
Niklas von Hertzen
29bd4c8c05 added result markdown creator 2012-12-30 16:29:01 +02:00
Niklas von Hertzen
0dd2c24ab4 refactored parsing init 2012-12-30 16:06:59 +02:00
Niklas von Hertzen
d93e36d768 Moved browser support checks to seperate file 2012-12-30 15:48:55 +02:00
Niklas von Hertzen
1357057cbf refactored background rendering 2012-12-30 15:38:17 +02:00
Niklas von Hertzen
74e93cbb93 refactoring backgroundimage rendering 2012-12-30 15:21:36 +02:00
Niklas von Hertzen
45853a083c refactored text rendering 2012-12-30 04:15:51 +02:00
Niklas von Hertzen
cb43e09899 refactor parsing 2012-12-30 01:39:37 +02:00
Niklas von Hertzen
bb1cd21367 refactored background color rendering 2012-12-30 01:11:05 +02:00
Niklas von Hertzen
5faa45847e refactored image rendering 2012-12-30 01:06:11 +02:00
Niklas von Hertzen
0c2572b5ce refactoring 2012-12-30 00:27:38 +02:00
Niklas von Hertzen
aa5b3d41c4 refactoring 2012-12-29 23:35:52 +02:00
Niklas von Hertzen
516edbceea Improve background-repeat accuracy for non int positions 2012-12-29 23:05:49 +02:00
Niklas von Hertzen
c72a02bf64 refactoring 2012-12-29 21:06:40 +02:00
Niklas von Hertzen
c3e9636e4f Moved renderer logic to renderer.js 2012-12-29 17:43:15 +02:00
Niklas von Hertzen
816ff6d3c5 updated testing instructions 2012-12-29 16:34:56 +02:00
Niklas von Hertzen
5f1fedf8f0 added npm dependencies 2012-12-29 16:31:57 +02:00
Niklas von Hertzen
a82234873e updated .gitignore 2012-12-29 16:04:50 +02:00
Niklas von Hertzen
d1ee6e9d64 removed baseline files 2012-12-29 16:02:46 +02:00
Niklas von Hertzen
e7b4dd17b9 updated readme 2012-12-29 15:31:24 +02:00
Niklas von Hertzen
05f3af4901 webdriver grunt task 2012-12-29 15:02:05 +02:00
Niklas von Hertzen
630bed968e baseline results 2012-12-29 15:01:48 +02:00
Niklas von Hertzen
7c870a6fb8 add border tests 2012-12-29 14:57:06 +02:00
Niklas von Hertzen
52033a5d72 Fixed jquery path 2012-12-29 14:34:01 +02:00
Niklas von Hertzen
07e80df399 Fixed lint error 2012-12-29 14:33:30 +02:00
Andy Edinborough
7e38df782c don't generate gradient on invisible elements 2012-12-28 16:52:48 -06:00
Andy Edinborough
7f1cbc70a8 fully remove regex; simplify whitespace check
jshint complained about case statements w/o break (boo)
2012-12-28 16:26:39 -06:00
Andy Edinborough
b81d7473e3 rewrite parseBackgroundImage to remove RegExp
RegExp couldn't handle parens inside parens:
linear-gradient(rgb(0,0,0),rgb(255,0,0))
2012-12-28 16:22:43 -06:00
Andy Edinborough
d7bef66cc5 export html2canvas, pass value not def to backgroundImage 2012-12-28 15:49:38 -06:00
Andy Edinborough
04782c1716 parse out prefixed methods 2012-12-28 12:54:58 -06:00
Andy Edinborough
a4b7d04e80 move parseBackgroundImage to Util; add tests 2012-12-28 12:33:57 -06:00
Andy Edinborough
62cb111956 style update 2012-12-28 12:24:49 -06:00
Andy Edinborough
3171390f80 satisfy lint task 2012-12-28 12:12:47 -06:00
Andy Edinborough
8fe61a43b0 initial support for multiple background images
Adding code from http://jsbin.com/ebigux/latest
2012-12-28 11:53:15 -06:00
Niklas von Hertzen
f0ce6917fa updated license formatting 2012-12-28 19:46:23 +02:00
Niklas von Hertzen
118b42eb7e remove .baseline files 2012-12-28 19:42:59 +02:00
Niklas von Hertzen
3bbcfe36e0 Test result outputs 2012-12-28 19:41:14 +02:00
Niklas von Hertzen
cdc7a744e3 Different quote styles 2012-12-28 18:06:44 +02:00
Niklas von Hertzen
fb0e7ca29d removed external files 2012-12-27 23:57:06 +02:00
Niklas von Hertzen
4e978c60cc formatted tests into smaller units 2012-12-27 23:53:27 +02:00
Niklas von Hertzen
c9ed8d91fa updated renderer 2012-12-27 23:07:00 +02:00
Niklas von Hertzen
582d10e00d webdriver testing 2012-12-27 23:06:47 +02:00
Niklas von Hertzen
4684177df8 fix ie 9 bug 2012-12-27 23:06:35 +02:00
Niklas von Hertzen
5198028c7b updated description 2012-12-22 16:29:43 +02:00
Niklas von Hertzen
d6ddb7e29d Added automated testing with selenium 2012-12-22 16:28:34 +02:00
Niklas von Hertzen
9df9426c91 update gitignore 2012-12-22 16:27:28 +02:00
Niklas von Hertzen
76aa1e8feb moved examples 2012-11-25 23:48:08 +02:00
Niklas von Hertzen
2841f19647 List refactoring 2012-11-25 23:36:28 +02:00
Niklas von Hertzen
c6baabc99c Text rendering refactoring 2012-11-25 23:26:18 +02:00
Niklas von Hertzen
44023015b6 Refactoring 2012-11-25 23:05:30 +02:00
Niklas von Hertzen
bca6458301 Clean up 2012-11-25 22:39:09 +02:00
Niklas von Hertzen
7c0b893564 Added support for "data-html2canvas-ignore" attribute 2012-11-25 22:25:08 +02:00
Niklas von Hertzen
084bf4b039 Switched build process to use grunt 2012-11-25 20:59:31 +02:00
Niklas von Hertzen
e83de7ae00 Merge pull request #130 from CyberShadow/opera-fixes
Opera performance fixes
2012-10-22 04:33:37 -07:00
Vladimir Panteleev
1b81f7d517 Accept 0px as an acceptable letter-spacing value for per-word rendering
This improves performance in Opera.
2012-10-20 22:18:46 +03:00
Vladimir Panteleev
3164e5bae0 Don't create zero-width text node after each render
This fixes rendering the same node getting slower each time
when using Opera.
2012-10-20 22:15:58 +03:00
Niklas von Hertzen
81ae37cbd1 syntax error in example 2012-07-10 22:42:16 +03:00
Niklas von Hertzen
730ebcfcaa border-radius parsing 2012-06-26 17:18:34 +03:00
Niklas von Hertzen
cce6e3537c corrected border drawing with multiple colors
initial code for border-radius implemented
2012-06-26 15:15:46 +03:00
Niklas von Hertzen
311a67ee22 added SVG 2012-06-26 01:55:46 +03:00
Niklas von Hertzen
be143935cb added usage example 2012-06-26 01:48:41 +03:00
Niklas von Hertzen
d6cb548a5c updated to version 0.34 2012-06-26 01:35:11 +03:00
Niklas von Hertzen
1ba911912d fixed firefox iframe permission error 2012-06-26 01:30:45 +03:00
Niklas von Hertzen
343b86705f removed last jQuery dependencies
should be fully jQuery free now!
2012-06-26 01:17:03 +03:00
Niklas von Hertzen
9f76f94a82 fix jQuery noconflict 2012-06-13 13:56:01 +03:00
Niklas von Hertzen
b02bb4d452 Merge pull request #100 from BugHerd/linting
Added a sublime-project file for Sublime Text 2 with linting and standards.
2012-05-28 23:38:20 -07:00
Niklas von Hertzen
3bdba0617a Merge pull request #99 from BugHerd/makefile
Added a makefile to build html2canvas
2012-05-28 23:32:17 -07:00
Benjamin Lupton
1059314258 Added a sublime-project file for Sublime Text 2 that contains the detected linting and standards set. Allows Sublime Text 2 to automatically follow the standards and have live linting enabled. Trimmed useless whitespace. 2012-05-29 11:26:26 +10:00
Benjamin Lupton
331c057273 Added a makefile to build html2canvas 2012-05-29 11:18:01 +10:00
Niklas von Hertzen
8d3a0c2b0d cache computed style 2012-05-24 14:10:40 +03:00
Niklas von Hertzen
16022b81c3 Merge pull request #92 from bdkzero/master
Support for other renderers than Canvas and fixed image rendering in SVG renderer
2012-05-22 05:19:23 -07:00
Bdkzero
17f4701ee5 Created new build target 'build-allrends'. Use this to build html2canvas including all renderers 2012-05-21 00:34:21 +02:00
Bdkzero
00c3fb791c Readded /build/ directory to .gitignore and removed /build directory 2012-05-20 23:55:23 +02:00
Bdkzero
98bc1f0833 Prebuilt distribution files 2012-05-08 10:59:14 +02:00
Bdkzero
a5969be6f6 Fixed custom renderer option 2012-04-23 23:04:23 +02:00
Binduck
de1d1d7087 Minor changes 2012-04-17 12:43:51 +02:00
Paolo
d494b8dfbd Added direnv conf file to .gitignore 2012-04-17 11:09:09 +02:00
Paolo
cfc45e4f6e Fixed image rendering in SVG renderer 2012-04-17 10:58:14 +02:00
Paolo
8d965029da Fixed support for other Renderers other than Canvas 2012-04-17 10:57:25 +02:00
Niklas von Hertzen
1ad7ed3e1c Merge pull request #77 from SunboX/background-gradients
ellipse gradient generation should now work
2012-03-14 03:38:05 -07:00
Niklas von Hertzen
d7f4509253 Merge pull request #78 from cobexer/fix-undefined-refs-and-bookmarklet
fixed a few undefined references, fixed missing files in a few places
2012-03-14 03:37:05 -07:00
Obexer Christoph
b47347d6b8 fixed a few undefined references, fixed missing files in a few places 2012-03-12 07:37:18 +01:00
André Fiedler
106b5ff214 "fixed circle gradient generation - image loading bug" 2012-03-11 17:12:24 +00:00
André Fiedler
d1dec8712e "added ellipse background gradient generation" 2012-03-10 15:31:16 +00:00
André Fiedler
fe4d2c5b81 "added TODO" 2012-03-10 14:28:09 +00:00
André Fiedler
6cf3d36624 "added TODO" 2012-03-09 23:27:22 +00:00
André Fiedler
4dc4132818 "removed TODO" 2012-03-09 23:26:23 +00:00
André Fiedler
1ab9941df6 "big performance boost ;o)" 2012-03-09 23:23:17 +00:00
André Fiedler
0fc5f643ba "cleanup" 2012-03-09 23:20:46 +00:00
André Fiedler
c7995061c9 "added TODO" 2012-03-09 23:11:01 +00:00
André Fiedler
12cf519e37 "some cleanup" 2012-03-09 23:09:16 +00:00
André Fiedler
40bce5e84c "added -o-radial-gradient support" 2012-03-09 23:05:42 +00:00
André Fiedler
0556892e12 "added -moz-radial-gradient support" 2012-03-09 22:58:32 +00:00
André Fiedler
6c29664e35 "added tests for radial gradients" 2012-03-09 22:37:45 +00:00
André Fiedler
155ad45292 "added -webkit-radial-gradient support" 2012-03-09 22:20:34 +00:00
André Fiedler
66ad7190c0 "ups, missed one radial gradient" 2012-03-09 20:00:56 +00:00
André Fiedler
4f22c18043 "added some radial gradient examples to background.html" 2012-03-09 19:58:04 +00:00
André Fiedler
d83b06458c "removed IE9 svg gradients from background.html" 2012-03-09 19:55:41 +00:00
Niklas von Hertzen
0d35571bbf Merge branch 'color-stops' of https://github.com/SunboX/html2canvas.git 2012-03-08 11:35:12 +02:00
André Fiedler
211467fcc1 "better unit test for gradient parsing, check inverted width/height" 2012-03-07 18:24:43 +00:00
André Fiedler
6390c1c7ac "center gradient BugFix" 2012-03-07 18:20:09 +00:00
André Fiedler
a0b498fbf5 "added TODO" 2012-03-06 21:50:09 +00:00
André Fiedler
cde96bb17e "added TODO´s" 2012-03-06 21:45:38 +00:00
André Fiedler
57bff5292d "added contributors" 2012-03-06 21:41:22 +00:00
André Fiedler
82446ee3c3 "cleanup" 2012-03-06 21:36:29 +00:00
André Fiedler
ffd998b015 "-o-linear-gradient parsing" 2012-03-06 21:26:43 +00:00
André Fiedler
51b2c01b0c "-moz-linear-gradient parsing" 2012-03-06 21:11:01 +00:00
Niklas von Hertzen
c08ac5d0c4 add svg powered rendering 2012-03-06 17:11:10 +02:00
Niklas von Hertzen
59306c839b Merge branch 'master' of https://github.com/niklasvh/html2canvas.git 2012-03-06 14:37:28 +02:00
Niklas von Hertzen
5fb8cb3e0b reverting back to jQuery.contents() 2012-03-06 14:35:33 +02:00
Niklas von Hertzen
67d3e0d0f5 first commit for SVG powered rendering 2012-03-06 14:32:45 +02:00
André Fiedler
f387267c0f "TODO: implement radial gradient generation" 2012-03-05 23:22:01 +00:00
André Fiedler
b65a850997 "cleanup test" 2012-03-05 23:15:58 +00:00
André Fiedler
163219b656 "added -webkit-gradient parsing + tests" 2012-03-05 23:03:36 +00:00
André Fiedler
a4f13de455 "renamed getColorStopsFromGradient to parseGradient" 2012-03-05 22:21:28 +00:00
André Fiedler
75ba867988 "added tests for getColorStopsFromGradient" 2012-03-05 21:04:25 +00:00
André Fiedler
ae8d499942 "small bugfix" 2012-03-05 19:37:54 +00:00
André Fiedler
e479c952f7 "added another background gradient to backgrounds.html" 2012-03-05 19:33:52 +00:00
André Fiedler
6637ba1bd7 "-webkit-linear-gradient color stops and rendering" 2012-03-05 19:20:44 +00:00
Niklas von Hertzen
883d8bb75b Merge pull request #71 from cobexer/some-cleanup
Some cleanup
2012-03-05 02:53:25 -08:00
Obexer Christoph
cfde57cb9f some whitespace/warning cleanup 2012-03-05 07:56:45 +01:00
Obexer Christoph
0674543ab1 cleanup event handers of image objects after use 2012-03-05 07:54:42 +01:00
André Fiedler
954631d045 "BugFixes" 2012-03-04 20:18:03 +00:00
André Fiedler
9fc366b3f7 "tests: moved Generate into own module; fixed QUnit messages" 2012-03-04 20:15:10 +00:00
Niklas von Hertzen
8a5b09be70 gradient check fix 2012-03-04 21:20:22 +02:00
Niklas von Hertzen
7a3ca77471 few bug fixes to getCSS and unit tests 2012-03-04 21:16:18 +02:00
André Fiedler
e82d703288 "tests: splitted Generate from CSS; if not supported pass test" 2012-03-04 19:06:25 +00:00
Niklas von Hertzen
c018166563 exclude children 2012-03-04 20:17:01 +02:00
Niklas von Hertzen
89d749e30a Merge branch 'master' of https://github.com/niklasvh/html2canvas.git 2012-03-04 20:15:51 +02:00
Niklas von Hertzen
c056acee43 children test 2012-03-04 20:15:35 +02:00
Niklas von Hertzen
c6ec65616c Merge pull request #70 from SunboX/tests-background-gradient
Tests background gradient
2012-03-04 10:14:03 -08:00
André Fiedler
f47f9025b7 "tests: small bugfix" 2012-03-04 17:30:01 +00:00
André Fiedler
d78687a3dc "fixed typo" 2012-03-04 17:00:58 +00:00
André Fiedler
2a0dff32b2 "test: backgroundGradient" 2012-03-04 16:59:00 +00:00
André Fiedler
00d73c0bf8 "test: backgroundGradients markup" 2012-03-04 15:16:07 +00:00
André Fiedler
5e57ebc0ce "preventing JSHint messages" 2012-03-04 15:02:03 +00:00
Niklas von Hertzen
3d7a6374ad bug fixes 2012-03-03 21:03:59 +02:00
Niklas von Hertzen
4579fb25c6 removed jQuery.css dependancy and few general CSS bug fixes 2012-03-03 19:18:39 +02:00
Niklas von Hertzen
e84d505f46 padding width needs to be in pixels 2012-03-03 16:56:31 +02:00
Niklas von Hertzen
946bdef0d4 Merge pull request #67 from cobexer/some-fixes
Some fixes
2012-03-03 06:42:46 -08:00
Niklas von Hertzen
b60fc931b5 moved backgroundPosition to utils 2012-03-03 16:39:52 +02:00
Niklas von Hertzen
8affbc3db5 add unit testing for css 2012-03-03 16:38:05 +02:00
Obexer Christoph
187ae9816e update tests/origin.html to use the new options overriding
allow tests to override selector and rendering options
2012-03-03 15:24:26 +01:00
Obexer Christoph
38fe643b25 clear the canvas with the documentElement background color
see tests/background.html
2012-03-03 15:07:14 +01:00
Obexer Christoph
fb7879fd17 background test: add html and body background-color 2012-03-03 15:07:14 +01:00
Niklas von Hertzen
7726cd9f39 updated test.js to allow options 2012-03-03 00:52:46 +02:00
Niklas von Hertzen
0b065ad5d8 bug fixes 2012-03-02 21:53:20 +02:00
Niklas von Hertzen
ec881018b3 capitalized name 2012-03-02 21:25:08 +02:00
Niklas von Hertzen
f83fb59053 fix naming 2012-03-02 21:24:21 +02:00
Niklas von Hertzen
6eab1d5a9c removed screenshots.html, it's available in gh-pages branch 2012-03-02 20:37:10 +02:00
Niklas von Hertzen
94f2f799a4 Split renderers to their own objects 2012-03-02 20:35:48 +02:00
Niklas von Hertzen
cad3be2c66 bug fixes, and further simplification of API 2012-03-02 19:07:15 +02:00
175 changed files with 16612 additions and 8098 deletions

19
.gitignore vendored
View File

@@ -1,14 +1,13 @@
/nbproject/
/images/
/external/
/tests/templates/
/tests/cache/
/tests/flashcanvas.html
/lib/
/build/
index.html
image.jpg
screenshots.html
screenshots_local.html
/.project
dist/
/.settings/
node_modules/
.envrc
*.sublime-workspace
*.baseline
*.iml
.idea/
.DS_Store
npm-debug.log

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "src/fabric"]
path = src/fabric
url = https://github.com/kangax/fabric.js.git

19
.jshintrc Normal file
View File

@@ -0,0 +1,19 @@
{
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": false,
"newcap": true,
"noarg": true,
"sub": true,
"undef": true,
"boss": true,
"eqnull": true,
"browser": true,
"node": true,
"indent": 4,
"globals": {
"jQuery": true
},
"predef": ["Promise", "define"]
}

10
.npmignore Normal file
View File

@@ -0,0 +1,10 @@
tests/
examples/
Gruntfile.js
bower.json
src/
*.iml
.idea/
.npmignore
.jshintrc
.travis.yml

29
.travis.yml Normal file
View File

@@ -0,0 +1,29 @@
language: node_js
node_js:
- '0.10'
env:
global:
- secure: eW41gIqOizwO4pTgWnAAbW75AP7F+CK9qfSed/fSh4sJ9HWMIY1YRIaY8gjr+6jV/f7XVHcXuym6ZxgINYSkVKbF1JKxBJNLOXtSgNbVHSic58pYFvUjwxIBI9aPig9uux1+DbnpWqXFDTcACJSevQZE0xwmjdrSkDLgB0G34v8=
- secure: Y2Av+Gd3z9uQEB36GwdOOuGka0hx0/HeitASEo59z934O8RxnmN9eNTXS7dDT3XtKtwxIyLTOEpS7qlRdWahH28hr/dS4xJj6ao58C+1xMcDs6NAPGmDxUlcJWpcGEsnjmXjQCc3fBioSTdpIBrK/gdvgpNh77UKG74Sk7Z+YGk=
- secure: YI+YbTOGf2x4fPMKW+KhJiZWswoXT6xOKGwLfsQsVwmFX1LerJouil5D5iYOQuL4FE3pNaoJSNakIsokJQuGKJMmnPc8rdhMZuBJBk6MRghurE2Xe9qBHfuUBPlfD61nARESm4WDcyMwM0QVYaOKeY6aIpZ91qbUbyc60EEx3C4=
addons:
sauce_connect: true
before_script:
- npm install -g grunt-cli
- npm install -g uglify-js
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/2b007d4f86de89588804
on_success: always
on_failure: always
on_start: false
deploy:
- provider: npm
email: niklasvh@gmail.com
api_key:
secure: G/Szpr8q4/D6hp+H/Z9yyluUXtHAwf7LLa1Y07X59/Enlj1h7V5fQ7AW4/iAVM3XbIsrCPWR3dJU9g/ZxpxFg4OovIHVpS2Jr/mahtPYWdHR3pWuSmMW8QD+Twnq2VAFwSgg5Oumq3QxhX3YbCOnZox6+6Uviqk8FO7Z5B0RwW4=
on:
tags: true
branch: master
repo: niklasvh/html2canvas

57
CHANGELOG.md Normal file
View File

@@ -0,0 +1,57 @@
### Changelog ###
v0.5.0-alpha2 - 3.2.2015
* Switch to using browserify for building
* Fix (#517) Chrome stretches background images with 'auto' or single attributes
v0.5.0-alpha - 19.1.2015
* Complete rewrite of library
* Switched interface to return Promise
* Uses hidden iframe window to perform rendering, allowing async rendering and doesn't force the viewport to be scrolled to the top anymore.
* Better support for unicode
* Checkbox/radio button rendering
* SVG rendering
* iframe rendering
* Changed format for proxy requests, permitting binary responses with CORS headers as well
* Fixed many layering issues (see z-index tests)
v0.4.1 - 7.9.2013
* Added support for bower
* Improved z-index ordering
* Basic implementation for CSS transformations
* Fixed inline text in top element
* Basic implementation for text-shadow
v0.4.0 - 30.1.2013
* Added rendering tests with <a href="https://github.com/niklasvh/webdriver.js">webdriver</a>
* Switched to using grunt for building
* Removed support for IE<9, including any FlashCanvas bits
* Support for border-radius
* Support for multiple background images, size, and clipping
* Support for :before and :after pseudo elements
* Support for placeholder rendering
* Reformatted all tests to small units to test specific features
v0.3.4 - 26.6.2012
* Removed (last?) jQuery dependencies (<a href="https://github.com/niklasvh/html2canvas/commit/343b86705fe163766fcf735eb0217130e4bd5b17">niklasvh</a>)
* SVG-powered rendering (<a href="https://github.com/niklasvh/html2canvas/commit/67d3e0d0f59a5a654caf71a2e3be6494ff146c75">niklasvh</a>)
* Radial gradients (<a href="https://github.com/niklasvh/html2canvas/commit/4f22c18043a73c0c3bbf3b5e4d62714c56acd3c7">SunboX</a>)
* Split renderers to their own objects (<a href="https://github.com/niklasvh/html2canvas/commit/94f2f799a457cd29a21cc56ef8c06f1697866739">niklasvh</a>)
* Simplified API, cleaned up code (<a href="https://github.com/niklasvh/html2canvas/commit/c7d526c9eaa6a4abf4754d205fe1dee360c7660e">niklasvh</a>)
v0.3.3 - 2.3.2012
* SVG taint fix, and additional taint testing options for rendering (<a href="https://github.com/niklasvh/html2canvas/commit/2dc8b9385e656696cb019d615bdfa1d98b17d5d4">niklasvh</a>)
* Added support for CORS images and option to create canvas as tainted (<a href="https://github.com/niklasvh/html2canvas/commit/3ad49efa0032cde25c6ed32a39e35d1505d3b2ef">niklasvh</a>)
* Improved minification saved ~1K! (<a href="https://github.com/cobexer/html2canvas/commit/b82be022b2b9240bd503e078ac980bde2b953e43">cobexer</a>)
* Added integrated support for Flashcanvas (<a href="https://github.com/niklasvh/html2canvas/commit/e9257191519f67d74fd5e364d8dee3c0963ba5fc">niklasvh</a>)
* Fixed a variety of legacy IE bugs (<a href="https://github.com/niklasvh/html2canvas/commit/b65357c55d0701017bafcd357bc654b54d458f8f">niklasvh</a>)
v0.3.2 - 20.2.2012
* Added changelog!
* Added bookmarklet (<a href="https://github.com/niklasvh/html2canvas/commit/b320dd306e1a2d32a3bc5a71b6ebf6d8c060cde5">cobexer</a>)
* Option to select single element to render (<a href="https://github.com/niklasvh/html2canvas/commit/0cb252ada91c84ef411288b317c03e97da1f12ad">niklasvh</a>)
* Fixed closure compiler warnings (<a href="https://github.com/niklasvh/html2canvas/commit/36ff1ec7aadcbdf66851a0b77f0b9e87e4a8e4a1">cobexer</a>)
* Enable profiling in FF (<a href="https://github.com/niklasvh/html2canvas/commit/bbd75286a8406cf9e5aea01fdb7950d547edefb9">cobexer</a>)

220
Gruntfile.js Normal file
View File

@@ -0,0 +1,220 @@
/*global module:false*/
var _ = require('lodash'), path = require('path');
var proxy = require('html2canvas-proxy');
module.exports = function(grunt) {
var meta = {
banner: '/*\n <%= pkg.title || pkg.name %> <%= pkg.version %>' +
'<%= pkg.homepage ? " <" + pkg.homepage + ">" : "" %>' + '\n' +
' Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>' +
'\n\n Released under <%= _.pluck(pkg.licenses, "type").join(", ") %> License\n*/\n'
};
var browsers = {
chrome: {
browserName: "chrome",
platform: "Windows 7",
version: "39"
},
firefox: {
browserName: "firefox",
version: "15",
platform: "Windows 7"
},
ie9: {
browserName: "internet explorer",
version: "9",
platform: "Windows 7"
},
ie10: {
browserName: "internet explorer",
version: "10",
platform: "Windows 8"
},
ie11: {
browserName: "internet explorer",
version: "11",
platform: "Windows 8.1"
},
safari6: {
browserName: "safari",
version: "6",
platform: "OS X 10.8"
},
safari7:{
browserName: "safari",
platform: "OS X 10.9",
version: "7"
},
chromeOSX:{
browserName: "chrome",
platform: "OS X 10.8",
version: "39"
}
};
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
browserify: {
dist: {
src: ['src/core.js'],
dest: 'dist/<%= pkg.name %>.js',
options: {
browserifyOptions: {
standalone: 'html2canvas'
},
banner: meta.banner
}
},
svg: {
src: [
'src/fabric/dist/fabric.js'
],
dest: 'dist/<%= pkg.name %>.svg.js',
options:{
browserifyOptions: {
standalone: 'html2canvas.svg'
},
banner: meta.banner
}
}
},
connect: {
server: {
options: {
port: 8080,
base: './',
keepalive: true
}
},
altServer: {
options: {
port: 8083,
base: './'
}
},
cors: {
options: {
port: 8081,
base: './',
middleware: function(connect, options) {
return [
function(req, res, next) {
if (req.url !== '/tests/assets/image2.jpg') {
next();
return;
}
res.setHeader("Access-Control-Allow-Origin", "*");
res.end(require("fs").readFileSync('tests/assets/image2.jpg'));
}
];
}
}
},
proxy: {
options: {
port: 8082,
middleware: function(connect, options) {
return [
function(req, res, next) {
res.jsonp = function(content) {
res.end(req.query.callback + "(" + JSON.stringify(content) + ")");
};
next();
},
proxy()
];
}
}
},
ci: {
options: {
port: 8080,
base: './'
}
}
},
execute: {
fabric: {
options: {
args: ['modules=' + ['text','serialization',
'parser', 'gradient', 'pattern', 'shadow', 'freedrawing',
'image_filters', 'serialization'].join(","), 'no-es5-compat', 'dest=' + path.resolve(__dirname, 'src/fabric/dist/') + '/']
},
src: ['src/fabric/build.js']
}
},
uglify: {
dist: {
src: ['<%= browserify.dist.dest %>'],
dest: 'dist/<%= pkg.name %>.min.js'
},
svg: {
src: ['<%= browserify.svg.dest %>'],
dest: 'dist/<%= pkg.name %>.svg.min.js'
},
options: {
banner: meta.banner
}
},
watch: {
files: ['src/**/*', '!src/fabric/**/*'],
tasks: ['jshint', 'build']
},
jshint: {
all: ['src/*.js', 'src/renderers/*.js'],
options: grunt.file.readJSON('./.jshintrc')
},
mochacli: {
options: {
reporter: 'spec'
},
all: ['tests/node/*.js']
},
mocha_phantomjs: {
all: ['tests/mocha/**/*.html']
},
mocha_webdriver: browsers,
webdriver: browsers
});
grunt.registerTask('webdriver', 'Browser render tests', function(browser, test) {
var selenium = require("./tests/selenium.js");
var done = this.async();
var browsers = (browser) ? [grunt.config.get(this.name + "." + browser)] : _.values(grunt.config.get(this.name));
selenium.tests(browsers, test).catch(function() {
done(false);
}).finally(function() {
console.log("Done");
done();
});
});
grunt.registerTask('mocha_webdriver', 'Browser mocha tests', function(browser, test) {
var selenium = require("./tests/mocha/selenium.js");
var done = this.async();
var browsers = (browser) ? [grunt.config.get(this.name + "." + browser)] : _.values(grunt.config.get(this.name));
selenium.tests(browsers, test).catch(function() {
done(false);
}).finally(function() {
done();
});
});
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-mocha-phantomjs');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-mocha-cli');
grunt.registerTask('server', ['connect:cors', 'connect:proxy', 'connect:altServer', 'connect:server']);
grunt.registerTask('build', ['execute', 'browserify', 'uglify']);
grunt.registerTask('default', ['jshint', 'build', 'mochacli', 'connect:altServer', 'mocha_phantomjs']);
grunt.registerTask('travis', ['jshint', 'build', 'connect:altServer', 'connect:ci', 'connect:proxy', 'connect:cors', 'mocha_phantomjs', 'webdriver']);
};

37
LICENSE
View File

@@ -1,21 +1,22 @@
/*
The MIT License
Copyright (c) 2012 Niklas von Hertzen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,57 +0,0 @@
<!doctype html>
<html>
<head>
<title>html2canvas Bookmarklet</title>
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript">
var isDebug = false, origBookmarklet = '';
function patchLinks() {
var bookmarklet = origBookmarklet;
if (isDebug) {
bookmarklet = bookmarklet.replace('//DEBUG: ', '');
}
bookmarklet = bookmarklet.replace(/\s\/\/.*/g, ''); // remove single line comments
bookmarklet = bookmarklet.replace(/[\u000A\u000D]+/g, ''); // remove all linebreaks
bookmarklet = bookmarklet.replace(/\/\*.*?\*\//g, ''); // remove multi line comments
bookmarklet = bookmarklet.replace(/\s\s+/g, ' '); // reduce multiple spaces to single spaces
bookmarklet = bookmarklet.replace(/\s+=\s+/g, '=');
$('a.bookmarklet').each(function(_, el) {
el.href = $(el).attr('data-href') + bookmarklet;
});
}
$(function() {
$('input[type=checkbox]').bind('change', function() {
isDebug = $(this).is(':checked');
patchLinks();
}).change();
$.ajax('src/plugins/bookmarklet.js', {
dataType: 'text',
success: function(data, status, xhr) {
origBookmarklet = data;
patchLinks();
}
});
});
</script>
</head>
<body>
<h1>html2canvas Bookmarklet</h1>
<p>
If you use a normal browser: drag the normal <a class="bookmarklet" data-href="javascript:">html2canvas</a> bookmarklet to your bookmarks toolbar.<br />
If not use the following link: <a class="bookmarklet" data-href="#_remove_this_javascript:">bookmarklet for those special mobile devices</a>
click / tap that link and then bookmark the page, edit the bookmark and remove the start up until including <code>#_remove_this_</code>
part at the beginning of the URL, it must start with: <code>javascript:</code> to be correct.
</p>
<p>
If you are using Firefox and the NoScript Addon: disable the ABE part of it,
took me quite some time to figure out that the reason for an unreliable bookmarklet was in NoScript...
</p>
<h2>For Developers:</h2>
<p>
If you are a developer and want to debug locally (you need the source tree of your html2canvas at:
<code>http(s)://localhost/html2canvas/</code>)
check the following box to get the bookmarklet patched automatically ;)<br />
<label>Debug bookmarklet: <input type="checkbox" /></label>
</p>
</body>
</html>

9
bower.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "html2canvas",
"description": "Screenshots with JavaScript",
"main": "dist/html2canvas.js",
"ignore": [
"tests",
".travis.yml"
]
}

100
build.xml
View File

@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<project name="html2canvas" basedir="." default="build">
<property name="src.dir" location="src"/>
<property name="lib.dir" location="../lib"/>
<property name="build.dir" location="build"/>
<property name="dist" location="dist"/>
<property name="jquery-externs" value="jquery-1.4.4.externs.js"/>
<property name="JS_NAME" value="html2canvas.js"/>
<property name="JS_NAME_MIN" value="html2canvas.min.js"/>
<property name="JQUERY_PLUGIN_NAME" value="jquery.plugin.html2canvas.js"/>
<loadfile property="version" srcfile="version.txt" />
<path id="sourcefiles">
<filelist dir="${src.dir}">
<file name="LICENSE"/>
<file name="html2canvas-pre.txt"/>
<file name="Core.js"/>
<file name="Generate.js"/>
<file name="Parse.js"/>
<file name="Preload.js"/>
<file name="Queue.js"/>
<file name="Renderer.js"/>
<file name="Util.js"/>
<file name="html2canvas-post.txt"/>
</filelist>
</path>
<path id="jquery-plugin">
<fileset dir="${src.dir}" includes="LICENSE"/>
<fileset dir="${src.dir}/plugins" includes="${JQUERY_PLUGIN_NAME}"/>
</path>
<target name="build-dir">
<echo>Creating directory ${build.dir}...</echo>
<mkdir dir="${build.dir}"/>
</target>
<target name="plugins" depends="build-dir">
<echo>Creating ${JQUERY_PLUGIN_NAME}...</echo>
<concat fixlastline="yes" destfile="${build.dir}/${JQUERY_PLUGIN_NAME}">
<path refid="jquery-plugin"/>
</concat>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JQUERY_PLUGIN_NAME}" />
</target>
<pathconvert property="prettty-sourcefiles" pathsep="${line.separator}" refid="sourcefiles"></pathconvert>
<target name="build" depends="build-dir,plugins">
<echo>Concatenating files:${line.separator}${prettty-sourcefiles}${line.separator}into ${build.dir}/${JS_NAME}...</echo>
<concat fixlastline="yes" destfile="${build.dir}/${JS_NAME}">
<path refid="sourcefiles"/>
</concat>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME}" />
</target>
<taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask"
classpath="${lib.dir}/compiler.jar" onerror="report"/>
<target name="syntaxcheck" depends="build-dir,build">
<jscomp compilationLevel="simple" warning="verbose"
debug="false"
output="${build.dir}/${JS_NAME_MIN}.tmp">
<externs dir="${lib.dir}">
<file name="${jquery-externs}"/>
</externs>
<sources dir="${src.dir}">
<!-- need to write them again here since the closure compiler doesn't understand filesets,... -->
<file name="LICENSE"/>
<file name="Core.js"/>
<file name="Generate.js"/>
<file name="Parse.js"/>
<file name="Preload.js"/>
<file name="Queue.js"/>
<file name="Renderer.js"/>
<file name="Util.js"/>
</sources>
</jscomp>
<delete file="${build.dir}/${JS_NAME_MIN}.tmp"></delete>
</target>
<target name="release" depends="build-dir,build,syntaxcheck">
<jscomp compilationLevel="simple" warning="verbose"
debug="false"
output="${build.dir}/${JS_NAME_MIN}">
<externs dir="${lib.dir}">
<file name="${jquery-externs}"/>
</externs>
<sources dir="${build.dir}">
<file name="${JS_NAME}"/>
</sources>
</jscomp>
<replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true" file="${build.dir}/${JS_NAME_MIN}" />
</target>
<target name="clean">
<delete dir="${build.dir}"></delete>
</target>
</project>

2373
demo.html

File diff suppressed because it is too large Load Diff

View File

@@ -1,186 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('body').html2canvas();
// var ss = $('ul').offset();
// alert(ss.left);
});
</script>
<title>
display/box/float/clear test
</title>
<style type="text/css">
/* last modified: 1 Dec 98 */
html {
font: 10px/1 Verdana, sans-serif;
background-color: blue;
color: white;
}
body {
margin: 1.5em;
border: .5em solid black;
padding: 0;
width: 48em;
background-color: white;
}
dl {
margin: 0;
border: 0;
padding: .5em;
}
dt {
background-color: rgb(204,0,0);
margin: 0;
padding: 1em;
width: 10.638%; /* refers to parent element's width of 47em. = 5em or 50px */
height: 28em;
border: .5em solid black;
float: left;
}
dd {
float: right;
margin: 0 0 0 1em;
border: 1em solid black;
padding: 1em;
width: 34em;
height: 27em;
}
ul {
margin: 0;
border: 0;
padding: 0;
}
li {
display: block; /* i.e., suppress marker */
color: black;
height: 9em;
width: 5em;
margin: 0;
border: .5em solid black;
padding: 1em;
float: left;
background-color: #FC0;
}
#bar {
background-color: black;
color: white;
width: 41.17%; /* = 14em */
border: 0;
margin: 0 1em;
}
#baz {
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
background-color: black;
color: white;
}
form {
margin: 0;
display: inline;
}
p {
margin: 0;
}
form p {
line-height: 1.9;
}
blockquote {
margin: 1em 1em 1em 2em;
border-width: 1em 1.5em 2em .5em;
border-style: solid;
border-color: black;
padding: 1em 0;
width: 5em;
height: 9em;
float: left;
background-color: #FC0;
color: black;
}
address {
font-style: normal;
}
h1 {
background-color: black;
color: white;
float: left;
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
font-weight: normal;
font-size: 1em;
}
</style>
</head>
<body>
<dl>
<dt>
toggle
</dt>
<dd>
<ul>
<li>
the way
</li>
<li id="bar">
<p>
the world ends
</p>
<form action="./" method="get">
<p>
bang
<input type="radio" name="foo" value="off">
</p>
<p>
whimper
<input type="radio" name="foo2" value="on">
</p>
</form>
</li>
<li>
i grow old
</li>
<li id="baz">
pluot?
</li>
</ul>
<blockquote>
<address>
bar maids,
</address>
</blockquote>
<h1>
sing to me, erbarme dich
</h1>
</dd>
</dl>
<p style="color: black; font-size: 1em; line-height: 1.3em; clear: both">
This is a nonsensical document, but syntactically valid HTML 4.0. All 100% conformant CSS1 agents should be able to render the document elements above this paragraph <b>indistinguishably</b> (to the pixel) from this reference rendering, (except font rasterization and form widgets). All discrepancies should be traceable to CSS1 implementation shortcomings. Once you have finished evaluating this test, you can return to the <A HREF="sec5526c.htm" style="text-decoration:none">parent page</A>.
</p>
</body>
</html>

View File

@@ -1,72 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript">
$(function(){
$('body').html2canvas();
});
</script>
<style>
.feedback-overlay-black{
background-color:#000;
opacity:0.5;
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
margin:0;
}
</style>
<style>
div{
padding:20px;
margin:0 auto;
border:5px solid black;
}
h1{
border-bottom:2px solid white;
}
h2{
background: #efefef;
padding:10px;
}
</style>
</head>
<body>
<div style="background:red;">
<div style="background:green;">
<div style="background:blue;border-color:white;">
<div style="background:yellow;"><div style="background:orange;"><h1>Heading</h1>
Text that isn't wrapped in anything.
<p>Followed by some text wrapped in a <b>&lt;p&gt; paragraph.</b> </p>
Maybe add a <a href="#">link</a> or a different style of <a href="#" style="background:white;" id="highlight">link with a highlight</a>.
<hr />
<h2>More content</h2>
<div style="width:10px;height:10px;border-width:10px;padding:0;">a</div>
</div></div>
</div>
</div>
</div>
</body>
</html>

181
examples/demo.html Normal file
View File

@@ -0,0 +1,181 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>
display/box/float/clear test
</title>
<style type="text/css">
/* last modified: 1 Dec 98 */
html {
font: 10px/1 Verdana, sans-serif;
background-color: blue;
color: white;
}
body {
margin: 1.5em;
border: .5em solid black;
padding: 0;
width: 48em;
background-color: white;
}
dl {
margin: 0;
border: 0;
padding: .5em;
}
dt {
background-color: rgb(204,0,0);
margin: 0;
padding: 1em;
width: 10.638%; /* refers to parent element's width of 47em. = 5em or 50px */
height: 28em;
border: .5em solid black;
float: left;
}
dd {
float: right;
margin: 0 0 0 1em;
border: 1em solid black;
padding: 1em;
width: 34em;
height: 27em;
}
ul {
margin: 0;
border: 0;
padding: 0;
}
li {
display: block; /* i.e., suppress marker */
color: black;
height: 9em;
width: 5em;
margin: 0;
border: .5em solid black;
padding: 1em;
float: left;
background-color: #FC0;
}
#bar {
background-color: black;
color: white;
width: 41.17%; /* = 14em */
border: 0;
margin: 0 1em;
}
#baz {
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
background-color: black;
color: white;
}
form {
margin: 0;
display: inline;
}
p {
margin: 0;
}
form p {
line-height: 1.9;
}
blockquote {
margin: 1em 1em 1em 2em;
border-width: 1em 1.5em 2em .5em;
border-style: solid;
border-color: black;
padding: 1em 0;
width: 5em;
height: 9em;
float: left;
background-color: #FC0;
color: black;
}
address {
font-style: normal;
}
h1 {
background-color: black;
color: white;
float: left;
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
font-weight: normal;
font-size: 1em;
}
</style>
</head>
<body>
<dl>
<dt>
toggle
</dt>
<dd>
<ul>
<li>
the way
</li>
<li id="bar">
<p>
the world ends
</p>
<form action="./" method="get">
<p>
bang
<input type="radio" name="foo" value="off">
</p>
<p>
whimper
<input type="radio" name="foo2" value="on">
</p>
</form>
</li>
<li>
i grow old
</li>
<li id="baz">
pluot?
</li>
</ul>
<blockquote>
<address>
bar maids,
</address>
</blockquote>
<h1>
sing to me, erbarme dich
</h1>
</dd>
</dl>
<p style="color: black; font-size: 1em; line-height: 1.3em; clear: both">
This is a nonsensical document, but syntactically valid HTML 4.0. All 100% conformant CSS1 agents should be able to render the document elements above this paragraph <b>indistinguishably</b> (to the pixel) from this reference rendering, (except font rasterization and form widgets). All discrepancies should be traceable to CSS1 implementation shortcomings. Once you have finished evaluating this test, you can return to the <A HREF="sec5526c.htm" style="text-decoration:none">parent page</A>.
</p>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<script type="text/javascript">
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
</script>
</body>
</html>

63
examples/demo2.html Normal file
View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.feedback-overlay-black{
background-color:#000;
opacity:0.5;
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
margin:0;
}
</style>
<style>
div{
padding:20px;
margin:0 auto;
border:5px solid black;
}
h1{
border-bottom:2px solid white;
}
h2{
background: #efefef;
padding:10px;
}
</style>
</head>
<body>
<div style="background:red;">
<div style="background:green;">
<div style="background:blue;border-color:white;">
<div style="background:yellow;"><div style="background:orange;"><h1>Heading</h1>
Text that isn't wrapped in anything.
<p>Followed by some text wrapped in a <b>&lt;p&gt; paragraph.</b> </p>
Maybe add a <a href="#">link</a> or a different style of <a href="#" style="background:white;" id="highlight">link with a highlight</a>.
<hr />
<h2>More content</h2>
<div style="width:10px;height:10px;border-width:10px;padding:0;">a</div>
</div></div>
</div>
</div>
</div>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<script type="text/javascript">
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Using an existing canvas to draw on</title>
<style>
canvas {
border: 1px solid black;
}
button {
clear: both;
display: block;
}
#content {
background: rgba(100, 255, 255, 0.5);
padding: 50px 10px;
}
</style>
</head>
<body>
<div><h1>HTML content to render:</h1>
<div id="content">Render the content in this element <strong>only</strong> onto the existing canvas element</div>
</div>
<h1>Existing canvas:</h1>
<canvas width="500" height="200"></canvas>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<button>Run html2canvas</button>
<script type="text/javascript">
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // Mouth (clockwise)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye
ctx.stroke();
document.querySelector("button").addEventListener("click", function() {
html2canvas(document.querySelector("#content"), {canvas: canvas}).then(function(canvas) {
console.log('Drew on the existing canvas');
});
}, false);
</script>
</body>
</html>

1194
external/flashcanvas.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +0,0 @@
/*
* FlashCanvas
*
* Copyright (c) 2009 Tim Cameron Ryan
* Copyright (c) 2009-2011 FlashCanvas Project
* Released under the MIT/X License
*/
window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b<c;++b)B.initElement(a[b])}}function G(){var a=
event.srcElement,b=a.parentNode;a.blur();b.focus()}function H(){var a=event.propertyName;if(a==="width"||a==="height"){var b=event.srcElement,c=b[a],d=parseInt(c,10);if(isNaN(d)||d<0)d=a==="width"?300:150;if(c===d){b.style[a]=d+"px";b.getContext("2d").I(b.width,b.height)}else b[a]=d}}function I(){h.detachEvent(J,I);for(var a in s){var b=s[a],c=b.firstChild,d;for(d in c)if(typeof c[d]==="function")c[d]=k;for(d in b)if(typeof b[d]==="function")b[d]=k;c.detachEvent(K,G);b.detachEvent(L,H)}h[M]=k;h[N]=
k;h[O]=k;h[C]=k;h[P]=k}function V(){var a=j.getElementsByTagName("script");a=a[a.length-1];return j.documentMode>=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;")}function W(a){return a.toLowerCase()}function i(a){throw new D(a);}function Q(a){var b=parseInt(a.width,10),c=parseInt(a.height,10);if(isNaN(b)||b<0)b=300;if(isNaN(c)||c<0)c=150;a.width=b;a.height=c}var k=null,r="canvas",M="CanvasRenderingContext2D",N="CanvasGradient",O="CanvasPattern",
C="FlashCanvas",P="G_vmlCanvasManager",K="onfocus",L="onpropertychange",F="onreadystatechange",J="onunload",w=((h[C+"Options"]||{}).swfPath||V().replace(/[^\/]+$/,""))+"flashcanvas.swf",e=new function(a){for(var b=0,c=a.length;b<c;b++)this[a[b]]=b}(["toDataURL","save","restore","scale","rotate","translate","transform","setTransform","globalAlpha","globalCompositeOperation","strokeStyle","fillStyle","createLinearGradient","createRadialGradient","createPattern","lineWidth","lineCap","lineJoin","miterLimit",
"shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","clearRect","fillRect","strokeRect","beginPath","closePath","moveTo","lineTo","quadraticCurveTo","bezierCurveTo","arcTo","rect","arc","fill","stroke","clip","isPointInPath","font","textAlign","textBaseline","fillText","strokeText","measureText","drawImage","createImageData","getImageData","putImageData","addColorStop","direction","resize"]),x={},n={},s={},y={};u.prototype={save:function(){this.b();this.c();this.m();this.l();this.z();this.w();
this.F.push([this.f,this.g,this.A,this.u,this.j,this.h,this.i,this.k,this.p,this.q,this.n,this.o,this.v,this.r,this.s]);this.a.push(e.save)},restore:function(){var a=this.F;if(a.length){a=a.pop();this.globalAlpha=a[0];this.globalCompositeOperation=a[1];this.strokeStyle=a[2];this.fillStyle=a[3];this.lineWidth=a[4];this.lineCap=a[5];this.lineJoin=a[6];this.miterLimit=a[7];this.shadowOffsetX=a[8];this.shadowOffsetY=a[9];this.shadowBlur=a[10];this.shadowColor=a[11];this.font=a[12];this.textAlign=a[13];
this.textBaseline=a[14]}this.a.push(e.restore)},scale:function(a,b){this.a.push(e.scale,a,b)},rotate:function(a){this.a.push(e.rotate,a)},translate:function(a,b){this.a.push(e.translate,a,b)},transform:function(a,b,c,d,f,g){this.a.push(e.transform,a,b,c,d,f,g)},setTransform:function(a,b,c,d,f,g){this.a.push(e.setTransform,a,b,c,d,f,g)},b:function(){var a=this.a;if(this.f!==this.globalAlpha){this.f=this.globalAlpha;a.push(e.globalAlpha,this.f)}if(this.g!==this.globalCompositeOperation){this.g=this.globalCompositeOperation;
a.push(e.globalCompositeOperation,this.g)}},m:function(){if(this.A!==this.strokeStyle){var a=this.A=this.strokeStyle;this.a.push(e.strokeStyle,typeof a==="object"?a.id:a)}},l:function(){if(this.u!==this.fillStyle){var a=this.u=this.fillStyle;this.a.push(e.fillStyle,typeof a==="object"?a.id:a)}},createLinearGradient:function(a,b,c,d){isFinite(a)&&isFinite(b)&&isFinite(c)&&isFinite(d)||i(9);this.a.push(e.createLinearGradient,a,b,c,d);return new t(this)},createRadialGradient:function(a,b,c,d,f,g){isFinite(a)&&
isFinite(b)&&isFinite(c)&&isFinite(d)&&isFinite(f)&&isFinite(g)||i(9);if(c<0||g<0)i(1);this.a.push(e.createRadialGradient,a,b,c,d,f,g);return new t(this)},createPattern:function(a,b){a||i(17);var c=a.tagName,d,f=this.d;if(c){c=c.toLowerCase();if(c==="img")d=a.getAttribute("src",2);else if(c===r||c==="video")return;else i(17)}else if(a.src)d=a.src;else i(17);b==="repeat"||b==="no-repeat"||b==="repeat-x"||b==="repeat-y"||b===""||b===k||i(12);this.a.push(e.createPattern,v(d),b);if(x[f]){this.e();++n[f]}return new E(this)},
z:function(){var a=this.a;if(this.j!==this.lineWidth){this.j=this.lineWidth;a.push(e.lineWidth,this.j)}if(this.h!==this.lineCap){this.h=this.lineCap;a.push(e.lineCap,this.h)}if(this.i!==this.lineJoin){this.i=this.lineJoin;a.push(e.lineJoin,this.i)}if(this.k!==this.miterLimit){this.k=this.miterLimit;a.push(e.miterLimit,this.k)}},c:function(){var a=this.a;if(this.p!==this.shadowOffsetX){this.p=this.shadowOffsetX;a.push(e.shadowOffsetX,this.p)}if(this.q!==this.shadowOffsetY){this.q=this.shadowOffsetY;
a.push(e.shadowOffsetY,this.q)}if(this.n!==this.shadowBlur){this.n=this.shadowBlur;a.push(e.shadowBlur,this.n)}if(this.o!==this.shadowColor){this.o=this.shadowColor;a.push(e.shadowColor,this.o)}},clearRect:function(a,b,c,d){this.a.push(e.clearRect,a,b,c,d)},fillRect:function(a,b,c,d){this.b();this.c();this.l();this.a.push(e.fillRect,a,b,c,d)},strokeRect:function(a,b,c,d){this.b();this.c();this.m();this.z();this.a.push(e.strokeRect,a,b,c,d)},beginPath:function(){this.a.push(e.beginPath)},closePath:function(){this.a.push(e.closePath)},
moveTo:function(a,b){this.a.push(e.moveTo,a,b)},lineTo:function(a,b){this.a.push(e.lineTo,a,b)},quadraticCurveTo:function(a,b,c,d){this.a.push(e.quadraticCurveTo,a,b,c,d)},bezierCurveTo:function(a,b,c,d,f,g){this.a.push(e.bezierCurveTo,a,b,c,d,f,g)},arcTo:function(a,b,c,d,f){f<0&&isFinite(f)&&i(1);this.a.push(e.arcTo,a,b,c,d,f)},rect:function(a,b,c,d){this.a.push(e.rect,a,b,c,d)},arc:function(a,b,c,d,f,g){c<0&&isFinite(c)&&i(1);this.a.push(e.arc,a,b,c,d,f,g?1:0)},fill:function(){this.b();this.c();
this.l();this.a.push(e.fill)},stroke:function(){this.b();this.c();this.m();this.z();this.a.push(e.stroke)},clip:function(){this.a.push(e.clip)},w:function(){var a=this.a;if(this.v!==this.font)try{var b=y[this.d];b.style.font=this.v=this.font;var c=b.currentStyle;a.push(e.font,[c.fontStyle,c.fontWeight,b.offsetHeight,c.fontFamily].join(" "))}catch(d){}if(this.r!==this.textAlign){this.r=this.textAlign;a.push(e.textAlign,this.r)}if(this.s!==this.textBaseline){this.s=this.textBaseline;a.push(e.textBaseline,
this.s)}if(this.t!==this.canvas.currentStyle.direction){this.t=this.canvas.currentStyle.direction;a.push(e.direction,this.t)}},fillText:function(a,b,c,d){this.b();this.l();this.c();this.w();this.a.push(e.fillText,v(a),b,c,d===void 0?Infinity:d)},strokeText:function(a,b,c,d){this.b();this.m();this.c();this.w();this.a.push(e.strokeText,v(a),b,c,d===void 0?Infinity:d)},measureText:function(a){var b=y[this.d];try{b.style.font=this.font}catch(c){}b.innerText=a.replace(/[ \n\f\r]/g,"\t");return new U(b.offsetWidth)},
drawImage:function(a,b,c,d,f,g,o,l,z){a||i(17);var p=a.tagName,m,q=arguments.length,R=this.d;if(p){p=p.toLowerCase();if(p==="img")m=a.getAttribute("src",2);else if(p===r||p==="video")return;else i(17)}else if(a.src)m=a.src;else i(17);this.b();this.c();m=v(m);if(q===3)this.a.push(e.drawImage,q,m,b,c);else if(q===5)this.a.push(e.drawImage,q,m,b,c,d,f);else if(q===9){if(d===0||f===0)i(1);this.a.push(e.drawImage,q,m,b,c,d,f,g,o,l,z)}else return;if(x[R]){this.e();++n[R]}},D:function(){this.globalAlpha=
this.f=1;this.globalCompositeOperation=this.g="source-over";this.fillStyle=this.u=this.strokeStyle=this.A="#000000";this.lineWidth=this.j=1;this.lineCap=this.h="butt";this.lineJoin=this.i="miter";this.miterLimit=this.k=10;this.shadowBlur=this.n=this.shadowOffsetY=this.q=this.shadowOffsetX=this.p=0;this.shadowColor=this.o="rgba(0, 0, 0, 0.0)";this.font=this.v="10px sans-serif";this.textAlign=this.r="start";this.textBaseline=this.s="alphabetic";this.a=[];this.F=[]},H:function(){var a=this.a;this.a=
[];return a},e:function(){var a=this.H();if(a.length>0)return eval(this.B.CallFunction('<invoke name="executeCommand" returntype="javascript"><arguments><string>'+a.join("&#0;")+"</string></arguments></invoke>"))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR",
12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+location.protocol+'//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="100%" height="100%" id="'+c+'"><param name="allowScriptAccess" value="always"><param name="flashvars" value="id='+c+'"><param name="wmode" value="transparent"></object><span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap"></span>';
s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()},
setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+
"//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document);

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

50
package.json Normal file
View File

@@ -0,0 +1,50 @@
{
"title": "html2canvas",
"name": "html2canvas",
"description": "Screenshots with JavaScript",
"main": "dist/html2canvas.js",
"version": "0.5.0-beta2",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "http://hertzen.com"
},
"engines": {
"node": ">=0.10.0"
},
"repository": {
"type": "git",
"url": "git@github.com:niklasvh/html2canvas.git"
},
"bugs": {
"url": "https://github.com/niklasvh/html2canvas/issues"
},
"devDependencies": {
"base64-arraybuffer": ">= 0.1.4",
"bluebird": "2.7.1",
"grunt": "^0.4.5",
"grunt-browserify": "^3.3.0",
"grunt-contrib-connect": "^0.8.0",
"grunt-contrib-jshint": "^0.10.0",
"grunt-contrib-uglify": "^0.6.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-execute": "^0.2.2",
"grunt-mocha-cli": "^1.12.0",
"grunt-mocha-phantomjs": "^2.0.0",
"html2canvas-proxy": "0.0.5",
"humanize-duration": "^2.0.1",
"lodash": "^2.4.1",
"png-js": ">= 0.1.1",
"requirejs": "^2.1.20",
"wd": "^0.2.21"
},
"scripts": {
"test": "grunt travis --verbose"
},
"homepage": "http://html2canvas.hertzen.com",
"licenses": [
{
"type": "MIT"
}
]
}

104
readme.md
View File

@@ -1,54 +1,90 @@
html2canvas
===========
[Homepage](http://html2canvas.hertzen.com) | [Downloads](https://github.com/niklasvh/html2canvas/releases) | [Questions](http://stackoverflow.com/questions/tagged/html2canvas?sort=newest) | [Donate](https://www.gittip.com/niklasvh/)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Build Status](https://travis-ci.org/niklasvh/html2canvas.png)](https://travis-ci.org/niklasvh/html2canvas)
#### JavaScript HTML renderer ####
This script allows you to take "screenshots" of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
The script allows you to take "screenshots" of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
###How does it work?###
The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. However, as many elements are displayed differently on different browsers and operating systems (such as form elements such as radio buttons or checkboxes) as well as
The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements.
It does <b>not require any rendering from the server</b>, as the whole image is created on the <b>clients browser</b>. However, for browsers without <code>canvas</code> support alternatives such as <a href="http://flashcanvas.net/">flashcanvas</a> or <a href="http://excanvas.sourceforge.net/">ExplorerCanvas</a> are necessary to create the image.
It does **not require any rendering from the server**, as the whole image is created on the **clients browser**. However, as it is heavily dependent on the browser, this library is *not suitable* to be used in nodejs.
It doesn't magically circumvent any browser content policy restrictions either, so rendering cross-origin content will require a [proxy](https://github.com/niklasvh/html2canvas/wiki/Proxies) to get the content to the [same origin](http://en.wikipedia.org/wiki/Same_origin_policy).
Additionally, to render <code>iframe</code> content or images situated outside of the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a> a proxy will be necessary to load the content to the users browser.
The script is still in a **very experimental state**, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made.
The script is still in a very experimental state, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made. However, please do test it out and report your findings, especially if something should be working, but is displaying it incorrectly.
###Browser compatibility###
The script should work fine on the following browsers:
The library should work fine on the following browsers (with `Promise` polyfill):
* Firefox 3.5+
* Google Chrome
* Newer versions of Opera (exactly how new is yet to be determined)
* >=IE9 (Older versions compatible with the use of flashcanvas)
Note that the compatibility will most likely be increased in future builds, as many of the current restrictions have at least partial work arounds, which can be used with older browser versions.
* Google Chrome
* Opera 12+
* IE9+
* Safari 6+
###So what isn't included yet?###
There are still a lot of CSS properties missing, including most CSS3 properties such as <code>text-shadow</code>, <code>box-radius</code> etc. as well as all elements created by the browser, such as radio and checkbox buttons and list icons. I will compile a full list of supported elements and CSS properties soon.
There is no support for <code>frame</code> and <code>object</code> content such as Flash.
As each CSS property needs to be manually built to be supported, there are a number of properties that are not yet supported.
### Usage ###
The html2canvas library utilizes `Promise`s and expects them to be available in the global context. If you wish to
support [older browsers](http://caniuse.com/#search=promise) that do not natively support `Promise`s, please include a polyfill such as
[es6-promise](https://github.com/jakearchibald/es6-promise) before including `html2canvas`.
**Note!** These instructions are for using the current dev version of 0.5, for the latest release version (0.4.1), checkout the [old readme](https://github.com/niklasvh/html2canvas/blob/v0.4/readme.md).
To render an `element` with html2canvas, simply call:
` html2canvas(element[, options]);`
The function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the `<canvas>` element. Simply add a promise fullfillment handler to the promise using `then`:
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
### Building ###
The library uses [grunt](http://gruntjs.com/) for building. Alternatively, you can download the latest build from [here](https://github.com/niklasvh/html2canvas/blob/master/dist/html2canvas.js).
Clone git repository with submodules:
$ git clone --recursive git://github.com/niklasvh/html2canvas.git
Install Grunt and uglifyjs:
$ npm install -g grunt-cli uglify-js
Run the full build process (including lint, qunit and webdriver tests):
$ grunt
Skip lint and tests and simply build from source:
$ grunt build
### Running tests ###
The library has two sets of tests. The first set is a number of qunit tests that check that different values parsed by browsers are correctly converted in html2canvas. To run these tests with grunt you'll need [phantomjs](http://phantomjs.org/).
The other set of tests run Firefox, Chrome and Internet Explorer with [webdriver](https://github.com/niklasvh/webdriver.js). The selenium standalone server (runs on Java) is required for these tests and can be downloaded from [here](http://code.google.com/p/selenium/downloads/list). They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values.
Start by downloading the dependencies:
$ npm install
Run qunit tests:
$ grunt test
### Examples ###
For more information and examples, please visit the <a href="http://html2canvas.hertzen.com">homepage</a> or try the <a href="http://html2canvas.hertzen.com/screenshots.html">test console</a>.
For more information and examples, please visit the [homepage](http://html2canvas.hertzen.com) or try the [test console](http://html2canvas.hertzen.com/screenshots.html).
### Changelog ###
### Contributing ###
v0.33 - 2.3.2012
* SVG taint fix, and additional taint testing options for rendering (<a href="https://github.com/niklasvh/html2canvas/commit/2dc8b9385e656696cb019d615bdfa1d98b17d5d4">niklasvh</a>)
* Added support for CORS images and option to create canvas as tainted (<a href="https://github.com/niklasvh/html2canvas/commit/3ad49efa0032cde25c6ed32a39e35d1505d3b2ef">niklasvh</a>)
* Improved minification saved ~1K! (<a href="https://github.com/cobexer/html2canvas/commit/b82be022b2b9240bd503e078ac980bde2b953e43">cobexer</a>)
* Added integrated support for Flashcanvas (<a href="https://github.com/niklasvh/html2canvas/commit/e9257191519f67d74fd5e364d8dee3c0963ba5fc">niklasvh</a>)
* Fixed a variety of legacy IE bugs (<a href="https://github.com/niklasvh/html2canvas/commit/b65357c55d0701017bafcd357bc654b54d458f8f">niklasvh</a>)
v0.32 - 20.2.2012
* Added changelog!
* Added bookmarklet (<a href="https://github.com/niklasvh/html2canvas/commit/b320dd306e1a2d32a3bc5a71b6ebf6d8c060cde5">cobexer</a>)
* Option to select single element to render (<a href="https://github.com/niklasvh/html2canvas/commit/0cb252ada91c84ef411288b317c03e97da1f12ad">niklasvh</a>)
* Fixed closure compiler warnings (<a href="https://github.com/niklasvh/html2canvas/commit/36ff1ec7aadcbdf66851a0b77f0b9e87e4a8e4a1">cobexer</a>)
* Enable profiling in FF (<a href="https://github.com/niklasvh/html2canvas/commit/bbd75286a8406cf9e5aea01fdb7950d547edefb9">cobexer</a>)
If you wish to contribute to the project, please send the pull requests to the develop branch. Before submitting any changes, try and test that the changes work with all the support browsers. If some CSS property isn't supported or is incomplete, please create appropriate tests for it as well before submitting any code changes.

View File

@@ -1,348 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>JavaScript screenshot creator</title>
<style type="text/css">
a {
color: #0B0B0B;
background-color: #FDF9EE;
padding: 0 8px;
text-decoration: none;
font: normal 12px/16px "Trebuchet MS", Arial, Helvetica, sans-serif;
}
a:hover {
color: #0B0B0B;
background-color: #EFEBDE;
padding: 0 8px;
text-decoration: none;
font: normal 12px/16px "Trebuchet MS", Arial, Helvetica, sans-serif;
}
body {
font: normal 14px/19px Arial, Helvetica, sans-serif;
background-color: white;
color: #4E4628;
}
textarea {
background-color: #EFEBDE;
color: #0B0B0B;
border: #C3BCA4 1px solid;
font: normal 11px Arial, Helvetica, sans-serif;
width:300px;
height:150px;
}
h2 {
background-color: white;
color: #0B0B0B;
font: normal 28px/46px Georgia, "Times New Roman", Times, serif;
margin:0;
clear:both;
}
h3 {
color: #786E4E;
padding: 0 0 10px 55px;
height: 37px;
font: normal 24px/30px Georgia, "Times New Roman", Times, serif;
}
ul{
float:left;
margin:0;
}
table{
margin:0 auto;
width:400px;
border:1px solid black;
}
#content{
clear:both;
text-align:center;
}
#about{
padding:0 10px;
width:450px;
float:left;
}
</style>
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js?221"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript" src="http://www.hertzen.com/js/ganalytics-heatmap.js"></script>
<script type="text/javascript">
var date = new Date();
var message,
timeoutTimer,
timer;
var proxyUrl = "http://html2canvas.appspot.com";
function addRow(table,field,val){
var tr = $('<tr />').appendTo( $(table));
tr.append($('<td />').css('font-weight','bold').text(field)).append($('<td />').text(val));
}
function throwMessage(msg,duration){
window.clearTimeout(timeoutTimer);
timeoutTimer = window.setTimeout(function(){
message.fadeOut(function(){
message.remove();
});
},duration || 2000);
$(message).remove();
message = $('<div />').html(msg).css({
margin:0,
padding:10,
background: "#000",
opacity:0.7,
position:"fixed",
top:10,
right:10,
fontFamily: 'Tahoma' ,
color:'#fff',
fontSize:12,
borderRadius:12,
width:'auto',
height:'auto',
textAlign:'center',
textDecoration:'none'
}).hide().fadeIn().appendTo('body');
}
$(function(){
$('ul li a').click(function(e){
e.preventDefault();
$('#url').val(this.href);
$('button').click();
})
var iframe,d;
$('input[type="button"]').click(function(){
$(iframe.contentWindow).unbind('load');
$(iframe).contents().find('body').html2canvas({
canvasHeight: d.body.scrollHeight,
canvasWidth: d.body.scrollWidth,
logging:true
});
});
$('button').click(function(){
$(this).prop('disabled',true);
var url = $('#url').val();
$('#content').append($('<img />').attr('src','loading.gif').css('margin-top',40));
var urlParts = document.createElement('a');
urlParts.href = url;
$.ajax({
data: {
xhr2:false,
url:urlParts.href
},
url: proxyUrl,
dataType: "jsonp",
success: function(html){
iframe = document.createElement('iframe');
$(iframe).css({
'visibility':'hidden'
}).width($(window).width()).height($(window).height());
$('#content').append(iframe);
d = iframe.contentWindow.document;
d.open();
$(iframe.contentWindow).load(function(){
// timer = date.getTime();
var date = new Date();
var message,
timeoutTimer,
timer = date.getTime();
var body = $(iframe).contents().find('body')[0];
var preload = html2canvas.Preload(body, {
"complete": function(images){
var queue = html2canvas.Parse(body, images);
var canvas = $(html2canvas.Renderer(queue));
var finishTime = new Date();
$("#content").empty().append(canvas);
// throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)/1000) + " seconds<br />",4000);
}
});
/*
$(iframe).contents().find('body').html2canvas({
canvasHeight: d.body.scrollHeight,
canvasWidth: d.body.scrollWidth,
logging:true,
proxyUrl: proxyUrl,
logger:function(msg){
$('#logger').val(function(e,i){
return i+"\n"+msg;
});
},
ready: function(renderer) {
$('button').prop('disabled',false);
$("#content").empty();
var finishTime = new Date();
var table = $('<table />');
$('#content')
.append('<h2>Screenshot</h2>')
.append(renderer.canvas)
.append('<h3>Details</h3>')
.append(table);
addRow(table,"Creation time",((finishTime.getTime()-timer)/1000) + " seconds");
addRow(table,"Total draws", renderer.numDraws);
addRow(table,"Context stacks", renderer.contextStacks.length);
addRow(table,"Loaded images", renderer.images.length/2);
addRow(table,"Performed z-index reorder", renderer.needReorder);
addRow(table,"Used rangeBounds", renderer.support.rangeBounds);
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)/1000) + " seconds<br />Total of "+renderer.numDraws+" draws performed",4000);
}
});*/
});
$('base').attr('href',urlParts.protocol+"//"+urlParts.hostname+"/");
var base = "<base href='"+urlParts.protocol+"//"+urlParts.hostname+"/' />";
var headIdx = html.indexOf('<head');
var endHeadIdx = html.indexOf('>', headIdx);
html = html.substring(0, endHeadIdx + 1) + base + html.substring(endHeadIdx + 1);
if ($("#disablejs").prop('checked')){
html = html.replace(/\<script/gi,"<!--<script");
html = html.replace(/\<\/script\>/gi,"<\/script>-->");
}
// console.log(html);
d.write(html);
d.close();
}
});
});
});
</script>
<script type="text/javascript">
var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-188600-10']);_gaq.push(['_trackPageview']);(function() {var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);})();
</script>
<base />
</head>
<body>
<!-- <div style="background:red;padding:10px;color:#fff">
App engine proxy is <a href="http://twitter.com/#!/Niklasvh/status/96265826713350144">temporarily out of use</a> due to exceeded bandwidth use. Please try again tomorrow or meanwhile check other examples <a href="http://html2canvas.hertzen.com/">here</a>.
</div>-->
<div style="float:left;width:500px;">
<h1>JavaScript screenshot creator</h1>
<label for="url">Website URL:</label>
<input type="url" id="url" value="http://www.yahoo.com" /><button>Get screenshot!</button>
<!-- <input type="button" value="Try anyway" />--><br />
<label for="disablejs">Disable JavaScript (recommended, doesn't work well with the proxy)</label> <input type="checkbox" id="disablejs" checked /><br />
<small>Tested with Google Chrome 12, Firefox 4 and Opera 11.5</small>
</div>
<div style="float:right;">
<div style="margin-left:17px;float:right;">
<!-- Place this tag in your head or just before your close body tag -->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<!-- Place this tag where you want the +1 button to render -->
<g:plusone size="tall"></g:plusone>
</div>
<div style="float:right;">
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://html2canvas.hertzen.com/" data-text="html2canvas - screenshots with #JavaScript" data-count="vertical" data-via="niklasvh">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
</div>
</div>
<div style="clear:both;"></div>
<h3>Recommended (tested) pages:</h3>
<ul>
<li><a href="http://www.yahoo.com">yahoo.com</a></li>
<li><a href="http://www.google.com">google.com</a></li>
<li><a href="https://github.com/niklasvh/html2canvas">github.com</a></li>
<li><a href="http://www.smashingmagazine.com">smashingmagazine.com</a></li>
<li><a href="http://www.mashable.com">mashable.com</a></li>
<li><a href="http://www.facebook.com/google">facebook.com/google</a></li>
<li><a href="http://www.youtube.com/">youtube.com</a></li>
<li><a href="http://www.cnn.com/">cnn.com</a></li>
<li><a href="http://www.engadget.com/">engadget.com (lot of elements, very slow)</a></li>
<li><a href="http://eu.battle.net/en/">battle.net</a></li>
</ul>
<div id="about"><b> About</b><br />
The whole screenshot is created with JavaScript. The only server interaction that is happening on this page is the proxy for loading the external pages/images into JSONP/CORS enabled page and onwards onto the JavaScript renderer script.
There are a lot of problems of loading external pages, even with a proxy, and as such many pages will not render at all. If you wish to try the script properly, I recommend you get a copy of the source from <a href="https://github.com/niklasvh/html2canvas">here</a> instead.
</div>
<div id="content"></div>
</body>
</html>

View File

@@ -1,148 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
"use strict";
var _html2canvas = {},
html2canvas;
function h2clog(a) {
if (_html2canvas.logging && window.console && window.console.log) {
window.console.log(a);
}
}
_html2canvas.Util = {};
_html2canvas.Util.backgroundImage = function (src) {
if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
return src;
}
if (src.toLowerCase().substr( 0, 5 ) === 'url("') {
src = src.substr( 5 );
src = src.substr( 0, src.length - 2 );
} else {
src = src.substr( 4 );
src = src.substr( 0, src.length - 1 );
}
return src;
};
_html2canvas.Util.Bounds = function getBounds (el) {
var clientRect,
bounds = {};
if (el.getBoundingClientRect){
clientRect = el.getBoundingClientRect();
// TODO add scroll position to bounds, so no scrolling of window necessary
bounds.top = clientRect.top;
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
bounds.left = clientRect.left;
// older IE doesn't have width/height, but top/bottom instead
bounds.width = clientRect.width || (clientRect.right - clientRect.left);
bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
return bounds;
} /*else{
p = $(el).offset();
return {
left: p.left + getCSS(el,"borderLeftWidth", true),
top: p.top + getCSS(el,"borderTopWidth", true),
width:$(el).innerWidth(),
height:$(el).innerHeight()
};
} */
};
_html2canvas.Util.getCSS = function (el, attribute) {
// return jQuery(el).css(attribute);
/*
var val,
left,
rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
style = el.style;
if ( el.currentStyle ) {
val = el.currentStyle[ attribute ];
} else if (window.getComputedStyle) {
val = document.defaultView.getComputedStyle(el, null)[ attribute ];
}
*/
// Check if we are not dealing with pixels, (Opera has issues with this)
// Ported from jQuery css.js
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
// if ( !/^-?\d+(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {
/*
// Remember the original values
left = style.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
el.runtimeStyle.left = el.currentStyle.left;
}
style.left = attribute === "fontSize" ? "1em" : (val || 0);
val = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
if ( rsLeft ) {
el.runtimeStyle.left = rsLeft;
}*/
// val = $(el).css(attribute);
// }
/*
var val = $(el).css(attribute);
if (val === "medium") {
val = 3;
}*/
return $(el).css(attribute);
};
_html2canvas.Util.Extend = function (options, defaults) {
var key;
for (key in options) {
if (options.hasOwnProperty(key)) {
defaults[key] = options[key];
}
}
return defaults;
};
_html2canvas.Util.Children = function(el) {
// $(el).contents() !== el.childNodes, Opera / IE have issues with that
var children;
try {
children = $(el).contents();
} catch (ex) {
h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
children = [];
}
return children;
};

View File

@@ -1,162 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Generate = {};
_html2canvas.Generate.Gradient = function(src, bounds) {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
tmp,
p0 = 0,
p1 = 0,
p2 = 0,
p3 = 0,
steps = [],
position,
i,
len,
lingrad,
increment,
p,
img;
canvas.width = bounds.width;
canvas.height = bounds.height;
function getColors(input) {
var j = -1,
color = '',
chr;
while( j++ < input.length ) {
chr = input.charAt( j );
if (chr === ')') {
color += chr;
steps.push( color );
color = '';
j = input.indexOf(",", j) + 1;
if (j === 0) {
break;
}
// while (j++ < input.length && input.charAt( j ) !== ',') {}
} else {
color += chr;
}
}
}
if ( (tmp = src.match(/-webkit-linear-gradient\((.*)\)/)) !== null ) {
position = tmp[1].split( ",", 1 )[0];
getColors( tmp[1].substr( position.length + 2 ) );
position = position.split(' ');
for (p = 0; p < position.length; p+=1) {
switch(position[p]) {
case 'top':
p3 = bounds.height;
break;
case 'right':
p0 = bounds.width;
break;
case 'bottom':
p1 = bounds.height;
break;
case 'left':
p2 = bounds.width;
break;
}
}
} else if ( (tmp = src.match(/-webkit-gradient\(linear, (\d+)[%]{0,1} (\d+)[%]{0,1}, (\d+)[%]{0,1} (\d+)[%]{0,1}, from\((.*)\), to\((.*)\)\)/)) !== null ) {
p0 = (tmp[1] * bounds.width) / 100;
p1 = (tmp[2] * bounds.height) / 100;
p2 = (tmp[3] * bounds.width) / 100;
p3 = (tmp[4] * bounds.height) / 100;
steps.push(tmp[5]);
steps.push(tmp[6]);
} else if ( (tmp = src.match(/-moz-linear-gradient\((\d+)[%]{0,1} (\d+)[%]{0,1}, (.*)\)/)) !== null ) {
p0 = (tmp[1] * bounds.width) / 100;
p1 = (tmp[2] * bounds.width) / 100;
p2 = bounds.width - p0;
p3 = bounds.height - p1;
getColors( tmp[3] );
} else {
return;
}
lingrad = ctx.createLinearGradient( p0, p1, p2, p3 );
increment = 1 / (steps.length - 1);
for (i = 0, len = steps.length; i < len; i+=1) {
try {
lingrad.addColorStop(increment * i, steps[i]);
}
catch(e) {
h2clog(['failed to add color stop: ', e, '; tried to add: ', steps[i], '; stop: ', i, '; in: ', src]);
}
}
ctx.fillStyle = lingrad;
// draw shapes
ctx.fillRect(0, 0, bounds.width,bounds.height);
img = new Image();
img.src = canvas.toDataURL();
return img;
};
_html2canvas.Generate.ListAlpha = function(number) {
var tmp = "",
modulus;
do {
modulus = number % 26;
tmp = String.fromCharCode((modulus) + 64) + tmp;
number = number / 26;
}while((number*26) > 26);
return tmp;
};
_html2canvas.Generate.ListRoman = function(number) {
var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
roman = "",
v,
len = romanArray.length;
if (number <= 0 || number >= 4000) {
return number;
}
for (v=0; v < len; v+=1) {
while (number >= decimal[v]) {
number -= decimal[v];
roman += romanArray[v];
}
}
return roman;
};

View File

@@ -1,7 +0,0 @@
/**
@license html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/

File diff suppressed because it is too large Load Diff

View File

@@ -1,347 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Preload = function(element, options){
var images = {
numLoaded: 0, // also failed are counted here
numFailed: 0,
numTotal: 0,
cleanupDone: false
},
pageOrigin,
methods,
i,
count = 0,
doc = element.ownerDocument,
domImages = doc.images, // TODO probably should limit it to images present in the element only
imgLen = domImages.length,
link = doc.createElement("a"),
supportCORS = (function( img ){
return (img.crossOrigin !== undefined);
})(new Image()),
timeoutTimer;
link.href = window.location.href;
pageOrigin = link.protocol + link.host;
element = element || doc.body;
function isSameOrigin(url){
link.href = url;
link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
var origin = link.protocol + link.host;
return (origin === pageOrigin);
}
function start(){
h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")");
if (!images.firstRun && images.numLoaded >= images.numTotal){
/*
this.log('Finished loading '+this.imagesLoaded+' images, Started parsing');
this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow;
document.getElementsByTagName('body')[0].style.overflow = "hidden";
*/
if (typeof options.complete === "function"){
options.complete(images);
}
h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")");
}
}
// TODO modify proxy to serve images with CORS enabled, where available
function proxyGetImage(url, img, imageObj){
var callback_name,
scriptUrl = options.proxy,
script;
link.href = url;
url = link.href; // work around for pages with base href="" set - WARNING: this may change the url
callback_name = 'html2canvas_' + (count++);
imageObj.callbackname = callback_name;
if (scriptUrl.indexOf("?") > -1) {
scriptUrl += "&";
} else {
scriptUrl += "?";
}
scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name;
script = doc.createElement("script");
window[callback_name] = function(a){
if (a.substring(0,6) === "error:"){
imageObj.succeeded = false;
images.numLoaded++;
images.numFailed++;
start();
} else {
setImageLoadHandlers(img, imageObj);
img.src = a;
}
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)
try {
delete window[callback_name]; // for all browser that support this
} catch(ex) {}
script.parentNode.removeChild(script);
script = null;
delete imageObj.script;
delete imageObj.callbackname;
};
script.setAttribute("type", "text/javascript");
script.setAttribute("src", scriptUrl);
imageObj.script = script;
window.document.body.appendChild(script);
}
function getImages (el) {
// if (!this.ignoreRe.test(el.nodeName)){
//
var contents = _html2canvas.Util.Children(el),
i,
contentsLen = contents.length,
background_image,
src,
img,
elNodeType = false;
for (i = 0; i < contentsLen; i+=1 ){
// var ignRe = new RegExp("("+this.ignoreElements+")");
// if (!ignRe.test(element.nodeName)){
getImages(contents[i]);
// }
}
// }
try {
elNodeType = el.nodeType;
} catch (ex) {
elNodeType = false;
h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
}
if (elNodeType === 1 || elNodeType === undefined){
// opera throws exception on external-content.html
try {
background_image = _html2canvas.Util.getCSS(el, 'backgroundImage');
}catch(e) {
h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
}
if ( background_image && background_image !== "1" && background_image !== "none" ) {
// TODO add multi image background support
if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") {
img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
if ( img !== undefined ){
images[background_image] = {
img: img,
succeeded: true
};
images.numTotal++;
images.numLoaded++;
start();
}
} else {
src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]);
methods.loadImage(src);
}
/*
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"){
// TODO add multi image background support
src = html2canvas.Util.backgroundImage(background_image.split(",")[0]);
methods.loadImage(src); */
}
}
}
function setImageLoadHandlers(img, imageObj) {
img.onload = function() {
if ( imageObj.timer !== undefined ) {
// CORS succeeded
window.clearTimeout( imageObj.timer );
}
images.numLoaded++;
imageObj.succeeded = true;
start();
};
img.onerror = function() {
if (img.crossOrigin === "anonymous") {
// CORS failed
window.clearTimeout( imageObj.timer );
// let's try with proxy instead
if ( options.proxy ) {
var src = img.src;
img = new Image();
imageObj.img = img;
img.src = src;
proxyGetImage( img.src, img, imageObj );
return;
}
}
images.numLoaded++;
images.numFailed++;
imageObj.succeeded = false;
start();
};
}
methods = {
loadImage: function( src ) {
var img, imageObj;
if ( src && images[src] === undefined ) {
img = new Image();
if ( src.match(/data:image\/.*;base64,/i) ) {
img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
imageObj = images[src] = {
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
} else if ( isSameOrigin( src ) || options.allowTaint === true ) {
imageObj = images[src] = {
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src = src;
} else if ( supportCORS && !options.allowTaint && options.useCORS ) {
// attempt to load with CORS
img.crossOrigin = "anonymous";
imageObj = images[src] = {
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src = src;
// work around for https://bugs.webkit.org/show_bug.cgi?id=80028
img.customComplete = function () {
if (!this.img.complete) {
this.timer = window.setTimeout(this.img.customComplete, 100);
} else {
this.img.onerror();
}
}.bind(imageObj);
img.customComplete();
} else if ( options.proxy ) {
imageObj = images[src] = {
img: img
};
images.numTotal++;
proxyGetImage( src, img, imageObj );
}
}
},
cleanupDOM: function(cause) {
var img, src;
if (!images.cleanupDone) {
if (cause && typeof cause === "string") {
h2clog("html2canvas: Cleanup because: " + cause);
} else {
h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
}
for (src in images) {
if (images.hasOwnProperty(src)) {
img = images[src];
if (typeof img === "object" && img.callbackname && img.succeeded === undefined) {
// cancel proxy image request
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)
try {
delete window[img.callbackname]; // for all browser that support this
} catch(ex) {}
if (img.script && img.script.parentNode) {
img.script.setAttribute("src", "about:blank"); // try to cancel running request
img.script.parentNode.removeChild(img.script);
}
images.numLoaded++;
images.numFailed++;
h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);
}
}
}
// cancel any pending requests
if(window.stop !== undefined) {
window.stop();
} else if(document.execCommand !== undefined) {
document.execCommand("Stop", false);
}
if (document.close !== undefined) {
document.close();
}
images.cleanupDone = true;
if (!(cause && typeof cause === "string")) {
start();
}
}
},
renderingDone: function() {
if (timeoutTimer) {
window.clearTimeout(timeoutTimer);
}
}
};
if (options.timeout > 0) {
timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout);
}
h2clog('html2canvas: Preload starts: finding background-images');
images.firstRun = true;
getImages( element );
h2clog('html2canvas: Preload: Finding images');
// load <img> images
for (i = 0; i < imgLen; i+=1){
methods.loadImage( domImages[i].getAttribute( "src" ) );
}
images.firstRun = false;
h2clog('html2canvas: Preload: Done.');
if ( images.numTotal === images.numLoaded ) {
start();
}
return methods;
};

View File

@@ -1,43 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
function h2cRenderContext(width, height) {
var storage = [];
return {
storage: storage,
width: width,
height: height,
fillRect: function () {
storage.push({
type: "function",
name: "fillRect",
'arguments': arguments
});
},
drawImage: function () {
storage.push({
type: "function",
name: "drawImage",
'arguments': arguments
});
},
fillText: function () {
storage.push({
type: "function",
name: "fillText",
'arguments': arguments
});
},
setVariable: function (variable, value) {
storage.push({
type: "variable",
name: variable,
'arguments': value
});
}
};
}

View File

@@ -1,473 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Renderer = function(parseQueue, options){
var queue = [],
canvas,
usingFlashcanvas = false,
flashMaxSize = 2880, // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
doc = document;
function sortZ(zStack){
var subStacks = [],
stackValues = [],
zStackChildren = zStack.children,
s,
i,
stackLen,
zValue,
zLen,
stackChild,
b,
subStackLen;
for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){
stackChild = zStackChildren[s];
if (stackChild.children && stackChild.children.length > 0){
subStacks.push(stackChild);
stackValues.push(stackChild.zindex);
}else{
queue.push(stackChild);
}
}
stackValues.sort(function(a, b) {
return a - b;
});
for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){
zValue = stackValues[i];
for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){
if (subStacks[b].zindex === zValue){
stackChild = subStacks.splice(b, 1);
sortZ(stackChild[0]);
break;
}
}
}
}
function canvasRenderer(zStack){
sortZ(zStack.zIndex);
var ctx = canvas.getContext("2d"),
storageContext,
i,
queueLen,
a,
newCanvas,
bounds,
testCanvas = document.createElement("canvas"),
hasCTX = ( testCanvas.getContext !== undefined ),
storageLen,
renderItem,
testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {},
safeImages = [],
fstyle;
canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) );
canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) );
fstyle = ctx.fillStyle;
ctx.fillStyle = zStack.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle;
for (i = 0, queueLen = queue.length; i < queueLen; i+=1){
storageContext = queue.splice(0, 1)[0];
storageContext.canvasPosition = storageContext.canvasPosition || {};
//this.canvasRenderContext(storageContext,parentctx);
// set common settings for canvas
ctx.textBaseline = "bottom";
if (storageContext.clip){
ctx.save();
ctx.beginPath();
// console.log(storageContext);
ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
ctx.clip();
}
if (storageContext.ctx.storage){
for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
renderItem = storageContext.ctx.storage[a];
switch(renderItem.type){
case "variable":
ctx[renderItem.name] = renderItem['arguments'];
break;
case "function":
if (renderItem.name === "fillRect") {
if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) {
ctx.fillRect.apply( ctx, renderItem['arguments'] );
}
}else if(renderItem.name === "fillText") {
if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) {
ctx.fillText.apply( ctx, renderItem['arguments'] );
}
}else if(renderItem.name === "drawImage") {
if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
if ( hasCTX && options.taintTest ) {
if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) {
testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 );
try {
testctx.getImageData( 0, 0, 1, 1 );
} catch(e) {
testCanvas = document.createElement("canvas");
testctx = testCanvas.getContext("2d");
continue;
}
safeImages.push( renderItem['arguments'][ 0 ].src );
}
}
ctx.drawImage.apply( ctx, renderItem['arguments'] );
}
}
break;
default:
}
}
}
if (storageContext.clip){
ctx.restore();
}
}
h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
// this.canvasRenderStorage(queue,this.ctx);
queueLen = options.elements.length;
if (queueLen === 1) {
if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) {
// crop image to the bounds of selected (single) element
bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] );
newCanvas = doc.createElement('canvas');
newCanvas.width = bounds.width;
newCanvas.height = bounds.height;
ctx = newCanvas.getContext("2d");
ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height );
canvas = null;
return newCanvas;
}
} /*else {
// TODO clip and resize multiple elements
for ( i = 0; i < queueLen; i+=1 ) {
if (options.elements[ i ] instanceof Element) {
}
}
}
*/
return canvas;
}
function svgRenderer(zStack){
sortZ(zStack.zIndex);
var svgNS = "http://www.w3.org/2000/svg",
svg = doc.createElementNS(svgNS, "svg"),
xlinkNS = "http://www.w3.org/1999/xlink",
defs = doc.createElementNS(svgNS, "defs"),
i,
a,
queueLen,
storageLen,
storageContext,
renderItem,
el,
settings = {},
text,
fontStyle,
clipId = 0;
svg.setAttribute("version", "1.1");
svg.setAttribute("baseProfile", "full");
svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height));
svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px");
svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px");
svg.setAttribute("preserveAspectRatio", "none");
svg.appendChild(defs);
for (i = 0, queueLen = queue.length; i < queueLen; i+=1){
storageContext = queue.splice(0, 1)[0];
storageContext.canvasPosition = storageContext.canvasPosition || {};
//this.canvasRenderContext(storageContext,parentctx);
/*
if (storageContext.clip){
ctx.save();
ctx.beginPath();
// console.log(storageContext);
ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
ctx.clip();
}*/
if (storageContext.ctx.storage){
for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
renderItem = storageContext.ctx.storage[a];
switch(renderItem.type){
case "variable":
settings[renderItem.name] = renderItem['arguments'];
break;
case "function":
if (renderItem.name === "fillRect") {
el = doc.createElementNS(svgNS, "rect");
el.setAttribute("x", renderItem['arguments'][0]);
el.setAttribute("y", renderItem['arguments'][1]);
el.setAttribute("width", renderItem['arguments'][2]);
el.setAttribute("height", renderItem['arguments'][3]);
el.setAttribute("fill", settings.fillStyle);
svg.appendChild(el);
} else if(renderItem.name === "fillText") {
el = doc.createElementNS(svgNS, "text");
fontStyle = settings.font.split(" ");
el.style.fontVariant = fontStyle.splice(0, 1)[0];
el.style.fontWeight = fontStyle.splice(0, 1)[0];
el.style.fontStyle = fontStyle.splice(0, 1)[0];
el.style.fontSize = fontStyle.splice(0, 1)[0];
el.setAttribute("x", renderItem['arguments'][1]);
el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3));
el.setAttribute("fill", settings.fillStyle);
// TODO get proper baseline
el.style.dominantBaseline = "text-before-edge";
el.style.fontFamily = fontStyle.join(" ");
text = doc.createTextNode(renderItem['arguments'][0]);
el.appendChild(text);
svg.appendChild(el);
} else if(renderItem.name === "drawImage") {
if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){
// TODO check whether even any clipping is necessary for this particular image
el = doc.createElementNS(svgNS, "clipPath");
el.setAttribute("id", "clipId" + clipId);
text = doc.createElementNS(svgNS, "rect");
text.setAttribute("x", renderItem['arguments'][5] );
text.setAttribute("y", renderItem['arguments'][6]);
text.setAttribute("width", renderItem['arguments'][3]);
text.setAttribute("height", renderItem['arguments'][4]);
el.appendChild(text);
defs.appendChild(el);
el = doc.createElementNS(svgNS, "image");
el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src);
el.setAttribute("width", renderItem['arguments'][0].width);
el.setAttribute("height", renderItem['arguments'][0].height);
el.setAttribute("x", renderItem['arguments'][5] - renderItem['arguments'][1]);
el.setAttribute("y", renderItem['arguments'][6] - renderItem['arguments'][2]);
el.setAttribute("clip-path", "url(#clipId" + clipId + ")");
// el.setAttribute("xlink:href", );
el.setAttribute("preserveAspectRatio", "none");
svg.appendChild(el);
clipId += 1;
/*
ctx.drawImage(
renderItem['arguments'][0],
renderItem['arguments'][1],
renderItem['arguments'][2],
renderItem['arguments'][3],
renderItem['arguments'][4],
renderItem['arguments'][5],
renderItem['arguments'][6],
renderItem['arguments'][7],
renderItem['arguments'][8]
);
*/
}
}
break;
default:
}
}
}
/*
if (storageContext.clip){
ctx.restore();
}
*/
}
h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj");
return svg;
}
//this.each(this.opts.renderOrder.split(" "),function(i,renderer){
//options.renderer = "svg";
switch(options.renderer.toLowerCase()){
case "canvas":
canvas = doc.createElement('canvas');
if (canvas.getContext){
h2clog("html2canvas: Renderer: using canvas renderer");
return canvasRenderer(parseQueue);
} else {
usingFlashcanvas = true;
h2clog("html2canvas: Renderer: canvas not available, using flashcanvas");
var script = doc.createElement("script");
script.src = options.flashcanvas;
script.onload = (function(script, func){
var intervalFunc;
if (script.onload === undefined) {
// IE lack of support for script onload
if( script.onreadystatechange !== undefined ) {
intervalFunc = function() {
if (script.readyState !== "loaded" && script.readyState !== "complete") {
window.setTimeout( intervalFunc, 250 );
} else {
// it is loaded
func();
}
};
window.setTimeout( intervalFunc, 250 );
} else {
h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded");
}
} else {
return func;
}
})(script, function(){
if (typeof window.FlashCanvas !== "undefined") {
h2clog("html2canvas: Renderer: Flashcanvas initialized");
window.FlashCanvas.initElement( canvas );
canvasRenderer(parseQueue);
}
});
doc.body.appendChild( script );
return canvas;
}
break;
case "svg":
if (doc.createElementNS){
h2clog("html2canvas: Renderer: using SVG renderer");
return svgRenderer(parseQueue);
}
break;
}
//});
return this;
};

View File

@@ -1,82 +0,0 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
html2canvas = function( elements, opts ) {
var queue,
canvas,
options = {
// general
logging: false,
// preload options
proxy: "http://html2canvas.appspot.com/",
timeout: 0, // no timeout
useCORS: false, // try to load images as CORS (where available), before falling back to proxy
allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true
// parse options
iframeDefault: "default",
ignoreElements: "IFRAME|OBJECT|PARAM",
useOverflow: true,
letterRendering: false,
// render options
width: null,
height: null,
renderer: "canvas",
taintTest: true // do a taint test with all images before applying to canvas
};
options = _html2canvas.Util.Extend(opts, options);
_html2canvas.logging = options.logging;
options.complete = function( images ) {
if (typeof options.onpreloaded === "function") {
if ( options.onpreloaded( images ) === false ) {
return;
}
}
queue = _html2canvas.Parse( elements, images, options);
if (typeof options.onparsed === "function") {
if ( options.onparsed( queue ) === false ) {
return;
}
}
canvas = _html2canvas.Renderer(queue, options);
if (typeof options.onrendered === "function") {
options.onrendered( canvas );
}
};
// for pages without images, we still want this to be async, i.e. return methods before executing
window.setTimeout( function(){
_html2canvas.Preload( elements, options );
}, 0 );
return {
render: function( queue, opts ) {
return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
},
parse: function( elements, images, opts ) {
return _html2canvas.Parse( elements, images, _html2canvas.Util.Extend(opts, options) );
},
preload: function( elements, opts ) {
return _html2canvas.Preload( elements, _html2canvas.Util.Extend(opts, options) );
},
log: h2clog
};
};

104
src/clone.js Normal file
View File

@@ -0,0 +1,104 @@
var log = require('./log');
function restoreOwnerScroll(ownerDocument, x, y) {
if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
ownerDocument.defaultView.scrollTo(x, y);
}
}
function cloneCanvasContents(canvas, clonedCanvas) {
try {
if (clonedCanvas) {
clonedCanvas.width = canvas.width;
clonedCanvas.height = canvas.height;
clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0);
}
} catch(e) {
log("Unable to copy canvas content from", canvas, e);
}
}
function cloneNode(node, javascriptEnabled) {
var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
var child = node.firstChild;
while(child) {
if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
clone.appendChild(cloneNode(child, javascriptEnabled));
}
child = child.nextSibling;
}
if (node.nodeType === 1) {
clone._scrollTop = node.scrollTop;
clone._scrollLeft = node.scrollLeft;
if (node.nodeName === "CANVAS") {
cloneCanvasContents(node, clone);
} else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") {
clone.value = node.value;
}
}
return clone;
}
function initNode(node) {
if (node.nodeType === 1) {
node.scrollTop = node._scrollTop;
node.scrollLeft = node._scrollLeft;
var child = node.firstChild;
while(child) {
initNode(child);
child = child.nextSibling;
}
}
}
module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) {
var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled);
var container = containerDocument.createElement("iframe");
container.className = "html2canvas-container";
container.style.visibility = "hidden";
container.style.position = "fixed";
container.style.left = "-10000px";
container.style.top = "0px";
container.style.border = "0";
container.width = width;
container.height = height;
container.scrolling = "no"; // ios won't scroll without it
containerDocument.body.appendChild(container);
return new Promise(function(resolve) {
var documentClone = container.contentWindow.document;
/* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
if window url is about:blank, we can assign the url to current by writing onto the document
*/
container.contentWindow.onload = container.onload = function() {
var interval = setInterval(function() {
if (documentClone.body.childNodes.length > 0) {
initNode(documentClone.documentElement);
clearInterval(interval);
if (options.type === "view") {
container.contentWindow.scrollTo(x, y);
if ((/(iPad|iPhone|iPod)/g).test(navigator.userAgent) && (container.contentWindow.scrollY !== y || container.contentWindow.scrollX !== x)) {
documentClone.documentElement.style.top = (-y) + "px";
documentClone.documentElement.style.left = (-x) + "px";
documentClone.documentElement.style.position = 'absolute';
}
}
resolve(container);
}
}, 50);
};
documentClone.open();
documentClone.write("<!DOCTYPE html><html></html>");
// Chrome scrolls the parent document for some reason after the write to the cloned window???
restoreOwnerScroll(ownerDocument, x, y);
documentClone.replaceChild(documentClone.adoptNode(documentElement), documentClone.documentElement);
documentClone.close();
});
};

271
src/color.js Normal file
View File

@@ -0,0 +1,271 @@
// http://dev.w3.org/csswg/css-color/
function Color(value) {
this.r = 0;
this.g = 0;
this.b = 0;
this.a = null;
var result = this.fromArray(value) ||
this.namedColor(value) ||
this.rgb(value) ||
this.rgba(value) ||
this.hex6(value) ||
this.hex3(value);
}
Color.prototype.darken = function(amount) {
var a = 1 - amount;
return new Color([
Math.round(this.r * a),
Math.round(this.g * a),
Math.round(this.b * a),
this.a
]);
};
Color.prototype.isTransparent = function() {
return this.a === 0;
};
Color.prototype.isBlack = function() {
return this.r === 0 && this.g === 0 && this.b === 0;
};
Color.prototype.fromArray = function(array) {
if (Array.isArray(array)) {
this.r = Math.min(array[0], 255);
this.g = Math.min(array[1], 255);
this.b = Math.min(array[2], 255);
if (array.length > 3) {
this.a = array[3];
}
}
return (Array.isArray(array));
};
var _hex3 = /^#([a-f0-9]{3})$/i;
Color.prototype.hex3 = function(value) {
var match = null;
if ((match = value.match(_hex3)) !== null) {
this.r = parseInt(match[1][0] + match[1][0], 16);
this.g = parseInt(match[1][1] + match[1][1], 16);
this.b = parseInt(match[1][2] + match[1][2], 16);
}
return match !== null;
};
var _hex6 = /^#([a-f0-9]{6})$/i;
Color.prototype.hex6 = function(value) {
var match = null;
if ((match = value.match(_hex6)) !== null) {
this.r = parseInt(match[1].substring(0, 2), 16);
this.g = parseInt(match[1].substring(2, 4), 16);
this.b = parseInt(match[1].substring(4, 6), 16);
}
return match !== null;
};
var _rgb = /^rgb\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3})\)$/;
Color.prototype.rgb = function(value) {
var match = null;
if ((match = value.match(_rgb)) !== null) {
this.r = Number(match[1]);
this.g = Number(match[2]);
this.b = Number(match[3]);
}
return match !== null;
};
var _rgba = /^rgba\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *, *(\d+\.?\d*)\)$/;
Color.prototype.rgba = function(value) {
var match = null;
if ((match = value.match(_rgba)) !== null) {
this.r = Number(match[1]);
this.g = Number(match[2]);
this.b = Number(match[3]);
this.a = Number(match[4]);
}
return match !== null;
};
Color.prototype.toString = function() {
return this.a !== null && this.a !== 1 ?
"rgba(" + [this.r, this.g, this.b, this.a].join(",") + ")" :
"rgb(" + [this.r, this.g, this.b].join(",") + ")";
};
Color.prototype.namedColor = function(value) {
var color = colors[value.toLowerCase()];
if (color) {
this.r = color[0];
this.g = color[1];
this.b = color[2];
} else if (value.toLowerCase() === "transparent") {
this.r = this.g = this.b = this.a = 0;
return true;
}
return !!color;
};
Color.prototype.isColor = true;
// JSON.stringify([].slice.call($$('.named-color-table tr'), 1).map(function(row) { return [row.childNodes[3].textContent, row.childNodes[5].textContent.trim().split(",").map(Number)] }).reduce(function(data, row) {data[row[0]] = row[1]; return data}, {}))
var colors = {
"aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255],
"aquamarine": [127, 255, 212],
"azure": [240, 255, 255],
"beige": [245, 245, 220],
"bisque": [255, 228, 196],
"black": [0, 0, 0],
"blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255],
"blueviolet": [138, 43, 226],
"brown": [165, 42, 42],
"burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30],
"coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220],
"crimson": [220, 20, 60],
"cyan": [0, 255, 255],
"darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204],
"darkred": [139, 0, 0],
"darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255],
"gold": [255, 215, 0],
"goldenrod": [218, 165, 32],
"gray": [128, 128, 128],
"green": [0, 128, 0],
"greenyellow": [173, 255, 47],
"grey": [128, 128, 128],
"honeydew": [240, 255, 240],
"hotpink": [255, 105, 180],
"indianred": [205, 92, 92],
"indigo": [75, 0, 130],
"ivory": [255, 255, 240],
"khaki": [240, 230, 140],
"lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224],
"lime": [0, 255, 0],
"limegreen": [50, 205, 50],
"linen": [250, 240, 230],
"magenta": [255, 0, 255],
"maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173],
"navy": [0, 0, 128],
"oldlace": [253, 245, 230],
"olive": [128, 128, 0],
"olivedrab": [107, 142, 35],
"orange": [255, 165, 0],
"orangered": [255, 69, 0],
"orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185],
"peru": [205, 133, 63],
"pink": [255, 192, 203],
"plum": [221, 160, 221],
"powderblue": [176, 224, 230],
"purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153],
"red": [255, 0, 0],
"rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114],
"sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87],
"seashell": [255, 245, 238],
"sienna": [160, 82, 45],
"silver": [192, 192, 192],
"skyblue": [135, 206, 235],
"slateblue": [106, 90, 205],
"slategray": [112, 128, 144],
"slategrey": [112, 128, 144],
"snow": [255, 250, 250],
"springgreen": [0, 255, 127],
"steelblue": [70, 130, 180],
"tan": [210, 180, 140],
"teal": [0, 128, 128],
"thistle": [216, 191, 216],
"tomato": [255, 99, 71],
"turquoise": [64, 224, 208],
"violet": [238, 130, 238],
"wheat": [245, 222, 179],
"white": [255, 255, 255],
"whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};
module.exports = Color;

153
src/core.js Normal file
View File

@@ -0,0 +1,153 @@
var Support = require('./support');
var CanvasRenderer = require('./renderers/canvas');
var ImageLoader = require('./imageloader');
var NodeParser = require('./nodeparser');
var NodeContainer = require('./nodecontainer');
var log = require('./log');
var utils = require('./utils');
var createWindowClone = require('./clone');
var loadUrlDocument = require('./proxy').loadUrlDocument;
var getBounds = utils.getBounds;
var html2canvasNodeAttribute = "data-html2canvas-node";
var html2canvasCloneIndex = 0;
function html2canvas(nodeList, options) {
var index = html2canvasCloneIndex++;
options = options || {};
if (options.logging) {
window.html2canvas.logging = true;
window.html2canvas.start = Date.now();
}
options.async = typeof(options.async) === "undefined" ? true : options.async;
options.allowTaint = typeof(options.allowTaint) === "undefined" ? false : options.allowTaint;
options.removeContainer = typeof(options.removeContainer) === "undefined" ? true : options.removeContainer;
options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled;
options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout;
options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer;
options.strict = !!options.strict;
if (typeof(nodeList) === "string") {
if (typeof(options.proxy) !== "string") {
return Promise.reject("Proxy must be used when rendering url");
}
var width = options.width != null ? options.width : window.innerWidth;
var height = options.height != null ? options.height : window.innerHeight;
return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) {
return renderWindow(container.contentWindow.document.documentElement, container, options, width, height);
});
}
var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0];
node.setAttribute(html2canvasNodeAttribute + index, index);
return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {
if (typeof(options.onrendered) === "function") {
log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
options.onrendered(canvas);
}
return canvas;
});
}
html2canvas.CanvasRenderer = CanvasRenderer;
html2canvas.NodeContainer = NodeContainer;
html2canvas.log = log;
html2canvas.utils = utils;
var html2canvasExport = (typeof(document) === "undefined" || typeof(Object.create) !== "function" || typeof(document.createElement("canvas").getContext) !== "function") ? function() {
return Promise.reject("No canvas support");
} : html2canvas;
module.exports = html2canvasExport;
if (typeof(define) === 'function' && define.amd) {
define('html2canvas', [], function() {
return html2canvasExport;
});
}
function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) {
return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) {
log("Document cloned");
var attributeName = html2canvasNodeAttribute + html2canvasIndex;
var selector = "[" + attributeName + "='" + html2canvasIndex + "']";
document.querySelector(selector).removeAttribute(attributeName);
var clonedWindow = container.contentWindow;
var node = clonedWindow.document.querySelector(selector);
var oncloneHandler = (typeof(options.onclone) === "function") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true);
return oncloneHandler.then(function() {
return renderWindow(node, container, options, windowWidth, windowHeight);
});
});
}
function renderWindow(node, container, options, windowWidth, windowHeight) {
var clonedWindow = container.contentWindow;
var support = new Support(clonedWindow.document);
var imageLoader = new ImageLoader(options, support);
var bounds = getBounds(node);
var width = options.type === "view" ? windowWidth : documentWidth(clonedWindow.document);
var height = options.type === "view" ? windowHeight : documentHeight(clonedWindow.document);
var renderer = new options.renderer(width, height, imageLoader, options, document);
var parser = new NodeParser(node, renderer, support, imageLoader, options);
return parser.ready.then(function() {
log("Finished rendering");
var canvas;
if (options.type === "view") {
canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});
} else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) {
canvas = renderer.canvas;
} else {
canvas = crop(renderer.canvas, {width: options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: clonedWindow.pageXOffset, y: clonedWindow.pageYOffset});
}
cleanupContainer(container, options);
return canvas;
});
}
function cleanupContainer(container, options) {
if (options.removeContainer) {
container.parentNode.removeChild(container);
log("Cleaned up container");
}
}
function crop(canvas, bounds) {
var croppedCanvas = document.createElement("canvas");
var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left));
var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width));
var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top));
var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height));
croppedCanvas.width = bounds.width;
croppedCanvas.height = bounds.height;
log("Cropping canvas at:", "left:", bounds.left, "top:", bounds.top, "width:", (x2-x1), "height:", (y2-y1));
log("Resulting crop with width", bounds.width, "and height", bounds.height, " with x", x1, "and y", y1);
croppedCanvas.getContext("2d").drawImage(canvas, x1, y1, x2-x1, y2-y1, bounds.x, bounds.y, x2-x1, y2-y1);
return croppedCanvas;
}
function documentWidth (doc) {
return Math.max(
Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
);
}
function documentHeight (doc) {
return Math.max(
Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
);
}
function absoluteUrl(url) {
var link = document.createElement("a");
link.href = url;
link.href = link.href;
return link;
}

View File

@@ -0,0 +1,22 @@
var log = require('./log');
var smallImage = require('./utils').smallImage;
function DummyImageContainer(src) {
this.src = src;
log("DummyImageContainer for", src);
if (!this.promise || !this.image) {
log("Initiating DummyImageContainer");
DummyImageContainer.prototype.image = new Image();
var image = this.image;
DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) {
image.onload = resolve;
image.onerror = reject;
image.src = smallImage();
if (image.complete === true) {
resolve(image);
}
});
}
}
module.exports = DummyImageContainer;

1
src/fabric Submodule

Submodule src/fabric added at 791c74a82e

52
src/font.js Normal file
View File

@@ -0,0 +1,52 @@
var smallImage = require('./utils').smallImage;
function Font(family, size) {
var container = document.createElement('div'),
img = document.createElement('img'),
span = document.createElement('span'),
sampleText = 'Hidden Text',
baseline,
middle;
container.style.visibility = "hidden";
container.style.fontFamily = family;
container.style.fontSize = size;
container.style.margin = 0;
container.style.padding = 0;
document.body.appendChild(container);
img.src = smallImage();
img.width = 1;
img.height = 1;
img.style.margin = 0;
img.style.padding = 0;
img.style.verticalAlign = "baseline";
span.style.fontFamily = family;
span.style.fontSize = size;
span.style.margin = 0;
span.style.padding = 0;
span.appendChild(document.createTextNode(sampleText));
container.appendChild(span);
container.appendChild(img);
baseline = (img.offsetTop - span.offsetTop) + 1;
container.removeChild(span);
container.appendChild(document.createTextNode(sampleText));
container.style.lineHeight = "normal";
img.style.verticalAlign = "super";
middle = (img.offsetTop-container.offsetTop) + 1;
document.body.removeChild(container);
this.baseline = baseline;
this.lineWidth = 1;
this.middle = middle;
}
module.exports = Font;

14
src/fontmetrics.js Normal file
View File

@@ -0,0 +1,14 @@
var Font = require('./font');
function FontMetrics() {
this.data = {};
}
FontMetrics.prototype.getMetrics = function(family, size) {
if (this.data[family + "-" + size] === undefined) {
this.data[family + "-" + size] = new Font(family, size);
}
return this.data[family + "-" + size];
};
module.exports = FontMetrics;

31
src/framecontainer.js Normal file
View File

@@ -0,0 +1,31 @@
var utils = require('./utils');
var getBounds = utils.getBounds;
var loadUrlDocument = require('./proxy').loadUrlDocument;
function FrameContainer(container, sameOrigin, options) {
this.image = null;
this.src = container;
var self = this;
var bounds = getBounds(container);
this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) {
if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
container.contentWindow.onload = container.onload = function() {
resolve(container);
};
} else {
resolve(container);
}
})).then(function(container) {
var html2canvas = require('./core');
return html2canvas(container.contentWindow.document.documentElement, {type: 'view', width: container.width, height: container.height, proxy: options.proxy, javascriptEnabled: options.javascriptEnabled, removeContainer: options.removeContainer, allowTaint: options.allowTaint, imageTimeout: options.imageTimeout / 2});
}).then(function(canvas) {
return self.image = canvas;
});
}
FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) {
var container = this.src;
return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options);
};
module.exports = FrameContainer;

17
src/gradientcontainer.js Normal file
View File

@@ -0,0 +1,17 @@
function GradientContainer(imageData) {
this.src = imageData.value;
this.colorStops = [];
this.type = null;
this.x0 = 0.5;
this.y0 = 0.5;
this.x1 = 0.5;
this.y1 = 0.5;
this.promise = Promise.resolve(true);
}
GradientContainer.prototype.TYPES = {
LINEAR: 1,
RADIAL: 2
};
module.exports = GradientContainer;

View File

@@ -1,2 +0,0 @@
window.html2canvas = html2canvas;
}(window, document));

View File

@@ -1 +0,0 @@
(function(window, document, undefined){

19
src/imagecontainer.js Normal file
View File

@@ -0,0 +1,19 @@
function ImageContainer(src, cors) {
this.src = src;
this.image = new Image();
var self = this;
this.tainted = null;
this.promise = new Promise(function(resolve, reject) {
self.image.onload = resolve;
self.image.onerror = reject;
if (cors) {
self.image.crossOrigin = "anonymous";
}
self.image.src = src;
if (self.image.complete === true) {
resolve(self.image);
}
});
}
module.exports = ImageContainer;

157
src/imageloader.js Normal file
View File

@@ -0,0 +1,157 @@
var log = require('./log');
var ImageContainer = require('./imagecontainer');
var DummyImageContainer = require('./dummyimagecontainer');
var ProxyImageContainer = require('./proxyimagecontainer');
var FrameContainer = require('./framecontainer');
var SVGContainer = require('./svgcontainer');
var SVGNodeContainer = require('./svgnodecontainer');
var LinearGradientContainer = require('./lineargradientcontainer');
var WebkitGradientContainer = require('./webkitgradientcontainer');
var bind = require('./utils').bind;
function ImageLoader(options, support) {
this.link = null;
this.options = options;
this.support = support;
this.origin = this.getOrigin(window.location.href);
}
ImageLoader.prototype.findImages = function(nodes) {
var images = [];
nodes.reduce(function(imageNodes, container) {
switch(container.node.nodeName) {
case "IMG":
return imageNodes.concat([{
args: [container.node.src],
method: "url"
}]);
case "svg":
case "IFRAME":
return imageNodes.concat([{
args: [container.node],
method: container.node.nodeName
}]);
}
return imageNodes;
}, []).forEach(this.addImage(images, this.loadImage), this);
return images;
};
ImageLoader.prototype.findBackgroundImage = function(images, container) {
container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this);
return images;
};
ImageLoader.prototype.addImage = function(images, callback) {
return function(newImage) {
newImage.args.forEach(function(image) {
if (!this.imageExists(images, image)) {
images.splice(0, 0, callback.call(this, newImage));
log('Added image #' + (images.length), typeof(image) === "string" ? image.substring(0, 100) : image);
}
}, this);
};
};
ImageLoader.prototype.hasImageBackground = function(imageData) {
return imageData.method !== "none";
};
ImageLoader.prototype.loadImage = function(imageData) {
if (imageData.method === "url") {
var src = imageData.args[0];
if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) {
return new SVGContainer(src);
} else if (src.match(/data:image\/.*;base64,/i)) {
return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
} else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) {
return new ImageContainer(src, false);
} else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
return new ImageContainer(src, true);
} else if (this.options.proxy) {
return new ProxyImageContainer(src, this.options.proxy);
} else {
return new DummyImageContainer(src);
}
} else if (imageData.method === "linear-gradient") {
return new LinearGradientContainer(imageData);
} else if (imageData.method === "gradient") {
return new WebkitGradientContainer(imageData);
} else if (imageData.method === "svg") {
return new SVGNodeContainer(imageData.args[0], this.support.svg);
} else if (imageData.method === "IFRAME") {
return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options);
} else {
return new DummyImageContainer(imageData);
}
};
ImageLoader.prototype.isSVG = function(src) {
return src.substring(src.length - 3).toLowerCase() === "svg" || SVGContainer.prototype.isInline(src);
};
ImageLoader.prototype.imageExists = function(images, src) {
return images.some(function(image) {
return image.src === src;
});
};
ImageLoader.prototype.isSameOrigin = function(url) {
return (this.getOrigin(url) === this.origin);
};
ImageLoader.prototype.getOrigin = function(url) {
var link = this.link || (this.link = document.createElement("a"));
link.href = url;
link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
return link.protocol + link.hostname + link.port;
};
ImageLoader.prototype.getPromise = function(container) {
return this.timeout(container, this.options.imageTimeout)['catch'](function() {
var dummy = new DummyImageContainer(container.src);
return dummy.promise.then(function(image) {
container.image = image;
});
});
};
ImageLoader.prototype.get = function(src) {
var found = null;
return this.images.some(function(img) {
return (found = img).src === src;
}) ? found : null;
};
ImageLoader.prototype.fetch = function(nodes) {
this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes));
this.images.forEach(function(image, index) {
image.promise.then(function() {
log("Succesfully loaded image #"+ (index+1), image);
}, function(e) {
log("Failed loading image #"+ (index+1), image, e);
});
});
this.ready = Promise.all(this.images.map(this.getPromise, this));
log("Finished searching images");
return this;
};
ImageLoader.prototype.timeout = function(container, timeout) {
var timer;
var promise = Promise.race([container.promise, new Promise(function(res, reject) {
timer = setTimeout(function() {
log("Timed out loading image", container);
reject(container);
}, timeout);
})]).then(function(container) {
clearTimeout(timer);
return container;
});
promise['catch'](function() {
clearTimeout(timer);
});
return promise;
};
module.exports = ImageLoader;

View File

@@ -0,0 +1,82 @@
var GradientContainer = require('./gradientcontainer');
var Color = require('./color');
var COLOR_STOP_REGEXP = /^\s*(.*)\s*(\d{1,3})?(%|px)?$/;
function LinearGradientContainer(imageData) {
GradientContainer.apply(this, arguments);
this.type = this.TYPES.LINEAR;
var hasDirection = imageData.args[0].match(this.stepRegExp) === null;
if (hasDirection) {
imageData.args[0].split(" ").reverse().forEach(function(position) {
switch(position) {
case "left":
this.x0 = 0;
this.x1 = 1;
break;
case "top":
this.y0 = 0;
this.y1 = 1;
break;
case "right":
this.x0 = 1;
this.x1 = 0;
break;
case "bottom":
this.y0 = 1;
this.y1 = 0;
break;
case "to":
var y0 = this.y0;
var x0 = this.x0;
this.y0 = this.y1;
this.x0 = this.x1;
this.x1 = x0;
this.y1 = y0;
break;
}
}, this);
} else {
this.y0 = 0;
this.y1 = 1;
}
this.colorStops = imageData.args.slice(hasDirection ? 1 : 0)
.map(function(colorStop) { return colorStop.match(COLOR_STOP_REGEXP);})
.filter(function(colorStopMatch) { return !!colorStopMatch;})
.map(function(colorStopMatch) {
return {
color: new Color(colorStopMatch[1]),
stop: colorStopMatch[3] === "%" ? colorStopMatch[2] / 100 : null
};
});
if (this.colorStops[0].stop === null) {
this.colorStops[0].stop = 0;
}
if (this.colorStops[this.colorStops.length - 1].stop === null) {
this.colorStops[this.colorStops.length - 1].stop = 1;
}
this.colorStops.forEach(function(colorStop, index) {
if (colorStop.stop === null) {
this.colorStops.slice(index).some(function(find, count) {
if (find.stop !== null) {
colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop;
return true;
} else {
return false;
}
}, this);
}
}, this);
}
LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);
LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/;
module.exports = LinearGradientContainer;

5
src/log.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = function() {
if (window.html2canvas.logging && window.console && window.console.log) {
Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
}
};

296
src/nodecontainer.js Normal file
View File

@@ -0,0 +1,296 @@
var Color = require('./color');
var utils = require('./utils');
var getBounds = utils.getBounds;
var parseBackgrounds = utils.parseBackgrounds;
var offsetBounds = utils.offsetBounds;
function NodeContainer(node, parent) {
this.node = node;
this.parent = parent;
this.stack = null;
this.bounds = null;
this.borders = null;
this.clip = [];
this.backgroundClip = [];
this.offsetBounds = null;
this.visible = null;
this.computedStyles = null;
this.colors = {};
this.styles = {};
this.backgroundImages = null;
this.transformData = null;
this.transformMatrix = null;
this.isPseudoElement = false;
this.opacity = null;
}
NodeContainer.prototype.cloneTo = function(stack) {
stack.visible = this.visible;
stack.borders = this.borders;
stack.bounds = this.bounds;
stack.clip = this.clip;
stack.backgroundClip = this.backgroundClip;
stack.computedStyles = this.computedStyles;
stack.styles = this.styles;
stack.backgroundImages = this.backgroundImages;
stack.opacity = this.opacity;
};
NodeContainer.prototype.getOpacity = function() {
return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity;
};
NodeContainer.prototype.assignStack = function(stack) {
this.stack = stack;
stack.children.push(this);
};
NodeContainer.prototype.isElementVisible = function() {
return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : (
this.css('display') !== "none" &&
this.css('visibility') !== "hidden" &&
!this.node.hasAttribute("data-html2canvas-ignore") &&
(this.node.nodeName !== "INPUT" || this.node.getAttribute("type") !== "hidden")
);
};
NodeContainer.prototype.css = function(attribute) {
if (!this.computedStyles) {
this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? ":before" : ":after") : this.computedStyle(null);
}
return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]);
};
NodeContainer.prototype.prefixedCss = function(attribute) {
var prefixes = ["webkit", "moz", "ms", "o"];
var value = this.css(attribute);
if (value === undefined) {
prefixes.some(function(prefix) {
value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1));
return value !== undefined;
}, this);
}
return value === undefined ? null : value;
};
NodeContainer.prototype.computedStyle = function(type) {
return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type);
};
NodeContainer.prototype.cssInt = function(attribute) {
var value = parseInt(this.css(attribute), 10);
return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html
};
NodeContainer.prototype.color = function(attribute) {
return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute)));
};
NodeContainer.prototype.cssFloat = function(attribute) {
var value = parseFloat(this.css(attribute));
return (isNaN(value)) ? 0 : value;
};
NodeContainer.prototype.fontWeight = function() {
var weight = this.css("fontWeight");
switch(parseInt(weight, 10)){
case 401:
weight = "bold";
break;
case 400:
weight = "normal";
break;
}
return weight;
};
NodeContainer.prototype.parseClip = function() {
var matches = this.css('clip').match(this.CLIP);
if (matches) {
return {
top: parseInt(matches[1], 10),
right: parseInt(matches[2], 10),
bottom: parseInt(matches[3], 10),
left: parseInt(matches[4], 10)
};
}
return null;
};
NodeContainer.prototype.parseBackgroundImages = function() {
return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css("backgroundImage")));
};
NodeContainer.prototype.cssList = function(property, index) {
var value = (this.css(property) || '').split(',');
value = value[index || 0] || value[0] || 'auto';
value = value.trim().split(' ');
if (value.length === 1) {
value = [value[0], isPercentage(value[0]) ? 'auto' : value[0]];
}
return value;
};
NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) {
var size = this.cssList("backgroundSize", index);
var width, height;
if (isPercentage(size[0])) {
width = bounds.width * parseFloat(size[0]) / 100;
} else if (/contain|cover/.test(size[0])) {
var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height;
return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio};
} else {
width = parseInt(size[0], 10);
}
if (size[0] === 'auto' && size[1] === 'auto') {
height = image.height;
} else if (size[1] === 'auto') {
height = width / image.width * image.height;
} else if (isPercentage(size[1])) {
height = bounds.height * parseFloat(size[1]) / 100;
} else {
height = parseInt(size[1], 10);
}
if (size[0] === 'auto') {
width = height / image.height * image.width;
}
return {width: width, height: height};
};
NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) {
var position = this.cssList('backgroundPosition', index);
var left, top;
if (isPercentage(position[0])){
left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100);
} else {
left = parseInt(position[0], 10);
}
if (position[1] === 'auto') {
top = left / image.width * image.height;
} else if (isPercentage(position[1])){
top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100;
} else {
top = parseInt(position[1], 10);
}
if (position[0] === 'auto') {
left = top / image.height * image.width;
}
return {left: left, top: top};
};
NodeContainer.prototype.parseBackgroundRepeat = function(index) {
return this.cssList("backgroundRepeat", index)[0];
};
NodeContainer.prototype.parseTextShadows = function() {
var textShadow = this.css("textShadow");
var results = [];
if (textShadow && textShadow !== 'none') {
var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY);
for (var i = 0; shadows && (i < shadows.length); i++) {
var s = shadows[i].match(this.TEXT_SHADOW_VALUES);
results.push({
color: new Color(s[0]),
offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0,
offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0,
blur: s[3] ? s[3].replace('px', '') : 0
});
}
}
return results;
};
NodeContainer.prototype.parseTransform = function() {
if (!this.transformData) {
if (this.hasTransform()) {
var offset = this.parseBounds();
var origin = this.prefixedCss("transformOrigin").split(" ").map(removePx).map(asFloat);
origin[0] += offset.left;
origin[1] += offset.top;
this.transformData = {
origin: origin,
matrix: this.parseTransformMatrix()
};
} else {
this.transformData = {
origin: [0, 0],
matrix: [1, 0, 0, 1, 0, 0]
};
}
}
return this.transformData;
};
NodeContainer.prototype.parseTransformMatrix = function() {
if (!this.transformMatrix) {
var transform = this.prefixedCss("transform");
var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null;
this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0];
}
return this.transformMatrix;
};
NodeContainer.prototype.parseBounds = function() {
return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node));
};
NodeContainer.prototype.hasTransform = function() {
return this.parseTransformMatrix().join(",") !== "1,0,0,1,0,0" || (this.parent && this.parent.hasTransform());
};
NodeContainer.prototype.getValue = function() {
var value = this.node.value || "";
if (this.node.tagName === "SELECT") {
value = selectionValue(this.node);
} else if (this.node.type === "password") {
value = Array(value.length + 1).join('\u2022'); // jshint ignore:line
}
return value.length === 0 ? (this.node.placeholder || "") : value;
};
NodeContainer.prototype.MATRIX_PROPERTY = /(matrix|matrix3d)\((.+)\)/;
NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g;
NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;
NodeContainer.prototype.CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/;
function selectionValue(node) {
var option = node.options[node.selectedIndex || 0];
return option ? (option.text || "") : "";
}
function parseMatrix(match) {
if (match && match[1] === "matrix") {
return match[2].split(",").map(function(s) {
return parseFloat(s.trim());
});
} else if (match && match[1] === "matrix3d") {
var matrix3d = match[2].split(",").map(function(s) {
return parseFloat(s.trim());
});
return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]];
}
}
function isPercentage(value) {
return value.toString().indexOf("%") !== -1;
}
function removePx(str) {
return str.replace("px", "");
}
function asFloat(str) {
return parseFloat(str);
}
module.exports = NodeContainer;

869
src/nodeparser.js Normal file
View File

@@ -0,0 +1,869 @@
var log = require('./log');
var punycode = require('punycode');
var NodeContainer = require('./nodecontainer');
var TextContainer = require('./textcontainer');
var PseudoElementContainer = require('./pseudoelementcontainer');
var FontMetrics = require('./fontmetrics');
var Color = require('./color');
var StackingContext = require('./stackingcontext');
var utils = require('./utils');
var bind = utils.bind;
var getBounds = utils.getBounds;
var parseBackgrounds = utils.parseBackgrounds;
var offsetBounds = utils.offsetBounds;
function NodeParser(element, renderer, support, imageLoader, options) {
log("Starting NodeParser");
this.renderer = renderer;
this.options = options;
this.range = null;
this.support = support;
this.renderQueue = [];
this.stack = new StackingContext(true, 1, element.ownerDocument, null);
var parent = new NodeContainer(element, null);
if (options.background) {
renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background));
}
if (element === element.ownerDocument.documentElement) {
// http://www.w3.org/TR/css3-background/#special-backgrounds
var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null);
renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor'));
}
parent.visibile = parent.isElementVisible();
this.createPseudoHideStyles(element.ownerDocument);
this.disableAnimations(element.ownerDocument);
this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) {
return container.visible = container.isElementVisible();
}).map(this.getPseudoElements, this));
this.fontMetrics = new FontMetrics();
log("Fetched nodes, total:", this.nodes.length);
log("Calculate overflow clips");
this.calculateOverflowClips();
log("Start fetching images");
this.images = imageLoader.fetch(this.nodes.filter(isElement));
this.ready = this.images.ready.then(bind(function() {
log("Images loaded, starting parsing");
log("Creating stacking contexts");
this.createStackingContexts();
log("Sorting stacking contexts");
this.sortStackingContexts(this.stack);
this.parse(this.stack);
log("Render queue created with " + this.renderQueue.length + " items");
return new Promise(bind(function(resolve) {
if (!options.async) {
this.renderQueue.forEach(this.paint, this);
resolve();
} else if (typeof(options.async) === "function") {
options.async.call(this, this.renderQueue, resolve);
} else if (this.renderQueue.length > 0){
this.renderIndex = 0;
this.asyncRenderer(this.renderQueue, resolve);
} else {
resolve();
}
}, this));
}, this));
}
NodeParser.prototype.calculateOverflowClips = function() {
this.nodes.forEach(function(container) {
if (isElement(container)) {
if (isPseudoElement(container)) {
container.appendToDOM();
}
container.borders = this.parseBorders(container);
var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : [];
var cssClip = container.parseClip();
if (cssClip && ["absolute", "fixed"].indexOf(container.css('position')) !== -1) {
clip.push([["rect",
container.bounds.left + cssClip.left,
container.bounds.top + cssClip.top,
cssClip.right - cssClip.left,
cssClip.bottom - cssClip.top
]]);
}
container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;
container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip;
if (isPseudoElement(container)) {
container.cleanDOM();
}
} else if (isTextNode(container)) {
container.clip = hasParentClip(container) ? container.parent.clip : [];
}
if (!isPseudoElement(container)) {
container.bounds = null;
}
}, this);
};
function hasParentClip(container) {
return container.parent && container.parent.clip.length;
}
NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {
asyncTimer = asyncTimer || Date.now();
this.paint(queue[this.renderIndex++]);
if (queue.length === this.renderIndex) {
resolve();
} else if (asyncTimer + 20 > Date.now()) {
this.asyncRenderer(queue, resolve, asyncTimer);
} else {
setTimeout(bind(function() {
this.asyncRenderer(queue, resolve);
}, this), 0);
}
};
NodeParser.prototype.createPseudoHideStyles = function(document) {
this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: "" !important; display: none !important; }' +
'.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: "" !important; display: none !important; }');
};
NodeParser.prototype.disableAnimations = function(document) {
this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +
'-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}');
};
NodeParser.prototype.createStyles = function(document, styles) {
var hidePseudoElements = document.createElement('style');
hidePseudoElements.innerHTML = styles;
document.body.appendChild(hidePseudoElements);
};
NodeParser.prototype.getPseudoElements = function(container) {
var nodes = [[container]];
if (container.node.nodeType === Node.ELEMENT_NODE) {
var before = this.getPseudoElement(container, ":before");
var after = this.getPseudoElement(container, ":after");
if (before) {
nodes.push(before);
}
if (after) {
nodes.push(after);
}
}
return flatten(nodes);
};
function toCamelCase(str) {
return str.replace(/(\-[a-z])/g, function(match){
return match.toUpperCase().replace('-','');
});
}
NodeParser.prototype.getPseudoElement = function(container, type) {
var style = container.computedStyle(type);
if(!style || !style.content || style.content === "none" || style.content === "-moz-alt-content" || style.display === "none") {
return null;
}
var content = stripQuotes(style.content);
var isImage = content.substr(0, 3) === 'url';
var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement');
var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type);
for (var i = style.length-1; i >= 0; i--) {
var property = toCamelCase(style.item(i));
pseudoNode.style[property] = style[property];
}
pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + " " + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER;
if (isImage) {
pseudoNode.src = parseBackgrounds(content)[0].args[0];
return [pseudoContainer];
} else {
var text = document.createTextNode(content);
pseudoNode.appendChild(text);
return [pseudoContainer, new TextContainer(text, pseudoContainer)];
}
};
NodeParser.prototype.getChildren = function(parentContainer) {
return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
}, this));
};
NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent);
container.cloneTo(stack);
var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;
parentStack.contexts.push(stack);
container.stack = stack;
};
NodeParser.prototype.createStackingContexts = function() {
this.nodes.forEach(function(container) {
if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) {
this.newStackingContext(container, true);
} else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) {
this.newStackingContext(container, false);
} else {
container.assignStack(container.parent.stack);
}
}, this);
};
NodeParser.prototype.isBodyWithTransparentRoot = function(container) {
return container.node.nodeName === "BODY" && container.parent.color('backgroundColor').isTransparent();
};
NodeParser.prototype.isRootElement = function(container) {
return container.parent === null;
};
NodeParser.prototype.sortStackingContexts = function(stack) {
stack.contexts.sort(zIndexSort(stack.contexts.slice(0)));
stack.contexts.forEach(this.sortStackingContexts, this);
};
NodeParser.prototype.parseTextBounds = function(container) {
return function(text, index, textList) {
if (container.parent.css("textDecoration").substr(0, 4) !== "none" || text.trim().length !== 0) {
if (this.support.rangeBounds && !container.parent.hasTransform()) {
var offset = textList.slice(0, index).join("").length;
return this.getRangeBounds(container.node, offset, text.length);
} else if (container.node && typeof(container.node.data) === "string") {
var replacementNode = container.node.splitText(text.length);
var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform());
container.node = replacementNode;
return bounds;
}
} else if(!this.support.rangeBounds || container.parent.hasTransform()){
container.node = container.node.splitText(text.length);
}
return {};
};
};
NodeParser.prototype.getWrapperBounds = function(node, transform) {
var wrapper = node.ownerDocument.createElement('html2canvaswrapper');
var parent = node.parentNode,
backupText = node.cloneNode(true);
wrapper.appendChild(node.cloneNode(true));
parent.replaceChild(wrapper, node);
var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper);
parent.replaceChild(backupText, wrapper);
return bounds;
};
NodeParser.prototype.getRangeBounds = function(node, offset, length) {
var range = this.range || (this.range = node.ownerDocument.createRange());
range.setStart(node, offset);
range.setEnd(node, offset + length);
return range.getBoundingClientRect();
};
function ClearTransform() {}
NodeParser.prototype.parse = function(stack) {
// http://www.w3.org/TR/CSS21/visuren.html#z-index
var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).
var descendantElements = stack.children.filter(isElement);
var descendantNonFloats = descendantElements.filter(not(isFloating));
var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.
var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.
var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
var text = stack.children.filter(isTextNode).filter(hasText);
var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).
negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)
.concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {
this.renderQueue.push(container);
if (isStackingContext(container)) {
this.parse(container);
this.renderQueue.push(new ClearTransform());
}
}, this);
};
NodeParser.prototype.paint = function(container) {
try {
if (container instanceof ClearTransform) {
this.renderer.ctx.restore();
} else if (isTextNode(container)) {
if (isPseudoElement(container.parent)) {
container.parent.appendToDOM();
}
this.paintText(container);
if (isPseudoElement(container.parent)) {
container.parent.cleanDOM();
}
} else {
this.paintNode(container);
}
} catch(e) {
log(e);
if (this.options.strict) {
throw e;
}
}
};
NodeParser.prototype.paintNode = function(container) {
if (isStackingContext(container)) {
this.renderer.setOpacity(container.opacity);
this.renderer.ctx.save();
if (container.hasTransform()) {
this.renderer.setTransform(container.parseTransform());
}
}
if (container.node.nodeName === "INPUT" && container.node.type === "checkbox") {
this.paintCheckbox(container);
} else if (container.node.nodeName === "INPUT" && container.node.type === "radio") {
this.paintRadio(container);
} else {
this.paintElement(container);
}
};
NodeParser.prototype.paintElement = function(container) {
var bounds = container.parseBounds();
this.renderer.clip(container.backgroundClip, function() {
this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));
}, this);
this.renderer.clip(container.clip, function() {
this.renderer.renderBorders(container.borders.borders);
}, this);
this.renderer.clip(container.backgroundClip, function() {
switch (container.node.nodeName) {
case "svg":
case "IFRAME":
var imgContainer = this.images.get(container.node);
if (imgContainer) {
this.renderer.renderImage(container, bounds, container.borders, imgContainer);
} else {
log("Error loading <" + container.node.nodeName + ">", container.node);
}
break;
case "IMG":
var imageContainer = this.images.get(container.node.src);
if (imageContainer) {
this.renderer.renderImage(container, bounds, container.borders, imageContainer);
} else {
log("Error loading <img>", container.node.src);
}
break;
case "CANVAS":
this.renderer.renderImage(container, bounds, container.borders, {image: container.node});
break;
case "SELECT":
case "INPUT":
case "TEXTAREA":
this.paintFormValue(container);
break;
}
}, this);
};
NodeParser.prototype.paintCheckbox = function(container) {
var b = container.parseBounds();
var size = Math.min(b.width, b.height);
var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left};
var r = [3, 3];
var radius = [r, r, r, r];
var borders = [1,1,1,1].map(function(w) {
return {color: new Color('#A5A5A5'), width: w};
});
var borderPoints = calculateCurvePoints(bounds, radius, borders);
this.renderer.clip(container.backgroundClip, function() {
this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color("#DEDEDE"));
this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius));
if (container.node.checked) {
this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + "px", 'arial');
this.renderer.text("\u2714", bounds.left + size / 6, bounds.top + size - 1);
}
}, this);
};
NodeParser.prototype.paintRadio = function(container) {
var bounds = container.parseBounds();
var size = Math.min(bounds.width, bounds.height) - 2;
this.renderer.clip(container.backgroundClip, function() {
this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5'));
if (container.node.checked) {
this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242'));
}
}, this);
};
NodeParser.prototype.paintFormValue = function(container) {
var value = container.getValue();
if (value.length > 0) {
var document = container.node.ownerDocument;
var wrapper = document.createElement('html2canvaswrapper');
var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',
'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',
'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth',
'boxSizing', 'whiteSpace', 'wordWrap'];
properties.forEach(function(property) {
try {
wrapper.style[property] = container.css(property);
} catch(e) {
// Older IE has issues with "border"
log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
}
});
var bounds = container.parseBounds();
wrapper.style.position = "fixed";
wrapper.style.left = bounds.left + "px";
wrapper.style.top = bounds.top + "px";
wrapper.textContent = value;
document.body.appendChild(wrapper);
this.paintText(new TextContainer(wrapper.firstChild, container));
document.body.removeChild(wrapper);
}
};
NodeParser.prototype.paintText = function(container) {
container.applyTextTransform();
var characters = punycode.ucs2.decode(container.node.data);
var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(character) {
return punycode.ucs2.encode([character]);
});
var weight = container.parent.fontWeight();
var size = container.parent.css('fontSize');
var family = container.parent.css('fontFamily');
var shadows = container.parent.parseTextShadows();
this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
if (shadows.length) {
// TODO: support multiple text shadows
this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur);
} else {
this.renderer.clearShadow();
}
this.renderer.clip(container.parent.clip, function() {
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
if (bounds) {
this.renderer.text(textList[index], bounds.left, bounds.bottom);
this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
}
}, this);
}, this);
};
NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) {
switch(container.css("textDecoration").split(" ")[0]) {
case "underline":
// Draws a line at the baseline of the font
// 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
this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color("color"));
break;
case "overline":
this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color("color"));
break;
case "line-through":
// TODO try and find exact position for line-through
this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color("color"));
break;
}
};
var borderColorTransforms = {
inset: [
["darken", 0.60],
["darken", 0.10],
["darken", 0.10],
["darken", 0.60]
]
};
NodeParser.prototype.parseBorders = function(container) {
var nodeBounds = container.parseBounds();
var radius = getBorderRadiusData(container);
var borders = ["Top", "Right", "Bottom", "Left"].map(function(side, index) {
var style = container.css('border' + side + 'Style');
var color = container.color('border' + side + 'Color');
if (style === "inset" && color.isBlack()) {
color = new Color([255, 255, 255, color.a]); // this is wrong, but
}
var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null;
return {
width: container.cssInt('border' + side + 'Width'),
color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color,
args: null
};
});
var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);
return {
clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),
borders: calculateBorders(borders, nodeBounds, borderPoints, radius)
};
};
function calculateBorders(borders, nodeBounds, borderPoints, radius) {
return borders.map(function(border, borderSide) {
if (border.width > 0) {
var bx = nodeBounds.left;
var by = nodeBounds.top;
var bw = nodeBounds.width;
var bh = nodeBounds.height - (borders[2].width);
switch(borderSide) {
case 0:
// top border
bh = borders[0].width;
border.args = drawSide({
c1: [bx, by],
c2: [bx + bw, by],
c3: [bx + bw - borders[1].width, by + bh],
c4: [bx + borders[3].width, by + bh]
}, radius[0], radius[1],
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
break;
case 1:
// right border
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
bw = borders[1].width;
border.args = drawSide({
c1: [bx + bw, by],
c2: [bx + bw, by + bh + borders[2].width],
c3: [bx, by + bh],
c4: [bx, by + borders[0].width]
}, radius[1], radius[2],
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
break;
case 2:
// bottom border
by = (by + nodeBounds.height) - (borders[2].width);
bh = borders[2].width;
border.args = drawSide({
c1: [bx + bw, by + bh],
c2: [bx, by + bh],
c3: [bx + borders[3].width, by],
c4: [bx + bw - borders[3].width, by]
}, radius[2], radius[3],
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
break;
case 3:
// left border
bw = borders[3].width;
border.args = drawSide({
c1: [bx, by + bh + borders[2].width],
c2: [bx, by],
c3: [bx + bw, by + borders[0].width],
c4: [bx + bw, by + bh]
}, radius[3], radius[0],
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
break;
}
}
return border;
});
}
NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {
var backgroundClip = container.css('backgroundClip'),
borderArgs = [];
switch(backgroundClip) {
case "content-box":
case "padding-box":
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
break;
default:
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
break;
}
return borderArgs;
};
function getCurvePoints(x, y, r1, r2) {
var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
var ox = (r1) * kappa, // control point offset horizontal
oy = (r2) * kappa, // control point offset vertical
xm = x + r1, // x-middle
ym = y + r2; // y-middle
return {
topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),
topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),
bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),
bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})
};
}
function calculateCurvePoints(bounds, borderRadius, borders) {
var x = bounds.left,
y = bounds.top,
width = bounds.width,
height = bounds.height,
tlh = borderRadius[0][0] < width / 2 ? borderRadius[0][0] : width / 2,
tlv = borderRadius[0][1] < height / 2 ? borderRadius[0][1] : height / 2,
trh = borderRadius[1][0] < width / 2 ? borderRadius[1][0] : width / 2,
trv = borderRadius[1][1] < height / 2 ? borderRadius[1][1] : height / 2,
brh = borderRadius[2][0] < width / 2 ? borderRadius[2][0] : width / 2,
brv = borderRadius[2][1] < height / 2 ? borderRadius[2][1] : height / 2,
blh = borderRadius[3][0] < width / 2 ? borderRadius[3][0] : width / 2,
blv = borderRadius[3][1] < height / 2 ? borderRadius[3][1] : height / 2;
var topWidth = width - trh,
rightHeight = height - brv,
bottomWidth = width - brh,
leftHeight = height - blv;
return {
topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),
topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5),
topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),
topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5),
bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),
bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width - borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width), brv - borders[2].width).bottomRight.subdivide(0.5),
bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),
bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5)
};
}
function bezierCurve(start, startControl, endControl, end) {
var lerp = function (a, b, t) {
return {
x: a.x + (b.x - a.x) * t,
y: a.y + (b.y - a.y) * t
};
};
return {
start: start,
startControl: startControl,
endControl: endControl,
end: end,
subdivide: function(t) {
var ab = lerp(start, startControl, t),
bc = lerp(startControl, endControl, t),
cd = lerp(endControl, end, t),
abbc = lerp(ab, bc, t),
bccd = lerp(bc, cd, t),
dest = lerp(abbc, bccd, t);
return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
},
curveTo: function(borderArgs) {
borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
},
curveToReversed: function(borderArgs) {
borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
}
};
}
function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
var borderArgs = [];
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
outer1[1].curveTo(borderArgs);
} else {
borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
}
if (radius2[0] > 0 || radius2[1] > 0) {
borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
outer2[0].curveTo(borderArgs);
borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
inner2[0].curveToReversed(borderArgs);
} else {
borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]);
borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]);
}
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
inner1[1].curveToReversed(borderArgs);
} else {
borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]);
}
return borderArgs;
}
function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
if (radius1[0] > 0 || radius1[1] > 0) {
borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
corner1[0].curveTo(borderArgs);
corner1[1].curveTo(borderArgs);
} else {
borderArgs.push(["line", x, y]);
}
if (radius2[0] > 0 || radius2[1] > 0) {
borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
}
}
function negativeZIndex(container) {
return container.cssInt("zIndex") < 0;
}
function positiveZIndex(container) {
return container.cssInt("zIndex") > 0;
}
function zIndex0(container) {
return container.cssInt("zIndex") === 0;
}
function inlineLevel(container) {
return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
}
function isStackingContext(container) {
return (container instanceof StackingContext);
}
function hasText(container) {
return container.node.data.trim().length > 0;
}
function noLetterSpacing(container) {
return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing")));
}
function getBorderRadiusData(container) {
return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
var value = container.css('border' + side + 'Radius');
var arr = value.split(" ");
if (arr.length <= 1) {
arr[1] = arr[0];
}
return arr.map(asInt);
});
}
function renderableNode(node) {
return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);
}
function isPositionedForStacking(container) {
var position = container.css("position");
var zIndex = (["absolute", "relative", "fixed"].indexOf(position) !== -1) ? container.css("zIndex") : "auto";
return zIndex !== "auto";
}
function isPositioned(container) {
return container.css("position") !== "static";
}
function isFloating(container) {
return container.css("float") !== "none";
}
function isInlineBlock(container) {
return ["inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
}
function not(callback) {
var context = this;
return function() {
return !callback.apply(context, arguments);
};
}
function isElement(container) {
return container.node.nodeType === Node.ELEMENT_NODE;
}
function isPseudoElement(container) {
return container.isPseudoElement === true;
}
function isTextNode(container) {
return container.node.nodeType === Node.TEXT_NODE;
}
function zIndexSort(contexts) {
return function(a, b) {
return (a.cssInt("zIndex") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt("zIndex") + (contexts.indexOf(b) / contexts.length));
};
}
function hasOpacity(container) {
return container.getOpacity() < 1;
}
function asInt(value) {
return parseInt(value, 10);
}
function getWidth(border) {
return border.width;
}
function nonIgnoredElement(nodeContainer) {
return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR", "OPTION"].indexOf(nodeContainer.node.nodeName) === -1);
}
function flatten(arrays) {
return [].concat.apply([], arrays);
}
function stripQuotes(content) {
var first = content.substr(0, 1);
return (first === content.substr(content.length - 1) && first.match(/'|"/)) ? content.substr(1, content.length - 2) : content;
}
function getWords(characters) {
var words = [], i = 0, onWordBoundary = false, word;
while(characters.length) {
if (isWordBoundary(characters[i]) === onWordBoundary) {
word = characters.splice(0, i);
if (word.length) {
words.push(punycode.ucs2.encode(word));
}
onWordBoundary =! onWordBoundary;
i = 0;
} else {
i++;
}
if (i >= characters.length) {
word = characters.splice(0, i);
if (word.length) {
words.push(punycode.ucs2.encode(word));
}
}
}
return words;
}
function isWordBoundary(characterCode) {
return [
32, // <space>
13, // \r
10, // \n
9, // \t
45 // -
].indexOf(characterCode) !== -1;
}
function hasUnicode(string) {
return (/[^\u0000-\u00ff]/).test(string);
}
module.exports = NodeParser;

View File

@@ -1,74 +0,0 @@
(function() {
/* options, customize to your needs */
var server = '//html2canvas.hertzen.com/js',
proxy = '//html2canvas.appspot.com',
debug = false,
profile = false;
//DEBUG: server = 'http://localhost/html2canvas'; debug = true;
var debugFiles = [
'external/jquery-1.6.2.min',
'src/Core',
'src/Generate',
'src/Parse',
'src/Preload',
'src/Queue',
'src/Renderer',
'src/plugins/jquery.plugin.html2canvas'
],
relFiles = [
'//code.jquery.com/jquery-1.6.4.js',
'html2canvas',
'jquery.plugin.html2canvas'
],
i = 0, el = null;
var loader = {
index: 0,
head: document.getElementsByTagName('head')[0],
statusline: document.createElement('div'),
files: (debug ? debugFiles : relFiles),
onload: function () {
var _ = this;
if (_.index < _.files.length) {
var el = document.createElement('script');
el.type = 'text/javascript';
el.onload = function() {
_.onload();
};
el.onerror = function() {
_.statusline.style.color = 'red';
_.statusline.innerHTML = _.statusline.innerHTML + ' failed';
_.statusline.onclick = function() {
_.statusline.parentNode.removeChild(_.statusline);
};
};
if (_.files[_.index].substr(0, 2) === '//') {
el.src = _.files[_.index];
}
else {
el.src = server + '/' + _.files[_.index] + '.js';
}
_.head.appendChild(el);
++_.index;
_.statusline.innerHTML = 'html2canvas: loading "' + el.src + '" ' + _.index + ' / ' + _.files.length + '...';
}
else {
_.statusline.parentNode.removeChild(_.statusline);
delete _.statusline;
$(document.documentElement).html2canvas({
logging: debug,
profile: profile
});
}
}
}, statusline = loader.statusline;
statusline.style.position = 'fixed';
statusline.style.bottom = '0px';
statusline.style.right = '20px';
statusline.style.backgroundColor = 'white';
statusline.style.border = '1px solid black';
statusline.style.borderBottomWidth = '0px';
statusline.style.padding = '2px 5px';
statusline.style.zIndex = 9999999;
document.body.appendChild(statusline);
loader.onload();
}());

View File

@@ -1,81 +0,0 @@
/*
* jQuery helper plugin for examples and tests
*/
(function( $ ){
$.fn.html2canvas = function(options) {
if (options && options.profile && window.console && window.console.profile) {
console.profile();
}
var date = new Date(),
html2obj,
$message = null,
timeoutTimer = false,
timer = date.getTime();
options = options || {};
options.elements = this;
options.onrendered = function( canvas ) {
var $canvas = $(canvas),
finishTime = new Date();
if (options && options.profile && window.console && window.console.profileEnd) {
console.profileEnd();
}
$canvas.css({
position: 'absolute',
left: 0,
top: 0
}).appendTo(document.body);
$canvas.siblings().toggle();
$(window).click(function(){
$canvas.toggle().siblings().toggle();
throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden"));
});
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms<br />",4000);
// test if canvas is read-able
try {
$canvas[0].toDataURL();
} catch(e) {
if ($canvas[0].nodeName.toLowerCase() === "canvas") {
// TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed
alert("Canvas is tainted, unable to read data");
}
}
};
html2obj = html2canvas(this[0], options);
function throwMessage(msg,duration){
window.clearTimeout(timeoutTimer);
timeoutTimer = window.setTimeout(function(){
$message.fadeOut(function(){
$message.remove();
$message = null;
});
},duration || 2000);
if ($message)
$message.remove();
$message = $('<div />').html(msg).css({
margin:0,
padding:10,
background: "#000",
opacity:0.7,
position:"fixed",
top:10,
right:10,
fontFamily: 'Tahoma',
color:'#fff',
fontSize:12,
borderRadius:12,
width:'auto',
height:'auto',
textAlign:'center',
textDecoration:'none',
display:'none'
}).appendTo(document.body).fadeIn();
html2obj.log(msg);
}
};
})( jQuery );

95
src/proxy.js Normal file
View File

@@ -0,0 +1,95 @@
var XHR = require('./xhr');
var utils = require('./utils');
var log = require('./log');
var createWindowClone = require('./clone');
var decode64 = utils.decode64;
function Proxy(src, proxyUrl, document) {
var supportsCORS = ('withCredentials' in new XMLHttpRequest());
if (!proxyUrl) {
return Promise.reject("No proxy configured");
}
var callback = createCallback(supportsCORS);
var url = createProxyUrl(proxyUrl, src, callback);
return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) {
return decode64(response.content);
}));
}
var proxyCount = 0;
function ProxyURL(src, proxyUrl, document) {
var supportsCORSImage = ('crossOrigin' in new Image());
var callback = createCallback(supportsCORSImage);
var url = createProxyUrl(proxyUrl, src, callback);
return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) {
return "data:" + response.type + ";base64," + response.content;
}));
}
function jsonp(document, url, callback) {
return new Promise(function(resolve, reject) {
var s = document.createElement("script");
var cleanup = function() {
delete window.html2canvas.proxy[callback];
document.body.removeChild(s);
};
window.html2canvas.proxy[callback] = function(response) {
cleanup();
resolve(response);
};
s.src = url;
s.onerror = function(e) {
cleanup();
reject(e);
};
document.body.appendChild(s);
});
}
function createCallback(useCORS) {
return !useCORS ? "html2canvas_" + Date.now() + "_" + (++proxyCount) + "_" + Math.round(Math.random() * 100000) : "";
}
function createProxyUrl(proxyUrl, src, callback) {
return proxyUrl + "?url=" + encodeURIComponent(src) + (callback.length ? "&callback=html2canvas.proxy." + callback : "");
}
function documentFromHTML(src) {
return function(html) {
var parser = new DOMParser(), doc;
try {
doc = parser.parseFromString(html, "text/html");
} catch(e) {
log("DOMParser not supported, falling back to createHTMLDocument");
doc = document.implementation.createHTMLDocument("");
try {
doc.open();
doc.write(html);
doc.close();
} catch(ee) {
log("createHTMLDocument write not supported, falling back to document.body.innerHTML");
doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement
}
}
var b = doc.querySelector("base");
if (!b || !b.href.host) {
var base = doc.createElement("base");
base.href = src;
doc.head.insertBefore(base, doc.head.firstChild);
}
return doc;
};
}
function loadUrlDocument(src, proxy, document, width, height, options) {
return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) {
return createWindowClone(doc, document, width, height, options, 0, 0);
});
}
exports.Proxy = Proxy;
exports.ProxyURL = ProxyURL;
exports.loadUrlDocument = loadUrlDocument;

View File

@@ -0,0 +1,21 @@
var ProxyURL = require('./proxy').ProxyURL;
function ProxyImageContainer(src, proxy) {
var link = document.createElement("a");
link.href = src;
src = link.href;
this.src = src;
this.image = new Image();
var self = this;
this.promise = new Promise(function(resolve, reject) {
self.image.crossOrigin = "Anonymous";
self.image.onload = resolve;
self.image.onerror = reject;
new ProxyURL(src, proxy, document).then(function(url) {
self.image.src = url;
})['catch'](reject);
});
}
module.exports = ProxyImageContainer;

View File

@@ -0,0 +1,38 @@
var NodeContainer = require('./nodecontainer');
function PseudoElementContainer(node, parent, type) {
NodeContainer.call(this, node, parent);
this.isPseudoElement = true;
this.before = type === ":before";
}
PseudoElementContainer.prototype.cloneTo = function(stack) {
PseudoElementContainer.prototype.cloneTo.call(this, stack);
stack.isPseudoElement = true;
stack.before = this.before;
};
PseudoElementContainer.prototype = Object.create(NodeContainer.prototype);
PseudoElementContainer.prototype.appendToDOM = function() {
if (this.before) {
this.parent.node.insertBefore(this.node, this.parent.node.firstChild);
} else {
this.parent.node.appendChild(this.node);
}
this.parent.node.className += " " + this.getHideClass();
};
PseudoElementContainer.prototype.cleanDOM = function() {
this.node.parentNode.removeChild(this.node);
this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), "");
};
PseudoElementContainer.prototype.getHideClass = function() {
return this["PSEUDO_HIDE_ELEMENT_CLASS_" + (this.before ? "BEFORE" : "AFTER")];
};
PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = "___html2canvas___pseudoelement_before";
PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = "___html2canvas___pseudoelement_after";
module.exports = PseudoElementContainer;

108
src/renderer.js Normal file
View File

@@ -0,0 +1,108 @@
var log = require('./log');
function Renderer(width, height, images, options, document) {
this.width = width;
this.height = height;
this.images = images;
this.options = options;
this.document = document;
}
Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) {
var paddingLeft = container.cssInt('paddingLeft'),
paddingTop = container.cssInt('paddingTop'),
paddingRight = container.cssInt('paddingRight'),
paddingBottom = container.cssInt('paddingBottom'),
borders = borderData.borders;
var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight);
var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom);
this.drawImage(
imageContainer,
0,
0,
imageContainer.image.width || width,
imageContainer.image.height || height,
bounds.left + paddingLeft + borders[3].width,
bounds.top + paddingTop + borders[0].width,
width,
height
);
};
Renderer.prototype.renderBackground = function(container, bounds, borderData) {
if (bounds.height > 0 && bounds.width > 0) {
this.renderBackgroundColor(container, bounds);
this.renderBackgroundImage(container, bounds, borderData);
}
};
Renderer.prototype.renderBackgroundColor = function(container, bounds) {
var color = container.color("backgroundColor");
if (!color.isTransparent()) {
this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color);
}
};
Renderer.prototype.renderBorders = function(borders) {
borders.forEach(this.renderBorder, this);
};
Renderer.prototype.renderBorder = function(data) {
if (!data.color.isTransparent() && data.args !== null) {
this.drawShape(data.args, data.color);
}
};
Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) {
var backgroundImages = container.parseBackgroundImages();
backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {
switch(backgroundImage.method) {
case "url":
var image = this.images.get(backgroundImage.args[0]);
if (image) {
this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData);
} else {
log("Error loading background-image", backgroundImage.args[0]);
}
break;
case "linear-gradient":
case "gradient":
var gradientImage = this.images.get(backgroundImage.value);
if (gradientImage) {
this.renderBackgroundGradient(gradientImage, bounds, borderData);
} else {
log("Error loading background-image", backgroundImage.args[0]);
}
break;
case "none":
break;
default:
log("Unknown background-image type", backgroundImage.args[0]);
}
}, this);
};
Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) {
var size = container.parseBackgroundSize(bounds, imageContainer.image, index);
var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size);
var repeat = container.parseBackgroundRepeat(index);
switch (repeat) {
case "repeat-x":
case "repeat no-repeat":
this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData);
break;
case "repeat-y":
case "no-repeat repeat":
this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData);
break;
case "no-repeat":
this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData);
break;
default:
this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]);
break;
}
};
module.exports = Renderer;

181
src/renderers/canvas.js Normal file
View File

@@ -0,0 +1,181 @@
var Renderer = require('../renderer');
var LinearGradientContainer = require('../lineargradientcontainer');
var log = require('../log');
function CanvasRenderer(width, height) {
Renderer.apply(this, arguments);
this.canvas = this.options.canvas || this.document.createElement("canvas");
if (!this.options.canvas) {
this.canvas.width = width;
this.canvas.height = height;
}
this.ctx = this.canvas.getContext("2d");
this.taintCtx = this.document.createElement("canvas").getContext("2d");
this.ctx.textBaseline = "bottom";
this.variables = {};
log("Initialized CanvasRenderer with size", width, "x", height);
}
CanvasRenderer.prototype = Object.create(Renderer.prototype);
CanvasRenderer.prototype.setFillStyle = function(fillStyle) {
this.ctx.fillStyle = typeof(fillStyle) === "object" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle;
return this.ctx;
};
CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) {
this.setFillStyle(color).fillRect(left, top, width, height);
};
CanvasRenderer.prototype.circle = function(left, top, size, color) {
this.setFillStyle(color);
this.ctx.beginPath();
this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true);
this.ctx.closePath();
this.ctx.fill();
};
CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) {
this.circle(left, top, size, color);
this.ctx.strokeStyle = strokeColor.toString();
this.ctx.stroke();
};
CanvasRenderer.prototype.drawShape = function(shape, color) {
this.shape(shape);
this.setFillStyle(color).fill();
};
CanvasRenderer.prototype.taints = function(imageContainer) {
if (imageContainer.tainted === null) {
this.taintCtx.drawImage(imageContainer.image, 0, 0);
try {
this.taintCtx.getImageData(0, 0, 1, 1);
imageContainer.tainted = false;
} catch(e) {
this.taintCtx = document.createElement("canvas").getContext("2d");
imageContainer.tainted = true;
}
}
return imageContainer.tainted;
};
CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) {
if (!this.taints(imageContainer) || this.options.allowTaint) {
this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh);
}
};
CanvasRenderer.prototype.clip = function(shapes, callback, context) {
this.ctx.save();
shapes.filter(hasEntries).forEach(function(shape) {
this.shape(shape).clip();
}, this);
callback.call(context);
this.ctx.restore();
};
CanvasRenderer.prototype.shape = function(shape) {
this.ctx.beginPath();
shape.forEach(function(point, index) {
if (point[0] === "rect") {
this.ctx.rect.apply(this.ctx, point.slice(1));
} else {
this.ctx[(index === 0) ? "moveTo" : point[0] + "To" ].apply(this.ctx, point.slice(1));
}
}, this);
this.ctx.closePath();
return this.ctx;
};
CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) {
this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ").split(",")[0];
};
CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) {
this.setVariable("shadowColor", color.toString())
.setVariable("shadowOffsetY", offsetX)
.setVariable("shadowOffsetX", offsetY)
.setVariable("shadowBlur", blur);
};
CanvasRenderer.prototype.clearShadow = function() {
this.setVariable("shadowColor", "rgba(0,0,0,0)");
};
CanvasRenderer.prototype.setOpacity = function(opacity) {
this.ctx.globalAlpha = opacity;
};
CanvasRenderer.prototype.setTransform = function(transform) {
this.ctx.translate(transform.origin[0], transform.origin[1]);
this.ctx.transform.apply(this.ctx, transform.matrix);
this.ctx.translate(-transform.origin[0], -transform.origin[1]);
};
CanvasRenderer.prototype.setVariable = function(property, value) {
if (this.variables[property] !== value) {
this.variables[property] = this.ctx[property] = value;
}
return this;
};
CanvasRenderer.prototype.text = function(text, left, bottom) {
this.ctx.fillText(text, left, bottom);
};
CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) {
var shape = [
["line", Math.round(left), Math.round(top)],
["line", Math.round(left + width), Math.round(top)],
["line", Math.round(left + width), Math.round(height + top)],
["line", Math.round(left), Math.round(height + top)]
];
this.clip([shape], function() {
this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);
}, this);
};
CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) {
var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop);
this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), "repeat"));
this.ctx.translate(offsetX, offsetY);
this.ctx.fill();
this.ctx.translate(-offsetX, -offsetY);
};
CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) {
if (gradientImage instanceof LinearGradientContainer) {
var gradient = this.ctx.createLinearGradient(
bounds.left + bounds.width * gradientImage.x0,
bounds.top + bounds.height * gradientImage.y0,
bounds.left + bounds.width * gradientImage.x1,
bounds.top + bounds.height * gradientImage.y1);
gradientImage.colorStops.forEach(function(colorStop) {
gradient.addColorStop(colorStop.stop, colorStop.color.toString());
});
this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient);
}
};
CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
var image = imageContainer.image;
if(image.width === size.width && image.height === size.height) {
return image;
}
var ctx, canvas = document.createElement('canvas');
canvas.width = size.width;
canvas.height = size.height;
ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height );
return canvas;
};
function hasEntries(array) {
return array.length > 0;
}
module.exports = CanvasRenderer;

18
src/stackingcontext.js Normal file
View File

@@ -0,0 +1,18 @@
var NodeContainer = require('./nodecontainer');
function StackingContext(hasOwnStacking, opacity, element, parent) {
NodeContainer.call(this, element, parent);
this.ownStacking = hasOwnStacking;
this.contexts = [];
this.children = [];
this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;
}
StackingContext.prototype = Object.create(NodeContainer.prototype);
StackingContext.prototype.getParentStack = function(context) {
var parentStack = (this.parent) ? this.parent.stack : null;
return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;
};
module.exports = StackingContext;

51
src/support.js Normal file
View File

@@ -0,0 +1,51 @@
function Support(document) {
this.rangeBounds = this.testRangeBounds(document);
this.cors = this.testCORS();
this.svg = this.testSVG();
}
Support.prototype.testRangeBounds = function(document) {
var range, testElement, rangeBounds, rangeHeight, support = false;
if (document.createRange) {
range = document.createRange();
if (range.getBoundingClientRect) {
testElement = document.createElement('boundtest');
testElement.style.height = "123px";
testElement.style.display = "block";
document.body.appendChild(testElement);
range.selectNode(testElement);
rangeBounds = range.getBoundingClientRect();
rangeHeight = rangeBounds.height;
if (rangeHeight === 123) {
support = true;
}
document.body.removeChild(testElement);
}
}
return support;
};
Support.prototype.testCORS = function() {
return typeof((new Image()).crossOrigin) !== "undefined";
};
Support.prototype.testSVG = function() {
var img = new Image();
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
img.src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";
try {
ctx.drawImage(img, 0, 0);
canvas.toDataURL();
} catch(e) {
return false;
}
return true;
};
module.exports = Support;

52
src/svgcontainer.js Normal file
View File

@@ -0,0 +1,52 @@
var XHR = require('./xhr');
var decode64 = require('./utils').decode64;
function SVGContainer(src) {
this.src = src;
this.image = null;
var self = this;
this.promise = this.hasFabric().then(function() {
return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src));
}).then(function(svg) {
return new Promise(function(resolve) {
window.html2canvas.svg.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve));
});
});
}
SVGContainer.prototype.hasFabric = function() {
return !window.html2canvas.svg || !window.html2canvas.svg.fabric ? Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg")) : Promise.resolve();
};
SVGContainer.prototype.inlineFormatting = function(src) {
return (/^data:image\/svg\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src);
};
SVGContainer.prototype.removeContentType = function(src) {
return src.replace(/^data:image\/svg\+xml(;base64)?,/,'');
};
SVGContainer.prototype.isInline = function(src) {
return (/^data:image\/svg\+xml/i.test(src));
};
SVGContainer.prototype.createCanvas = function(resolve) {
var self = this;
return function (objects, options) {
var canvas = new window.html2canvas.svg.fabric.StaticCanvas('c');
self.image = canvas.lowerCanvasEl;
canvas
.setWidth(options.width)
.setHeight(options.height)
.add(window.html2canvas.svg.fabric.util.groupSVGElements(objects, options))
.renderAll();
resolve(canvas.lowerCanvasEl);
};
};
SVGContainer.prototype.decode64 = function(str) {
return (typeof(window.atob) === "function") ? window.atob(str) : decode64(str);
};
module.exports = SVGContainer;

25
src/svgnodecontainer.js Normal file
View File

@@ -0,0 +1,25 @@
var SVGContainer = require('./svgcontainer');
function SVGNodeContainer(node, _native) {
this.src = node;
this.image = null;
var self = this;
this.promise = _native ? new Promise(function(resolve, reject) {
self.image = new Image();
self.image.onload = resolve;
self.image.onerror = reject;
self.image.src = "data:image/svg+xml," + (new XMLSerializer()).serializeToString(node);
if (self.image.complete === true) {
resolve(self.image);
}
}) : this.hasFabric().then(function() {
return new Promise(function(resolve) {
window.html2canvas.svg.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve));
});
});
}
SVGNodeContainer.prototype = Object.create(SVGContainer.prototype);
module.exports = SVGNodeContainer;

33
src/textcontainer.js Normal file
View File

@@ -0,0 +1,33 @@
var NodeContainer = require('./nodecontainer');
function TextContainer(node, parent) {
NodeContainer.call(this, node, parent);
}
TextContainer.prototype = Object.create(NodeContainer.prototype);
TextContainer.prototype.applyTextTransform = function() {
this.node.data = this.transform(this.parent.css("textTransform"));
};
TextContainer.prototype.transform = function(transform) {
var text = this.node.data;
switch(transform){
case "lowercase":
return text.toLowerCase();
case "capitalize":
return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize);
case "uppercase":
return text.toUpperCase();
default:
return text;
}
};
function capitalize(m, p1, p2) {
if (m.length > 0) {
return p1 + p2.toUpperCase();
}
}
module.exports = TextContainer;

169
src/utils.js Normal file
View File

@@ -0,0 +1,169 @@
exports.smallImage = function smallImage() {
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
};
exports.bind = function(callback, context) {
return function() {
return callback.apply(context, arguments);
};
};
/*
* base64-arraybuffer
* https://github.com/niklasvh/base64-arraybuffer
*
* Copyright (c) 2012 Niklas von Hertzen
* Licensed under the MIT license.
*/
exports.decode64 = function(base64) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3;
var output = "";
for (i = 0; i < len; i+=4) {
encoded1 = chars.indexOf(base64[i]);
encoded2 = chars.indexOf(base64[i+1]);
encoded3 = chars.indexOf(base64[i+2]);
encoded4 = chars.indexOf(base64[i+3]);
byte1 = (encoded1 << 2) | (encoded2 >> 4);
byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);
byte3 = ((encoded3 & 3) << 6) | encoded4;
if (encoded3 === 64) {
output += String.fromCharCode(byte1);
} else if (encoded4 === 64 || encoded4 === -1) {
output += String.fromCharCode(byte1, byte2);
} else{
output += String.fromCharCode(byte1, byte2, byte3);
}
}
return output;
};
exports.getBounds = function(node) {
if (node.getBoundingClientRect) {
var clientRect = node.getBoundingClientRect();
var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth;
return {
top: clientRect.top,
bottom: clientRect.bottom || (clientRect.top + clientRect.height),
right: clientRect.left + width,
left: clientRect.left,
width: width,
height: node.offsetHeight == null ? clientRect.height : node.offsetHeight
};
}
return {};
};
exports.offsetBounds = function(node) {
var parent = node.offsetParent ? exports.offsetBounds(node.offsetParent) : {top: 0, left: 0};
return {
top: node.offsetTop + parent.top,
bottom: node.offsetTop + node.offsetHeight + parent.top,
right: node.offsetLeft + parent.left + node.offsetWidth,
left: node.offsetLeft + parent.left,
width: node.offsetWidth,
height: node.offsetHeight
};
};
exports.parseBackgrounds = function(backgroundImage) {
var whitespace = ' \r\n\t',
method, definition, prefix, prefix_i, block, results = [],
mode = 0, numParen = 0, quote, args;
var appendResult = function() {
if(method) {
if (definition.substr(0, 1) === '"') {
definition = definition.substr(1, definition.length - 2);
}
if (definition) {
args.push(definition);
}
if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) {
prefix = method.substr(0, prefix_i);
method = method.substr(prefix_i);
}
results.push({
prefix: prefix,
method: method.toLowerCase(),
value: block,
args: args,
image: null
});
}
args = [];
method = prefix = definition = block = '';
};
args = [];
method = prefix = definition = block = '';
backgroundImage.split("").forEach(function(c) {
if (mode === 0 && whitespace.indexOf(c) > -1) {
return;
}
switch(c) {
case '"':
if(!quote) {
quote = c;
} else if(quote === c) {
quote = null;
}
break;
case '(':
if(quote) {
break;
} else if(mode === 0) {
mode = 1;
block += c;
return;
} else {
numParen++;
}
break;
case ')':
if (quote) {
break;
} else if(mode === 1) {
if(numParen === 0) {
mode = 0;
block += c;
appendResult();
return;
} else {
numParen--;
}
}
break;
case ',':
if (quote) {
break;
} else if(mode === 0) {
appendResult();
return;
} else if (mode === 1) {
if (numParen === 0 && !method.match(/^url$/i)) {
args.push(definition);
definition = '';
block += c;
return;
}
}
break;
}
block += c;
if (mode === 0) {
method += c;
} else {
definition += c;
}
});
appendResult();
return results;
};

View File

@@ -0,0 +1,10 @@
var GradientContainer = require('./gradientcontainer');
function WebkitGradientContainer(imageData) {
GradientContainer.apply(this, arguments);
this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
}
WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
module.exports = WebkitGradientContainer;

22
src/xhr.js Normal file
View File

@@ -0,0 +1,22 @@
function XHR(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = function() {
reject(new Error("Network Error"));
};
xhr.send();
});
}
module.exports = XHR;

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>frame 1</title>
<style>
body {
background: green;
color: red;
}
</style>
</head>
<body>
this is the content of frame1
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
tests/assets/image2_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
tests/assets/image_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,109 +0,0 @@
<!--
* @author Niklas von Hertzen <niklas at hertzen.com>
* @created 15.7.2011
* @website http://hertzen.com
-->
<!DOCTYPE html>
<html>
<head>
<title>border tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="#" type="text/css" rel="stylesheet">
<script type="text/javascript" src="test.js"></script>
<style type="text/css">
div { font: 12px Arial; }
span.bold { font-weight: bold; }
#div2 { z-index: 2; }
#div3 { z-index: 1; }
#div4 { z-index: 10; }
#div1,#div3 {
height: 80px;
position: relative;
border: 23px double #669966;
background-color: #ccffcc;
padding-left: 5px;
}
#div2 {
opacity: 0.8;
position: absolute;
width: 150px;
height: 201px;
top: 20px;
left: 170px;
border: 20px dotted #990000;
background-color: #ffdddd;
text-align: center;
}
#div4 {
opacity: 0.8;
position: absolute;
width: 200px;
height: 70px;
top: 65px;
left: 50px;
border: 15px dashed #000099;
background-color: #ddddff;
text-align: left;
padding-left: 10px;
}
#div5{
border: 15px dashed #669966;
background-color: #ccffcc;
padding-left: 5px;
position:relative;
margin-bottom:-15px;
height:50px;
margin-top:10px;
}
#div6{
border: 1px dashed #000099;
background-color: #ddddff;
text-align: left;
padding-left: 10px;
}
</style></head>
<body>
<br />
<div id="div1">
<br /><span class="bold">DIV #1</span>
<br />position: relative;
<div id="div2">
<br /><span class="bold">DIV #2</span>
<br />position: absolute;
<br />z-index: 2;
</div>
</div>
<br />
<div id="div3" style="background-image:url(image.jpg)">
<br /><span class="bold">DIV #3</span>
<br />position: relative;
<br />z-index: 1;
<div id="div4">
<br /><span class="bold">DIV #4</span>
<br />position: absolute;
<br />z-index: 10;
</div>
</div>
<div id="div5"><br />DIV #5<br />position:relative;<br /></div>
<div id ="div6"><br />DIV #6<br />position:static;<br /></div>
</body>
</html>

141
tests/cases/acid2.html Normal file
View File

@@ -0,0 +1,141 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>The Second Acid Test</title>
<style type="text/css">
/* section numbers refer to CSS2.1 */
/* page setup */
html { font: 12px sans-serif; margin: 0; padding: 0; overflow: hidden; /* hides scrollbars on viewport, see 11.1.1:3 */ background: white; color: red; }
body { margin: 0; padding: 0; }
/* introduction message */
.intro { font: 2em sans-serif; margin: 3.5em 2em; padding: 0.5em; border: solid thin; background: white; color: black; position: relative; z-index: 2; /* should cover the black and red bars that are fixed-positioned */ }
.intro * { font: inherit; margin: 0; padding: 0; }
.intro h1 { font-size: 1em; font-weight: bolder; margin: 0; padding: 0; }
.intro :link { color: blue; }
.intro :visited { color: purple; }
/* picture setup */
#top { margin: 100em 3em 0; padding: 2em 0 0 .5em; text-align: left; font: 2em/24px sans-serif; color: navy; white-space: pre; } /* "Hello World!" text */
.picture { position: relative; border: 1em solid transparent; margin: 0 0 100em 3em; } /* containing block for face */
.picture { background: red; } /* overriden by preferred stylesheet below */
/* top line of face (scalp): fixed positioning and min/max height/width */
.picture p { position: fixed; margin: 0; padding: 0; border: 0; top: 9em; left: 11em; width: 140%; max-width: 4em; height: 8px; min-height: 1em; max-height: 2mm; /* min-height overrides max-height, see 10.7 */ background: black; border-bottom: 0.5em yellow solid; }
/* bits that shouldn't be part of the top line (and shouldn't be visible at all): HTML parsing, "+" combinator, stacking order */
.picture p.bad { border-bottom: red solid; /* shouldn't matter, because the "p + table + p" rule below should match it too, thus hiding it */ }
.picture p + p { background: maroon; z-index: 1; } /* shouldn't match anything */
.picture p + table + p { margin-top: 3em; /* should end up under the absolutely positioned table below, and thus not be visible */ }
/* second line of face: attribute selectors, float positioning */
[class~=one].first.one { position: absolute; top: 0; margin: 36px 0 0 60px; padding: 0; border: black 2em; border-style: none solid; /* shrink wraps around float */ }
[class~=one][class~=first] [class=second\ two][class="second two"] { float: right; width: 48px; height: 12px; background: yellow; margin: 0; padding: 0; } /* only content of abs pos block */
/* third line of face: width and overflow */
.forehead { margin: 4em; width: 8em; border-left: solid black 1em; border-right: solid black 1em; background: red url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4%2F58BAAT%2FAf9jgNErAAAAAElFTkSuQmCC); /* that's a 1x1 yellow pixel PNG */ }
.forehead * { width: 12em; line-height: 1em; }
/* class selectors headache */
.two.error.two { background: maroon; } /* shouldn't match */
.forehead.error.forehead { background: red; } /* shouldn't match */
[class=second two] { background: red; } /* this should be ignored (invalid selector -- grammar says it only accepts IDENTs or STRINGs) */
/* fourth and fifth lines of face, with eyes: paint order test (see appendix E) and fixed backgrounds */
/* the two images are identical: 2-by-2 squares with the top left
and bottom right pixels set to yellow and the other two set to
transparent. Since they are offset by one pixel from each other,
the second one paints exactly over the transparent parts of the
first one, thus creating a solid yellow block. */
.eyes { position: absolute; top: 5em; left: 3em; margin: 0; padding: 0; background: red; }
#eyes-a { height: 0; line-height: 2em; text-align: right; } /* contents should paint top-most because they're inline */
#eyes-a object { display: inline; vertical-align: bottom; }
#eyes-a object[type] { width: 7.5em; height: 2.5em; } /* should have no effect since that object should fallback to being inline (height/width don't apply to inlines) */
#eyes-a object object object { border-right: solid 1em black; padding: 0 12px 0 11px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAABnRSTlMAAAAAAABupgeRAAAABmJLR0QA%2FwD%2FAP%2BgvaeTAAAAEUlEQVR42mP4%2F58BCv7%2FZwAAHfAD%2FabwPj4AAAAASUVORK5CYII%3D) fixed 1px 0; }
#eyes-b { float: left; width: 10em; height: 2em; background: fixed url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAABnRSTlMAAAAAAABupgeRAAAABmJLR0QA%2FwD%2FAP%2BgvaeTAAAAEUlEQVR42mP4%2F58BCv7%2FZwAAHfAD%2FabwPj4AAAAASUVORK5CYII%3D); border-left: solid 1em black; border-right: solid 1em red; } /* should paint in the middle layer because it is a float */
#eyes-c { display: block; background: red; border-left: 2em solid yellow; width: 10em; height: 2em; } /* should paint bottom most because it is a block */
/* lines six to nine, with nose: auto margins */
.nose { float: left; margin: -2em 2em -1em; border: solid 1em black; border-top: 0; min-height: 80%; height: 60%; max-height: 3em; /* percentages become auto (see 10.5 and 10.7) and intrinsic height is more than 3em, so 3em wins */ padding: 0; width: 12em; }
.nose > div { padding: 1em 1em 3em; height: 0; background: yellow; }
.nose div div { width: 2em; height: 2em; background: red; margin: auto; }
.nose :hover div { border-color: blue; }
.nose div:hover :before { border-bottom-color: inherit; }
.nose div:hover :after { border-top-color: inherit; }
.nose div div:before { display: block; border-style: none solid solid; border-color: red yellow black yellow; border-width: 1em; content: ''; height: 0; }
.nose div :after { display: block; border-style: solid solid none; border-color: black yellow red yellow; border-width: 1em; content: ''; height: 0; }
/* between lines nine and ten: margin collapsing with 'float' and 'clear' */
.empty { margin: 6.25em; height: 10%; /* computes to auto which makes it empty per 8.3.1:7 (own margins) */ }
.empty div { margin: 0 2em -6em 4em; }
.smile { margin: 5em 3em; clear: both; /* clearance is negative (see 8.3.1 and 9.5.1) */ }
/* line ten and eleven: containing block for abs pos */
.smile div { margin-top: 0.25em; background: black; width: 12em; height: 2em; position: relative; bottom: -1em; }
.smile div div { position: absolute; top: 0; right: 1em; width: auto; height: 0; margin: 0; border: yellow solid 1em; }
/* smile (over lines ten and eleven): backgrounds behind borders, inheritance of 'float', nested floats, negative heights */
.smile div div span { display: inline; margin: -1em 0 0 0; border: solid 1em transparent; border-style: none solid; float: right; background: black; height: 1em; }
.smile div div span em { float: inherit; border-top: solid yellow 1em; border-bottom: solid black 1em; } /* zero-height block; width comes from (zero-height) child. */
.smile div div span em strong { width: 6em; display: block; margin-bottom: -1em; /* should have no effect, since parent has top&bottom borders, so this margin doesn't collapse */ }
/* line twelve: line-height */
.chin { margin: -4em 4em 0; width: 8em; line-height: 1em; border-left: solid 1em black; border-right: solid 1em black; background: yellow url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAFSDNYfAAAAaklEQVR42u3XQQrAIAwAQeP%2F%2F6wf8CJBJTK9lnQ7FpHGaOurt1I34nfH9pMMZAZ8BwMGEvvh%2BBsJCAgICLwIOA8EBAQEBAQEBAQEBK79H5RfIQAAAAAAAAAAAAAAAAAAAAAAAAAAAID%2FABMSqAfj%2FsLmvAAAAABJRU5ErkJggg%3D%3D) /* 64x64 red square */ no-repeat fixed /* shouldn't be visible unless the smiley is moved to the top left of the viewport */; }
.chin div { display: inline; font: 2px/4px serif; }
/* line thirteen: cascade and selector tests */
.parser-container div { color: maroon; border: solid; color: orange; } /* setup */
div.parser-container * { border-color: black; /* overrides (implied) border-color on previous line */ } /* setup */
* div.parser { border-width: 0 2em; /* overrides (implied) declarations on earlier line */ } /* setup */
/* line thirteen continued: parser tests */
.parser { /* comment parsing test -- comment ends before the end of this line, the backslash should have no effect: \*/ }
.parser { margin: 0 5em 1em; padding: 0 1em; width: 2em; height: 1em; error: \}; background: yellow; } /* setup with parsing test */
* html .parser { background: gray; }
\.parser { padding: 2em; }
.parser { m\argin: 2em; };
.parser { height: 3em; }
.parser { width: 200; }
.parser { border: 5em solid red ! error; }
.parser { background: red pink; }
/* line fourteen (last line of face): table */
ul { display: table; padding: 0; margin: -1em 7em 0; background: red; }
ul li { padding: 0; margin: 0; }
ul li.first-part { display: table-cell; height: 1em; width: 1em; background: black; }
ul li.second-part { display: table; height: 1em; width: 1em; background: black; } /* anonymous table cell wraps around this */
ul li.third-part { display: table-cell; height: 0.5em; /* gets stretched to fit row */ width: 1em; background: black; }
ul li.fourth-part { list-style: none; height: 1em; width: 1em; background: black; } /* anonymous table cell wraps around this */
/* bits that shouldn't appear: inline alignment in cells */
.image-height-test { height: 10px; overflow: hidden; font: 20em serif; } /* only the area between the top of the line box and the top of the image should be visible */
table { margin: 0; border-spacing: 0; }
td { padding: 0; }
</style>
<link rel="appendix stylesheet" href="data:text/css,.picture%20%7B%20background%3A%20none%3B%20%7D"> <!-- this stylesheet should be applied by default -->
<script type="text/javascript" src="../test.js"></script>
</head>
<body>
<div style="margin: 70px;"></div>
<div class="picture">
<p><table><tr><td></table><p class="bad"> <!-- <table> closes <p> per the HTML4 DTD -->
<blockquote class="first one"><address class="second two"></address></blockquote>
<div class="forehead"><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div></div>
<div class="eyes"><div id="eyes-a"><object data="data:application/x-unknown,ERROR"><object data="http://www.damowmow.com/404/" type="text/html"><object data="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAYCAYAAAFy7sgCAAAGsUlEQVRo3u2ZbWwcZxHHf3s%2B7LNbO3ZjXBtowprGODRX0qpNQCjmJKuVKhMl1P2AkCwhFOIKkCBSm9IXavGFKAixIAECwkmWo5MrhRI3Ub40IEwQgp6aIDg3Cd6eEqyIHEteah%2B1E69vhw%2BZtTaX8704ZzkKjHS6271nZ56ZZ%2BY%2F%2F%2BdZKF%2FCwYshx3EkkggLsD1v4FQkEZZYLCbAKyG9%2Ba9EIsG6hnUAf8x74K3aUC3j4%2BM54HcsR2oAIomwZOezkv%2FnSHpYNh%2BNCmAE7xv94zvFdd1bHsjMZmQkPSxAJP%2B%2FfuBLwK54PC7JZFKAVJmzXLBt2w%2FMvcDLwIb8QS8CeJ4nkURYIomw7J%2FYJ8BvSiiXptGGxWds2%2Fa9%2Bnaxh%2BYAD%2Bgt04NDgABTpQY2cvvSFLzw86gWeBVwC8SzlOSv2YeBPfmDBoBHgKmR9LBEEmHZfDTqGykqfkUE0nA78BzQGfSgUeP3wNeTXwXg7MwZDhw4UHL6ra2ti79%2FOvljgG8AZ4H64Lhm4MvAocxsRppGG%2FxcXihlwLIs6R%2FfKV2HO%2F26uA94pdDYUKUZUU7W1RQYXA98Gnhaf5%2FXWX0HeAHYoQonqa4sZSOsSWMCWeC9Yko%2BCQwBe4E6oNc0Tc91XTl1%2BaTsn9gnI%2Blhyc5nZWxsrBIkKSbl2tiic3tW53YDEwOKaoFBrcOfqKee53lG9xsPMjV784r%2F4lO%2FpPvyJ9iyZcuvFSaXK5XYeAZ4CDgGvB3MS4B54LQuWYPeuy4iRFsevsXqpuYoqVQKIH2bK1CuDQNo11o4XUzh%2FcDWYIe1LEtyuZx4niee54njOGKapgfsqlL%2Bl2OjEXg8nxrc1dJ0h3hbtL%2BGCtz7KPBF4CuBe9uB15VafE8hr9qylI3HgG8C2%2FK7VyHZoJj7MrBRm30qFotJMpkU27YlHo%2F7Ha5a%2BV%2FKRkSJ4KuKRLVLKapTjB1SzAVIjY2NSXY%2BKyPpYdk%2FsU9OXT4pruv6BdZbBQfKsVGnvWlIe1VB6VQO8JxC1vZYLCbZ%2BaxsPhpdZDyRRFhG0sPiOE6ldKBg2lRg4xF1YCDIIIKN7DGgD3gH%2BBXwejKZfPrs2tPs%2FvPN2bKuYR1nd7xLKBSSJeqoXKnERjPwNWAG%2BLn2rZuM%2B4Tpml6vaWlp4eLcxVusZq5lCgVgOVKJjRqdX86ffL4D5wIoZACnTpw4wRMdT96i%2FImOJxERAs4uVyqxUacF%2FPdiCj%2BjdRBRGFtwXVdG0sPSdbhTmkYbpH98p2RmM2JZlig1vl0GWo4NQ%2Fn%2Bs5pKRXfwjweaxy7TND3HcRZbfC6X8xVPVQlGy7WxVWlO5XRXFXm6EZmrQuSXYyPE3SiVoEhE6Wyr0u2rumO6zv%2B21AFdQAswC1wCMuUCXCmyWQus103Qg8qlDO0lxwOb%2Fl4FiK3AB3VS%2FuKKLtK%2FgbeAnwG%2FvUODuRw%2FFrR0H1UC75fwu8oJ%2FhFsW5VIG%2FBUgEIN6Y65O4AHu4Ap0zQ9y7LEcZyb9lRBUHQcRyzL8unZVBW5bFWAvAp%2BhDQ2g4F47dUYtlU6obXA54DnVdFLekjUGGifh4AFy7LEdV3xj3X9I66m0QZpGm2QrsOd0j%2B%2BU0bSw5KZzYjrun6HWlAd961i4FfCj0aN1Usau%2Bc1lmuXPFwvAEumUut7tQQvAb%2FXb%2FT0bCAej9cODg7yt%2Bm%2F8q2%2F7OUHZ76PnZ1k2p0mJzlykmPancbOTnL0whHs7CQfb%2B5mx2d3sH79%2BtCRI0c6FeaOr9ICrIQfLvA%2B8BGNXxi4R6HrisJVUWrxAVW2oMFf0Aczim8o3kV6enowDIPjF9%2Fk%2BMU3S3rrjzMMg56eHr%2BxP7qKFbASfojG6kpeDGs1tiW53RxwWT%2Bin5q8w4xpQK5evQpAR30H7ZH2khNvj7TTUd8BgD4rqmu1ZKX8qNeY%2BfHz4zlXDgT5E8tpCTUq7XSBC4Euv8227TV9fX1E73%2BYtvo27BmbS9cvFVTY3bSRFza9yOcf6Gfmygy7d%2B%2Fm%2FPnzF4DvrsBLhnJlJfwIKXxv1PheAE4qK6p4H9AGbNKTuhngBPBPXYRe4IemaT5kWZbR19fHNbmGnZ1k4r3U4glDR30Hm5qjbGjsImJEOHbsGHv27JFz5869o0eFq01Jq%2BmHAXwI6FFKagMTgHM7GzFDS%2BoeLSMv7zjzC9x4Y7gxFovVDAwMEI1GaWlpWSzRVCrFwYMH%2FXfxZ4AfAa8B%2F7lDaGg1%2FQgp43lfK0yqtRMuJa3ceKe5DfgYsCYAZ2ngD8CfAkzqTpW7xY%2F%2FSznyX%2FVeUb2kVmX4AAAAAElFTkSuQmCC">ERROR</object></object></object></div><div id="eyes-b"></div><div id="eyes-c"></div></div> <!-- that's a PNG with 8bit alpha containing two eyes -->
<div class="nose"><div><div></div></div></div>
<div class="empty"><div></div></div>
<div class="smile"><div><div><span><em><strong></strong></em></span></div></div></div>
<div class="chin"><div>&nbsp;</div></div>
<div class="parser-container"><div class="parser"><!-- ->ERROR<!- --></div></div> <!-- two dashes is what delimits a comment, so the text "->ERROR<!-" earlier on this line is actually part of a comment -->
<ul>
<li class="first-part"></li>
<li class="second-part"></li>
<li class="third-part"></li>
<li class="fourth-part"></li>
</ul>
<div class="image-height-test"><table><tr><td><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAFSDNYfAAAAaklEQVR42u3XQQrAIAwAQeP%2F%2F6wf8CJBJTK9lnQ7FpHGaOurt1I34nfH9pMMZAZ8BwMGEvvh%2BBsJCAgICLwIOA8EBAQEBAQEBAQEBK79H5RfIQAAAAAAAAAAAAAAAAAAAAAAAAAAAID%2FABMSqAfj%2FsLmvAAAAABJRU5ErkJggg%3D%3D" alt=""></td></tr></table></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:20px solid transparent;
border-width: 10px 20px 30px 40px;
background: green;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
</style>
</head>
<body>
<div class="medium">
<div style="background:url(../../assets/image.jpg);background-clip: border-box;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: padding-box;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: content-box;"></div>
<div style="background:url(../../assets/image.jpg);"></div>
</div>
<div class="medium">
<div style="background:url(../../assets/image.jpg);background-clip: border-box; background-repeat: no-repeat;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: padding-box; background-repeat: repeat-y;"></div>
<div style="background:url(../../assets/image.jpg);background-clip: content-box; background-repeat: repeat-x;"></div>
<div style="background:url(../../assets/image.jpg); background-repeat: no-repeat;"></div>
</div>
<div class="medium">
<div style="background-clip: border-box;"></div>
<div style="background-clip: padding-box;"></div>
<div style="background-clip: content-box;"></div>
<div style=""></div>
</div>
</body>
</html>

View File

@@ -1,19 +1,19 @@
<!--
* @author Niklas von Hertzen <niklas at hertzen.com>
* @created 15.7.2011
* @website http://hertzen.com
-->
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
@@ -21,7 +21,7 @@
.medium div{
width:200px;
height:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
@@ -31,79 +31,22 @@
clear:both;
}
div{
display:block;
div{
display:block;
}
.encoded {
background:url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCABLAEsDAREAAhEBAxEB/8QAHAAAAgMBAQEBAAAAAAAAAAAABQcEBggCAwAJ/8QANxAAAgECBQEHAQUIAwEAAAAAAQIDBBEABQYSITEHExQiQVFhcTKBkbHBCCNCUnLR4fAWYqLx/8QAGwEAAQUBAQAAAAAAAAAAAAAAAQIDBAUGAAf/xAAzEQACAgECAwQHCAMAAAAAAAAAAQIDEQQhEjFBIlFhcRMUgZGhsdEFFSNCUsHh8DKi8f/aAAwDAQACEQMRAD8AxREirbfKlv6rYxDz0R6mmj2eohpqrLKmGVTJTVsEpG65sHF8dBSakpLmn8hq5pxTXRo0DlaCn1dB7eJZfxxD0b/FiyFqVmmQ1Yo/KMaRFBIj5tCTFSG3SsgP/sf3x0t0JTJtLTlJa0FSP31+R/0TCcYG8nM0Q9sLAQJoRzgnECoiFjggB7RC5wsSZHTLaGCQpJBSq68EbQ1sUjst72bdU1dy9x1mlPQJlNQY0p+9Rd67YwDcG/HGBVK12JPOPMF1darbUVt4GgoazdmeW1YNxKaea/8AUoJxVafsTj4P9xm5cVcvJjjhljjlRZDtBPt1xqlzMu3tsNjSulMrziigkVUkUOHK9RcdD8Ys6owcStsnJMsFV2ZUrxVDRqQ0luSL9P8AH5YcdUWhtWtC91T2e1mWRPUQx740BLKt7/X6YhTqcd0S4WKWxQZhiOPA+ccYICAy3Y4UAyXS/uwFXLGFv5kX++KCWHu5m6i3y4SbI7y0k0bZbZHQqTtT2/qw3FRUk1P5/QVJtxa4X8PqNHIanxmndMzg+Z6KEc+6kqfyxG4MXuPiyFJ4qcn3F5o9RGrzPbI5aRz6np7D2GLpSbfiZ9xWMD20Fq5MlgpF72PdNIyGE8OFFvMPjn7/ALsWlE3FIrbYcTH5keaw11MtnVz063xZqWSvccMkZhRwT08m4KRY3BwibSW4uCeTJ+qKA5PnuYURBXuZmVQ/Xbe6n8CMVTWGWXQATOCCLjBAQXbzHphQkyTS5XE1HTXpttTtPfB5bruvxt+LdcUc7e08PY3dcez2luTIsm3GwjhF+OW/xhl3eLHuHwLpo1pn0lkrRh5no2qICkSGRmZJSQAo6khhxhmxS9azFc8P3kLsqiUZeQ3dPafhmrFkU7HUh3Exsyi9269LeuL2Me0ZWUsIM6p7EKjtkzTLXi1XVaWaEvfwSkmqJZdq7Qy9LdSeOeD6WlSRAm8YbNDN2f6ry6lpqXRGq6RqjLXjapgzaiM3i4woPdK6sCpYXs3NuOuJXBjkR3JNbjRy+KvkpF8bEaaV1tYi5/8AuI0k2tx1NLkYQ/aB1XmucdrGeHLNQ5jl+X07ikjiptiITGNrNfaSSWvyTf7gMUt2olGxxjyRe0Uw9GnOOWxXyVuoVLM+rc7kBvYeIC2/BcNeszew+qK1+UGTVmdGRj/yjPV+PGH+2HVqJ94PRV/pQtos+1SPs0WVi/S5kP64S9PpespfAlLUaz9Efj9QtkEmuc/roaajgycB2AaRo5NsY9WJB6DHeraVvCcm/Z9BM9Vq4rMlFL2/Dc0dp7L4dO5eKWl6k7pJtoVpGtyxA6dOgxIqpVawipuvldLMgtRzIjbDJsBYeW32sSlAiNkLtI7Y6nRkMcOWGeKYKqrMsO8Ru91Qkeo3bfKP1wpN8XDEXCtSWWGv2Le1nXk2qJn1VXjNVrHWJJpy3euIRtLcKAFAO0X6k8Di+Jzmk4xiQ5UWRUnYsGvO23thyvs60bUVIq0GcV1O4yykU3ldyNvegeiITck8XFuThOqvhRXxN7vkDSUTvsUUtlzPzdzqecS940zOzkvI7HlmuSxPyeuMfF5e/M1/CkerKFiaZQZIiN19wJ+oGE5T2Ow2QGkhc7u7fn6YWmDhYMnyDLGBanrp43JG8TQhx09CtrYs5aaPSTIEPtKf54L2PHzyXrRmVQZTlJZFQyyctIqlbj0HOHKqvRrfdkbUah3yzyXcWnK6lK4Ps+0h2snqDiXFZITeA/leTSV1ZGii537DZbgAjn64kRhljbkkhqt+zkmo6ekLx0s61ZNJJT1se5GABNn4IN/0BxIlpctSi8MajqOcZLKG32e9immtA6berly+nVqaF3ip4dyxR2UndybliR1J4Fhh6FSj2p7sZna32Y8jM+faKzjU2YNmWZ54tZmFSAZJ54mdiB0A6WUDoBYYoZ6C62XHZYm34MtofaNNceCFbS80B6jskrWRQmb0e8g3D07gLb7zfCPuufSa9zF/elfWD96Kzn2iZ8mjqFmmjd46hoN5tGsjKBu2BjubhlPAsAet8Vl+mlTlt9f7jr+xa1aiNuMLpn+9Clyaan3tt7vbfjzjEbLJWUUxqqxBtdR6e+NJgy/IZmlKiTMdPpO8RiuWsL/aAPUYUlsBvvO8glmi1LHGEURzAhh6gAXGFxW4mT2HbpOhTxSSOGWNrbghBJ+behxOgiLKRobSWo6fJKGE11YYYmO9TI/mP8J2jkkm1uP1xNTSW5F3b2CGp9UxHTGZVCkmnaMokbdTu8vP44alPIBCxkKFHJsStwfS5FsN5AfSSkpMyXEaAFiebLwCfxOCAomrshgGofFzw0UVPVoHlqJgu+NhZJG5IFhwbnnzcemKfVVQVnHJJZ69f748zQaO6cqlFNvG2Onev+chT1MEwqZfDyQdxuOwVDhZAL9CPjpjOvbkzQLxFk8TpIA6kpe7WHpjTYMrnI49KVVHmGX0cdOsnhwLI5Ujc3sB1Nr9elz64dSBkFZ+9To7PaatqwsNNFUhWk3G0YY28xP8PNyfS+OinGRzawa07E4IaySCaVVeOQdOoBHS/vixqIFnMtnb8tatHpxcpjgepjqHlKzOYwU2bSoYA26g9PTBtWcYG4y4XuU4Z9mE+TJl88cMUJ2FlDFzcG9gbDi+GEmFyT5AWSInvFFyFJHHXp/nBE5PHLA0Ek9M7EtNEYw59T8/76YMe46W+5W9dd+cm76IWMDhZbRxuAkg2tuDgggMFuBzyeuIesjmviXTwT2fmWOhklY4Pr5815Cxly+GRwxy+lclQdypMoPA5spKj7iR7YoJVtPHAv8AZfLK9xpI2Jr/ACfwItT2YTToVjCsTx5W639Mah0dxkVbgjZVojV+nqhXyeKOeON9yxzuFHwASeAOTb3OBGqaFemj1Gpn2VVuudD51kdRkcFDPWqIvE1dTG7qLLd1CX5JDDn64kcDwMuxE3sNoNWdnarltd3FZRx37mYVQOxFAsGH2r9eelrYEISiGVkZLI3M/wBRTallp5p9qLGuyNAb2FuTf5OHWskdsATRb1L2Fgf9/XCcHZIrRHxJHsdxHryPyIwMCiPLAwa9gHS4t7m/GOwcQ66natgqaSQeWqjaJSSP4hcG5vYggdQbH3wmUOOLj3jlcuCal3CIqWplqJAaWGNtxJSSdkYH1upVbG/wMZnixtlL2r+PkazhT33+P8jGy5iViYnzWBv841hjwplh3RwX9SXPyS2OQcBNpWpYZWiO096E6X8u61vwxyE9AplEjLPmLBjdQFHwOOMFdRL5BOjdmijJJJAU/wC/gMcgs6pnLIpJuTUAH6XtgndWjwy1Qc1qeP5fzwFzO6EOR28dSDcfPVhW56j93x+eB1CBdXVEtLmmWiJzGDOQbH03DCJbNBXJiU1zUyUGtc+p6dzDBFXzqkacBQJDYDGV1F9kLpxi9k38zbaaqE6ISkt2kf/Z");
background-position: center center;
}
.linearGradient {
/*background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 0, 0)), to(rgb(0, 0, 255)));*/
background: -webkit-linear-gradient(top left, #f00, #00f, #BADA55, rgba(0, 0, 255,0.5));
background: -moz-linear-gradient(top right, #f00, #00f, #BADA55, rgba(0, 0, 255,0.5));
}
.linearGradient2 {
background: -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)));
}
</style>
</head>
<body>
<div class="medium">
<div class="linearGradient">&nbsp;</div>
<div class="linearGradient2">&nbsp;</div>
</div>
<div class="medium">
<div style="background:url(image.jpg);"></div>
<div style="background:url(image.jpg) repeat-x;"></div>
<div style="background:url(image.jpg) repeat-y;"></div>
<div style="background:url(image.jpg) no-repeat;"></div>
</div>
<div class="small">
<div style="background:url(image.jpg);"></div>
<div style="background:url(image.jpg) repeat-x;"></div>
<div style="background:url(image.jpg) repeat-y;"></div>
<div style="background:url(image.jpg) no-repeat;"></div>
</div>
<div class="medium">
<div style="background:url(image.jpg) center center;"></div>
<div style="background:url(image.jpg) repeat-x center center;"></div>
<div style="background:url(image.jpg) repeat-y center center;"></div>
<div style="background:url(image.jpg) no-repeat center center;"></div>
</div>
<div class="small">
<div style="background:url(image.jpg) center center;"></div>
<div style="background:url(image.jpg) repeat-x center center;"></div>
<div style="background:url(image.jpg) repeat-y center center;"></div>
<div style="background:url(image.jpg) no-repeat center center;"></div>
</div>
<div class="medium">
<div style="background:url(image.jpg) 50px 50px;"></div>
<div style="background:url(image.jpg) repeat-x 50px 50px;"></div>
<div style="background:url(image.jpg) repeat-y 50px 50px;"></div>
<div style="background:url(image.jpg) no-repeat 50px 50px;"></div>
</div>
<div class="medium">
<div style="background:url(image.jpg) no-repeat -15% 25px;"></div>
<div style="background-image:url(image.jpg), url(image2.jpg); background-repeat: repeat-x; background-position: 50px 50px, 100px 100px;"></div>
</div>
<div class="medium">
<div class="encoded"></div>
</div>
</body>
</html>
</html>

View File

@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
.linearGradient {
/*background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 0, 0)), to(rgb(0, 0, 255)));*/
background: -webkit-linear-gradient(top left, #f00, #00f, #BADA55, rgba(0, 0, 255,0.5));
background: -moz-linear-gradient(top right, #f00, #00f, #BADA55, rgba(0, 0, 255,0.5));
}
.linearGradient2 {
background: -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)));
}
.linearGradient3 {
/* FF 3.6+ */
background: -moz-linear-gradient(left, #ff0000, #ffff00, #00ff00);
/* Chrome,Safari4+ */
background: -webkit-gradient(linear, left top, right top, color-stop(#ff0000), color-stop(#ffff00), color-stop(#00ff00));
/* Chrome 10+, Safari 5.1+ */
background: -webkit-linear-gradient(left, #ff0000, #ffff00, #00ff00);
/* Opera 11.10+ */
background: -o-linear-gradient(left, #ff0000, #ffff00, #00ff00);
/* IE 10+ */
background: -ms-linear-gradient(left, #ff0000, #ffff00, #00ff00);
/* W3C */
background: linear-gradient(left, #ff0000, #ffff00, #00ff00);
}
.linearGradient4 {
/* FF 3.6+ */
background: -moz-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
/* Chrome, Safari 4+ */
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #cedbe9), color-stop(17%, #aac5de), color-stop(50%, #6199c7), color-stop(51%, #3a84c3), color-stop(59%, #419ad6), color-stop(71%, #4bb8f0), color-stop(84%, #3a8bc2), color-stop(100%, #26558b));
/* Chrome 10+, Safari 5.1+ */
background: -webkit-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
/* Opera 11.10+ */
background: -o-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
/* IE10+ */
background: -ms-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
/* W3C */
background: linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
}
.linearGradient5 {
/* FF 3.6+ */
background: -moz-linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
/* Chrome, Safari 4+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f0b7a1), color-stop(50%, #8c3310), color-stop(51%, #752201), color-stop(100%, #bf6e4e));
/* Chrome 10+, Safari 5.1+ */
background: -webkit-linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
/* Opera 11.10+ */
background: -o-linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
/* IE 10+ */
background: -ms-linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
/* W3C */
background: linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
}
.linearGradient6 {
/* FF 3.6+ */
background: -moz-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6, #26558b 100%);
/* Chrome 10+, Safari 5.1+ */
background: -webkit-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6, #26558b 100%);
/* Opera 11.10+ */
background: -o-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6, #26558b 100%);
/* IE10+ */
background: -ms-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6, #26558b 100%);
/* W3C */
background: linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6, #26558b 100%);
}
.linearGradient7 {
background: #cce5f4;
background: -moz-linear-gradient(top, #cce5f4 0%, #00263c 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #cce5f4), color-stop(100%, #00263c));
background: -webkit-linear-gradient(top, #cce5f4 0%, #00263c 100%);
background: -o-linear-gradient(top, #cce5f4 0%, #00263c 100%);
background: -ms-linear-gradient(top, #cce5f4 0%, #00263c 100%);
background: linear-gradient(to bottom, #cce5f4 0%, #00263c 100%);
}
.linearGradient8 {
background: linear-gradient(to top left, #fff 0%, #00263c 100%);
}
.linearGradient9 {
background: linear-gradient(to top left, white 0%, black 100%);
}
</style>
</head>
<body>
<div class="medium">
<div class="linearGradient">&nbsp;</div>
<div class="linearGradient2">&nbsp;</div>
<div class="linearGradient3">&nbsp;</div>
<div class="linearGradient4">&nbsp;</div>
<div class="linearGradient5">&nbsp;</div>
<div class="linearGradient6">&nbsp;</div>
<div class="linearGradient7">&nbsp;</div>
<div class="linearGradient8">&nbsp;</div>
<div class="linearGradient9">&nbsp;</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
</style>
</head>
<body>
<div class="medium">
<div style="background-image:url(../../assets/image.jpg), url(../../assets/image2.jpg); background-repeat: repeat-x; background-position: 50px 50px, 100px 130px;"></div>
<div style="background-image:url(../../assets/image.jpg), url(../../assets/image2.jpg); background-repeat: repeat-x; background-position: 50px 50px, 20px -20px;"></div>
<div style="background-image:url(../../assets/image.jpg), url(../../assets/image2.jpg); background-repeat: repeat-x; background-position: 50px 50px, 450px 100px;"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
</style>
</head>
<body>
<div class="medium">
<div style='background:url("../../assets/image.jpg") center center;'></div>
<div style="background:url('../../assets/image.jpg') repeat-x center center;"></div>
<div style="background:url(../../assets/image.jpg) repeat-y center center;"></div>
<div style="background:url(../../assets/image.jpg) no-repeat center center;"></div>
</div>
<div class="small">
<div style="background:url(../../assets/image.jpg) center center;"></div>
<div style="background:url(../../assets/image.jpg) repeat-x center center;"></div>
<div style="background:url(../../assets/image.jpg) repeat-y center center;"></div>
<div style="background:url(../../assets/image.jpg) no-repeat center center;"></div>
</div>
<div class="medium">
<div style="background:url(../../assets/image.jpg) -15% 25px;"></div>
<div style="background:url(../../assets/image.jpg) repeat-x 50px 50px;"></div>
<div style="background:url(../../assets/image.jpg) repeat-y 50px 50px;"></div>
<div style="background:url(../../assets/image.jpg) no-repeat 50px 50px;"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,161 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
.radialGradient {
/* FF 3.6+ */
background: -moz-radial-gradient(center, ellipse cover, #959595 0%, #0d0d0d 48%, #2f7bd8 50%, #0a0a0a 64%, #4e4e4e 80%, #383838 87%, #1b1b1b 100%);
/* Chrome, Safari 4+ */
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#959595), color-stop(48%,#0d0d0d), color-stop(50%,#2f7bd8), color-stop(64%,#0a0a0a), color-stop(80%,#4e4e4e), color-stop(87%,#383838), color-stop(100%,#1b1b1b));
/* Chrome 10+, Safari 5.1+ */
background: -webkit-radial-gradient(center, ellipse cover, #959595 0%, #0d0d0d 48%,#2f7bd8 50%,#0a0a0a 64%,#4e4e4e 80%,#383838 87%,#1b1b1b 100%);
/* Opera 12+ */
background: -o-radial-gradient(center, ellipse cover, #959595 0%, #0d0d0d 48%,#2f7bd8 50%,#0a0a0a 64%,#4e4e4e 80%,#383838 87%,#1b1b1b 100%);
/* IE 10+ */
background: -ms-radial-gradient(center, ellipse cover, #959595 0%,#0d0d0d 48%,#2f7bd8 50%,#0a0a0a 64%,#4e4e4e 80%,#383838 87%,#1b1b1b 100%);
/* W3C */
background: radial-gradient(center, ellipse cover, #959595 0%,#0d0d0d 48%,#2f7bd8 50%,#0a0a0a 64%,#4e4e4e 80%,#383838 87%,#1b1b1b 100%);
}
.radialGradient2 {
/* thx to https://twitter.com/#!/markjmclaren/status/13067366701 */
background-color: #FFF;
background-image:
-webkit-gradient(radial, 45 45, 10, 52 50, 30, from(#A7D30C), to(rgba(1,159,98,0)), color-stop(90%, #019F62)),
-webkit-gradient(radial, 105 105, 20, 112 120, 50, from(#ff5f98), to(rgba(255,1,136,0)), color-stop(75%, #ff0188)),
-webkit-gradient(radial, 95 15, 15, 102 20, 40, from(#00c9ff), to(rgba(0,201,255,0)), color-stop(80%, #00b5e2)),
-webkit-gradient(radial, 0 150, 50, 0 140, 90, from(#f4f201), to(rgba(228, 199,0,0)), color-stop(80%, #e4c700));
background-image:
-moz-radial-gradient(42px 42px, circle farthest-corner, #A7D30C 0%, #A7D30C 3%, rgba(1,159,98,0) 11%),
-moz-radial-gradient(45px 45px, circle farthest-corner, #019F62 0%, #019F62 13%, rgba(255,255,255,0) 16%, rgba(255,255,255,0) 100%),
-moz-radial-gradient(102px 102px, circle farthest-corner, #ff5f98 0%, #ff5f98 15%, rgba(255,1,136,0) 27%),
-moz-radial-gradient(105px 105px, circle farthest-corner, #FF0188 0%, #FF0188 28%, rgba(255,255,255,0) 32%, rgba(255,255,255,0) 100%),
-moz-radial-gradient(92px 12px, circle farthest-corner, #00c9ff 0%, #00c9ff 10%, rgba(0,181,226,0) 26%),
-moz-radial-gradient(95px 15px, circle farthest-corner, #00b5e2 0%, #00b5e2 28%, rgba(255,255,255,0) 31%, rgba(255,255,255,0) 100%),
-moz-radial-gradient(0px 150px, circle farthest-corner, #f4f201 0%, #f4f201 25%, rgba(228,199,0,0) 45%),
-moz-radial-gradient(0px 150px, circle farthest-corner, #e4c700 0%, #e4c700 47%, rgba(255,255,255,0) 50%, rgba(255,255,255,0) 100%);
}
.radialGradient3 {
background: -moz-radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient4 {
background: -moz-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient5 {
background: -moz-radial-gradient(75% 19%, ellipse farthest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, ellipse farthest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, ellipse farthest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, ellipse farthest-side, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, ellipse farthest-side, #ababab, #0000ff 33%,#991f1f 100%)
}
.radialGradient6 {
background: -moz-radial-gradient(75% 19%, ellipse farthest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, ellipse farthest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, ellipse farthest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, ellipse farthest-corner, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, ellipse farthest-corner, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient7 {
background: -moz-radial-gradient(75% 19%, circle contain, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, circle contain, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, circle contain, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, circle contain, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, circle contain, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient8 {
background: -moz-radial-gradient(75% 19%, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(75% 19%, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(75% 19%, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(75% 19%, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(75% 19%, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient9 {
background: -moz-radial-gradient(right 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(right 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(right 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(right 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(right 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient10 {
background: -moz-radial-gradient(left 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(left 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(left 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(left 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(left 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
}
.radialGradient11 {
background: -moz-radial-gradient(left top, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -webkit-radial-gradient(left top, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -o-radial-gradient(left top, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: -ms-radial-gradient(left top, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
background: radial-gradient(left top, circle cover, #ababab, #0000ff 33%,#991f1f 100%);
}
</style>
</head>
<body>
<div class="medium">
<div class="radialGradient">&nbsp;</div>
<div class="radialGradient2">&nbsp;</div>
<div class="radialGradient3">&nbsp;</div>
<div class="radialGradient4">&nbsp;</div>
<div class="radialGradient5">&nbsp;</div>
<div class="radialGradient6">&nbsp;</div>
<div class="radialGradient7">&nbsp;</div>
<div class="radialGradient8">&nbsp;</div>
<div class="radialGradient9">&nbsp;</div>
<div class="radialGradient10">&nbsp;</div>
<div class="radialGradient11">&nbsp;</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<title>Background attribute tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
html {
background-color: red;
}
body {
background-color: lime;
}
.small div{
width:100px;
height:100px;
float:left;
margin:10px;
border:1px solid #000;
}
.medium div{
width:200px;
height:200px;
float:left;
margin:10px;
border:1px solid #000;
}
.small, .medium{
clear:both;
}
div{
display:block;
}
</style>
</head>
<body>
<div class="medium">
<div style="background:url(../../assets/image.jpg);"></div>
<div style="background:url(../../assets/image.jpg) repeat-x;"></div>
<div style="background:url(../../assets/image.jpg) repeat-y;"></div>
<div style="background:url(../../assets/image.jpg) no-repeat;"></div>
</div>
<div class="small">
<div style="background:url(../../assets/image.jpg);"></div>
<div style="background:url(../../assets/image.jpg) repeat-x;"></div>
<div style="background:url(../../assets/image.jpg) repeat-y;"></div>
<div style="background:url(../../assets/image.jpg) no-repeat;"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>Background size tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style>
.horizontal div, .vertical div {
display: block;
background:url("../../assets/image.jpg") center center;
}
.vertical {
float: right;
}
.horizontal {
float: left;
}
.horizontal div {
width: 400px; height: 100px;
}
.vertical div {
width: 200px; height: 200px;
}
.container {
float: left;
border: 1px solid black;
width: 150px;
height: 200px;
background-image: url(../../assets/image.jpg);
background-size: 34px;
background-repeat: no-repeat;
background-position: center;
}
</style>
</head>
<body>
<div class="container"></div>
<div class="container" style="background-repeat: repeat-y"></div>
<div class="container" style="background-repeat: repeat-x"></div>
<div class="container" style="background-size: 150% auto"></div>
<div class="horizontal">
<div style='background-size: cover;'></div>
<div style='background-size: contain;'></div>
<div style='background-size: auto 100%;'></div>
<div style='background-size: auto;'></div>
</div>
<div class="vertical">
<div style='background-size: cover;'></div>
<div style='background-size: contain;'></div>
<div style='background-size: auto 100%;'></div>
<div style='background-size: auto;'></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style: dashed;
}
.box1 {
border-width: 1px;
border-color: #00b5e2;
}
.box2 {
border-width: 3px;
border-color: red;
}
.box3 {
border-width: 10px;
}
.box4 {
border-width: 50px;
border-color: green;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style: dotted;
}
.box1 {
border-width: 1px;
border-color: #00b5e2;
}
.box2 {
border-width: 3px;
border-color: red;
}
.box3 {
border-width: 10px;
}
.box4 {
border-width: 50px;
border-color: green;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style:double;
}
.box1 {
border-width: 1px;
border-color: #00b5e2;
}
.box2 {
border-width: 3px;
border-color: red;
}
.box3 {
border-width: 10px;
}
.box4 {
border-width: 50px;
border-color: green;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style: inset;
}
.box1 {
border-width: 1px;
border-color: #00b5e2;
}
.box2 {
border-width: 3px;
border-color: red;
}
.box3 {
border-width: 10px;
}
.box4 {
border-width: 50px;
border-color: green;
}
.box5 {
border-width: 50px;
border-color: rgb(0, 0, 0);
}
input {
border-width: 50px;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
<div class="box5">&nbsp;</div>
<input type="text" />
</body>
</html>

View File

@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style: solid;
border-radius: 50px;
}
.box1 {
border-width: 1px;
border-left-color: #00b5e2;
border-top-color: red;
border-right-color: green;
}
.box2 {
border-width: 3px;
border-left-color: #00b5e2;
border-top-color: red;
border-right-color: green;
}
.box3 {
border-width: 10px;
border-left-color: transparent;
border-top-color: red;
border-right-color: green;
}
.box4 {
border-width: 50px;
border-left-color: transparent;
border-top-color: red;
border-top-width: 10px;
border-right-color: green;
border-bottom-right-radius: 190px;
background-clip: padding-box;
}
.box5 {
margin: 100px;
border-width: 50px;
border-left-color: transparent;
border-top-color: red;
border-top-width: 10px;
border-right-color: green;
border-radius: 25px;
background-clip: padding-box;
}
.box6 {
height: 200px;
width: 200px;
border-radius: 200px;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
<div class="box5">&nbsp;</div>
<div class="box6">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Borders tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<style type="text/css">
div {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
background:#6F428C;
border-style: solid;
}
.box1 {
border-width: 1px;
border-color: #00b5e2;
}
.box2 {
border-width: 3px;
border-color: red;
}
.box3 {
border-width: 10px;
}
.box4 {
border-width: 50px;
border-color: green;
}
html {
background: #3a84c3;
}
</style>
</head>
<body>
<div class="box1">&nbsp;</div>
<div class="box2">&nbsp;</div>
<div class="box3">&nbsp;</div>
<div class="box4">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>Inline text in the top element</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../test.js"></script>
<style>
span {
color:blue;
}
p {
background-color: green;
}
</style>
</head>
<body>
Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.
</body>
</html>

43
tests/cases/clip.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Inline text in the top element</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../test.js"></script>
<style>
span {
color:blue;
}
p {
background-color: green;
}
div {
background: red;
border: 5px solid blue;
}
</style>
</head>
<body>
<div style="clip: rect(0px, 400px, 50px, 200px); ">Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.</div>
<div style="clip: rect(0px, 400px, 50px, 200px); position: relative; ">Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.</div>
<div style="clip: rect(0px, 400px, 50px, 200px); position: fixed; ">Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.</div>
<div style="clip: rect(0px, 400px, 50px, 200px); position: absolute; top: 250px; left: 500px;">Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.</div>
</body>
<div style="clip: rect(0px, 400px, 50px, 200px); position: absolute; top: 500px;">Some inline text <span> followed by text in span </span> followed by more inline text.
<p>Then a block level element.</p>
Then more inline text.</div>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>cross-origin iframe test</title>
<script>
var h2cOptions = {proxy: "http://localhost:8082"};
</script>
<script type="text/javascript" src="../test.js"></script>
</head>
<body>
<iframe src="http://hertzen.com/" width="800" height="800"></iframe>
</body>
</html>

View File

@@ -1,23 +1,21 @@
<!--
* @author Niklas von Hertzen <niklas at hertzen.com>
* @created 16.7.2011
* @website http://hertzen.com
-->
<!DOCTYPE html>
<html>
<head>
<title>Form tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="#" type="text/css" rel="stylesheet">
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="../test.js"></script>
<style>
input[type="radio"], input[type="checkbox"] {
margin: 10px;
display: inline-block;
}
</style>
</head>
<body>
<body>
<input type="hidden" value="THIS SHOULD NOT BE VISIBLE!" />
<input type="text" value="textbox" />
<input type="password" value="textbox" />
<input type="text" value="textbox" style="border:5px solid navy;" />
<input type="text" value="textbox" style="border:5px solid navy;height:40px;" />
@@ -53,13 +51,31 @@
<input type="submit" value="Submit" style="width:200px;" />
<input type="Button" value="Button" style="width:200px;height:50px;" />
<input type="Reset" value="Reset" style="width:200px;height:50px;text-align:left;" />
<hr />
<textarea> </textarea>
<textarea style="border-width:10px;"></textarea>
<textarea> text </textarea>
<textarea style="border-width:10px;">text</textarea>
<hr />
<input type="radio" value="radio1" />
<input type="radio" value="radio2" style="transform:scale(3)" />
<input type="RADIO" value="radio3" checked />
<input type="radio" value="radio4" style="transform:scale(3)" checked />
<input type="radio" value="radio5" style="transform:scale(3); background: red;" />
<input type="radio" value="radio6" style="width: 200px;" />
<input type="radio" value="radio6" style="width: 200px; height: 200px;" />
<input type="radio" value="radio6" style="width: 200px; height: 200px;" checked />
<input type="checkbox" value="checkbox1" />
<input type="chECKbox" value="checkbox2" style="transform:scale(3)" />
<input type="checkbox" value="checkbox3" checked />
<input type="checkbox" value="checkbox4" style="transform:scale(3)" checked />
<input type="checkbox" value="checkbox5" style="transform:scale(3); background: red;" />
<input type="checkbox" value="checkbox6" style="width: 200px;" />
<input type="checkbox" value="checkbox6" style="width: 200px; height: 200px;" />
<input type="checkbox" value="checkbox6" style="width: 200px; height: 200px;" checked />
<div style="position: absolute; width: 10px; height: 10px; border:1px solid rgb(165,165,165); border-radius: 3px; background: rgb(222,222,222)"></div>
</body>
</html>

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>google maps</title>
<script>
var dontRun = true;
</script>
<script type="text/javascript" src="../test.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<style>
#map {
width: 100%;
height: 500px;
position: absolute;
top: 0;
}
body, html {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div>
<h2>Google maps</h2>
<div id="map"></div>
<script>
var mapOptions = { zoom: 13, center: new google.maps.LatLng(60.1656623, 24.9477731) };
var map = new google.maps.Map(document.querySelector('#map'), mapOptions);
google.maps.event.addListenerOnce(map, 'idle', function(){
if (typeof(window.run) === "function") {
window.run();
} else {
dontRun = undefined;
}
});
</script>
</div>
</body>
</html>

10
tests/cases/iframe.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>iframe test</title>
<script type="text/javascript" src="../test.js"></script>
</head>
<body>
<iframe src="/tests/assets/iframe/frame1.html" width="500" height="500"></iframe>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>External content tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<base href="http://www.google.com/" />
</head>
<body>
<h1>External image</h1>
<img src="http://www.google.com/logos/2011/gregormendel11-hp.jpg" style="border:5px solid black;" />
<h1>External image (using &lt;base&gt; href)</h1>
<img src="/logos/2011/gregormendel11-res.jpg" />
</body>
</html>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>Image tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
<script type="text/javascript">
function setUp() {
if ($('#testcanvas')[0].getContext) {
var ctx = $('#testcanvas')[0].getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
} else {
$('#testcanvas').remove();
}
};
</script>
</head>
<body>
<canvas id="testcanvas" style="width:700px;height:300px;"></canvas>
</body>
</html>

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>External content tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
h2cOptions = {useCORS: true, proxy: null};
</script>
<script type="text/javascript" src="../../test.js"></script>
</head>
<body>
<h1>External image (CORS)</h1>
<img src="http://localhost:8081/tests/assets/image2.jpg" />
</body>
</html>

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Image tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
</head>
<body>
Image without src attribute, should not crash:
<img style="width:50px;height:50px;border:1px solid red;display:block;" />
Image with broken src attribute, should not crash:
<img src="404" style="width:50px;height:50px;border:1px solid red;display:block;" />
<img src="404_2" style="width:50px;height:50px;border:1px solid red;display:block;" />
</body>
</html>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>Image tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../test.js"></script>
</head>
<body>
<img src="../../assets/image.jpg" />
<img src="../../assets/image.jpg" style="width:50px;height:400px;" />
<img src="../../assets/image.jpg" style="width:500px;" />
<img src="../../assets/image.jpg" style="width:100px;border-radius:50px;" />
<img src="../../assets/image.jpg" style="width:500px;height:40px;" />
<img src="../../assets/image.jpg" style="padding:20px;border:5px solid black;" />
<img src="../../assets/image.jpg" style="padding-bottom:20px;border-top:5px solid black;clear:both;" />
<img src="../../assets/image.jpg" style="padding-top:20px;border-top:5px solid black;clear:both;width:50px;" />
<img src="../../assets/image.jpg" style="padding-top:20px;border-top:5px solid black;clear:both;width:50px;height:25px;" />
<img src="../../assets/image.jpg" style="width:0px;height:0px;border:1px solid black" />
<img src="../../assets/image.jpg" style="width:0px;height:0px;" />
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Base64 svg</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="../../../test.js"></script>
</head>
<body>
<div>
Inline svg image: <br />
<img width="200" height="200" src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjMwNiIgaGVpZ2h0PSIyOTYiPjxkZWZzIGlkPSJkZWZzNCIgLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYyLjQ2OTk1LC00NzcuMjg2MykiIGlkPSJsYXllcjEiPjxwYXRoIGQ9Im0gMzE0LjE1NzQ1LDQ4MS42OTU1OCBjIC01OS4yMDA4OSwwLjUzNzc0IC0xMTQuODA5NzksMzYuNzIyMTkgLTEzNy4zMTI1LDk1LjM0Mzc1IC0yOS4zOTEyOSw3Ni41NjY5MyA4LjgzOTMyLDE2Mi40NTI0NiA4NS40MDYyNSwxOTEuODQzNzUgbCAzNC4wMzEyNSwtODguNjg3NSBjIC0yMC4wNjc4LC03LjcxMzU4IC0zNC4zMTI1LC0yNy4xNTMyNCAtMzQuMzEyNSwtNDkuOTM3NSAwLC0yOS41NDcyMyAyMy45NTI3NywtNTMuNSA1My41LC01My41IDI5LjU0NzIzLDAgNTMuNSwyMy45NTI3NyA1My41LDUzLjUgMCwyMi43ODQyNiAtMTQuMjQ0Nyw0Mi4yMjM5MiAtMzQuMzEyNSw0OS45Mzc1IGwgMzQuMDMxMjUsODguNjg3NSBjIDM5LjI5MDg1LC0xNS4wODIzNCA3MC4zMjM5LC00Ni4xMTU0IDg1LjQwNjI1LC04NS40MDYyNSAyOS4zOTEyOSwtNzYuNTY2OTMgLTguODM5MzIsLTE2Mi40ODM3MSAtODUuNDA2MjUsLTE5MS44NzUgLTE3Ljk0NTM3LC02Ljg4ODU5IC0zNi40MDg1MywtMTAuMDcwODcgLTU0LjUzMTI1LC05LjkwNjI1IHoiIGlkPSJwYXRoMjgzMCIgc3R5bGU9ImZpbGw6IzQwYWE1NDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzIwNTUyYTtzdHJva2Utd2lkdGg6Ny45OTk5OTk1MjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPjwvZz48L3N2Zz4=" /></div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More