Permalink
Browse files

Keep up with master

Conflicts:
	lib/jsdom/browser/domtohtml.js
	lib/jsdom/browser/index.js
	lib/jsdom/level1/core.js
	lib/jsdom/level2/events.js
	lib/jsdom/level2/html.js
	lib/jsdom/level3/core.js
	package.json
	test/level3/core/files/hc_nodtdstaff.xml.js
	test/level3/core/files/hc_staff.xml.js
	test/runner
  • Loading branch information...
2 parents 31df582 + fdd605c commit fb2ffd19efeddc6686fa9fbf97b380d56bdc53b6 @tmpvar committed Nov 21, 2011
Showing with 13,703 additions and 1,055 deletions.
  1. +108 −67 README.md
  2. +123 −0 changelog
  3. +9 −12 example/browser/browser.js
  4. +10 −11 example/node-xml/run.js
  5. +46 −47 example/pure/pure.js
  6. +3 −3 example/pure/run.js
  7. +3 −2 example/pure/sax-test.js
  8. +24 −24 example/sizzle/run.js
  9. +54 −69 lib/jsdom.js
  10. +52 −0 lib/jsdom/browser/documentfeatures.js
  11. +50 −90 lib/jsdom/browser/domtohtml.js
  12. +1,486 −125 lib/jsdom/browser/htmlencoding.js
  13. +17 −11 lib/jsdom/browser/htmltodom.js
  14. +107 −45 lib/jsdom/browser/index.js
  15. +100 −34 lib/jsdom/level1/core.js
  16. +9 −18 lib/jsdom/level2/core.js
  17. +61 −92 lib/jsdom/level2/events.js
  18. +175 −69 lib/jsdom/level2/html.js
  19. +5 −13 lib/jsdom/level2/languages/javascript.js
  20. +54 −38 lib/jsdom/level2/style.js
  21. +6 −3 lib/jsdom/level3/core.js
  22. +3 −0 lib/jsdom/level3/events.js
  23. +29 −0 lib/jsdom/util.js
  24. +129 −66 package.json
  25. +15 −0 test/browser/index.js
  26. +0 −8 test/env.html
  27. +46 −0 test/jsdom/files/ctx-script1.js
  28. +24 −0 test/jsdom/files/ctx-script2.js
  29. +23 −0 test/jsdom/files/ctx-test.html
  30. +457 −0 test/jsdom/files/ender-qwery.js
  31. +13 −0 test/jsdom/files/iframe.html
  32. +8,981 −0 test/jsdom/files/jquery.js
  33. +649 −91 test/jsdom/index.js
  34. +12 −0 test/jsdom/leak.js
  35. +5 −0 test/jsonp/jquery-1.6.4.min.js
  36. +43 −0 test/jsonp/jsonp.js
  37. +24 −0 test/level1/core.js
  38. +5 −7 test/level2/core.js
  39. +1 −2 test/level2/core/files/hc_staff.xml.js
  40. +1 −2 test/level2/core/files/staff.xml.js
  41. +1 −2 test/level2/core/files/staff2.xml.js
  42. +1 −2 test/level2/core/files/staffNS.xml.js
  43. +15 −9 test/level2/events.js
  44. +4 −2 test/level2/events/files/hc_staff.xml.js
  45. +35 −4 test/level2/html.js
  46. +78 −45 test/level2/style.js
  47. +9 −0 test/level2/style/external_css.html
  48. +1 −2 test/level3/core/files/hc_nodtdstaff.xml.js
  49. +1 −0 test/level3/core/files/hc_staff.xml.js
  50. +26 −13 test/runner
  51. +0 −1 test/status.json
  52. +0 −14 test/testlog.txt
  53. +21 −0 test/window/files/163.js
  54. +2 −0 test/window/files/179.js
  55. +9 −0 test/window/files/frameset_parent.html
  56. +13 −0 test/window/files/iframe.html
  57. +6 −0 test/window/files/iframe_parent.html
  58. +9 −0 test/window/files/multiple_iframe_parent.html
  59. +7 −0 test/window/files/simple_iframe.html
  60. +4 −0 test/window/files/timer_in_context.js
  61. +335 −0 test/window/frame.js
  62. +37 −1 test/window/index.js
  63. +127 −0 test/window/script.js
  64. +0 −11 wscript
View
175 README.md
@@ -1,4 +1,4 @@
-# jsdom 0.2.0
+# jsdom
A javascript implementation of the W3C DOM.
@@ -12,6 +12,15 @@ or
cd jsdom
npm link
+## Human contact
+
+see: [mailing list][]
+
+ [mailing list]: http://groups.google.com/group/jsdom
+
+
+
+
## Easymode
Bootstrapping a DOM is generally a difficult process involving many error prone steps. We didn't want jsdom to fall into the same trap and that is why a new method, `jsdom.env()`, has been added in jsdom 0.2.0 which should make everyone's lives easier.
@@ -20,61 +29,80 @@ with URL
// Count all of the links from the nodejs build page
var jsdom = require("jsdom");
-
- jsdom.env("http://nodejs.org/dist/",
- [
- 'http://code.jquery.com/jquery-1.5.min.js'
- ],
- function(errors, window) {
- console.log("there have been", window.$("a").length, "nodejs releases!");
- });
+
+ jsdom.env("http://nodejs.org/dist/", [
+ 'http://code.jquery.com/jquery-1.5.min.js'
+ ],
+ function(errors, window) {
+ console.log("there have been", window.$("a").length, "nodejs releases!");
+ });
or with raw html
// Run some jQuery on a html fragment
var jsdom = require('jsdom');
-
- jsdom.env('<p><a class="the-link" href="http://jsdom.org>JSDOM\'s Homepage</a></p>',
- [
- 'http://code.jquery.com/jquery-1.5.min.js'
- ],
- function(errors, window) {
- console.log("contents of a.the-link:", window.$("a.the-link").text());
- });
+
+ jsdom.env('<p><a class="the-link" href="http://jsdom.org>JSDOM\'s Homepage</a></p>', [
+ 'http://code.jquery.com/jquery-1.5.min.js'
+ ],
+ function(errors, window) {
+ console.log("contents of a.the-link:", window.$("a.the-link").text());
+ });
+
or with a configuration object
// Print all of the news items on hackernews
var jsdom = require('jsdom');
-
+
jsdom.env({
- html: 'http://news.ycombinator.com/',
- scripts: [
- 'http://code.jquery.com/jquery-1.5.min.js'
- ],
- done: function(errors, window) {
- var $ = window.$;
- console.log('HN Links');
- $('td.title:not(:last) a').each(function() {
- console.log(' -', $(this).text());
- });
- }
- });
+ html: 'http://news.ycombinator.com/',
+ scripts: [
+ 'http://code.jquery.com/jquery-1.5.min.js'
+ ],
+ done: function(errors, window) {
+ var $ = window.$;
+ console.log('HN Links');
+ $('td.title:not(:last) a').each(function() {
+ console.log(' -', $(this).text());
+ });
+ }
+ });
+or with raw javascript source
+
+ // Print all of the news items on hackernews
+ var jsdom = require('jsdom');
+ var fs = require('fs');
+ var jquery = fs.readFileSync("./jquery-1.6.2.min.js").toString();
+
+ jsdom.env({
+ html: 'http://news.ycombinator.com/',
+ src: [
+ jquery
+ ],
+ done: function(errors, window) {
+ var $ = window.$;
+ console.log('HN Links');
+ $('td.title:not(:last) a').each(function() {
+ console.log(' -', $(this).text());
+ });
+ }
+ });
### How it works
`jsdom.env` is built for ease of use, which is rare in the world of the DOM! Since the web has some absolutely horrible javascript on it, as of jsdom 0.2.0 `jsdom.env` will not process external resources (scripts, images, etc). If you want to process the javascript use one of the methods below (`jsdom.jsdom` or `jsdom.jQueryify`)
- jsdom.env(html, [scripts], [options], callback)
+ jsdom.env(html, [scripts], [config], callback)
- `html` (**required**)
May be a url, html fragment, or file
- `scripts` (**optional**)
May contain files or urls
-
+
- `callback` (**required**)
- Takes 2 arguments:
+ Takes 2 arguments:
- `errors` : array of errors
- `window` : a brand new window
@@ -85,9 +113,14 @@ If you would like to specify a configuration object
jsdom.env({ /* config */ })
- - config.html : see `html` above
- - config.scripts : see `scripts` above
- - config.done : see `callback` above
+ - config.html : see `html` above
+ - config.scripts : see `scripts` above
+ - config.src : An array of javascript strings that will be evaluated against the resulting document. Similar to `scripts`, but it accepts javascript instead of paths/urls.
+ - config.done : see `callback` above
+ - config.document :
+ - referer : the new document will have this referer
+ - cookie : manually set a cookie value i.e. `'key=value; expires=Wed, Sep 21 2011 12:00:00 GMT; path=/'`
+ - config.features : see `Flexibility` section below. **Note**: the default feature set for jsdom.env does _not_ include fetching remote javascript and executing it. This is something that you will need to **carefully** enable yourself.
## For the hardcore
@@ -96,14 +129,14 @@ If you want to spawn a document/window and specify all sorts of options this is
var jsdom = require("jsdom").jsdom,
doc = jsdom(markup, level, options),
window = doc.createWindow();
-
- - `markup` is a full html/xml document to be parsed
+
+ - `markup` is an html/xml document to be parsed. You can also pass `null` or an undefined value to get a basic document with empty head and body tags. Document fragments are also supported (including `""`), and will behave as sanely as possible (eg. the resulting document will lack the `head`, `body` and `documentElement` properties if the corresponding elements aren't included).
- `level` is `null` (which means level3) by default, but you can pass another level if you'd like.
var jsdom = require('jsdom'),
doc = jsdom.jsdom('<html><body></body></html>', jsdom.dom.level1.core)
-
+
- `options` see the **Flexibility** section below
### Flexibility
@@ -125,9 +158,9 @@ the `DOMImplementation` that every `Document` has, and may be tweaked in two way
will use the defaults specified below (see: Default Features)
2. Previous to creating any documents you can modify the defaults for all future documents
-
+
require('jsdom').defaultDocumentFeatures = {
- FetchExternalResources : ['script'],
+ FetchExternalResources : ['script'],
ProcessExternalResources : false,
MutationEvents : false,
QuerySelector : false
@@ -149,7 +182,7 @@ Enables/Disables fetching files over the filesystem/http
`ProcessExternalResources`
_default_: ['script']
_allowed_: ['script'] or false
-
+
Disabling this will disable script execution (currently only javascript).
`MutationEvents`
@@ -210,28 +243,36 @@ This feature is backed by [sizzle][] but currently causes problems with some lib
console.log(window.$('.testing').text());
});
-# W3C Test Compliance:
-
- - DOM Level 1 html/svg/xml (100%)
- - DOM Level 2 html/events/core (100%)
- - DOM Level 3 core (14%)
-
-see: [testlog][] for w3/jsdom test compliance
-
-# More
-
-see: [mailing list][]
-
-
-
-see: [plan][] for roadmap and thoughts about this project
-
-see: [project site][] for additional information
-
- [mailing list]: http://groups.google.com/group/jsdom
- [project site]: http://www.jsdom.org
- [mjsunit.runner]: http://github.com/tmpvar/mjsunit.runner
- [testlog]: http://github.com/tmpvar/jsdom/blob/master/test/testlog.txt
- [plan]: http://github.com/tmpvar/jsdom/blob/master/PLAN.json
-
-[mjsunit.runner]: http://github.com/tmpvar/mjsunit.runner
+# Test Compliance:
+
+ level1/core 531/531 100%
+ level1/html 238/238 100%
+ level1/svg 527/527 100%
+ level2/core 283/283 100%
+ level2/html 687/687 100%
+ level2/style 4/4 100%
+ level2/extra 4/4 100%
+ level3/xpath 93/93 100%
+ window/index 5/5 100%
+ window/script 8/8 100%
+ window/frame 14/14 100%
+ sizzle/index 12/15 80%
+ jsdom/index 63/63 100%
+ --------------------------------------
+ TOTALS: 3/2472 failed; 99% success
+ TIME: 16730ms
+
+## Running the tests
+
+First you'll want to `npm install -g nodeunit` then `npm install --dev`
+
+Using `test/runner` you can slice and dice which tests your want to run from different levels. Usage is as follows:
+
+ test/runner --help
+ Run the jsdom test suite
+
+ Options:
+ -s, --suites suites that you want to run. ie: -s level1/core,1/html,html [string]
+ -f, --fail-fast stop on the first failed test
+ -h, --help show the help
+ -t, --tests choose the test cases to run. ie: -t jquery
View
123 changelog
@@ -0,0 +1,123 @@
+0.2.9
+ * Fix: ensure features are properly reset after a jsdom.env invocation. Closes #239
+ * Fix: ReferenceError in the scanForImportRules helper function
+ * Fix: bug in appendHtmlToElement with HTML5 parser (Brian McDaniel)
+ * Add: jsonp support (lheiskan)
+ * Fix: for setting script element's text property (Brian McDaniel)
+ * Fix: for jsdom.env src bug
+ * Add: test for jsdom.env src bug (multiple done calls)
+ * Fix: NodeList properties should enumerate like arrays (Felix Gnass)
+ * Fix: when downloading a file, include the url.search in file path
+ * Add: test for making a jsonp request with jquery from jsdom window
+ * Add: test case for issue #338
+ * Fix: double load behavior when mixing jsdom.env's `scripts` and `src` properties (cjroebuck)
+
+0.2.8 (hotfix)
+ * Fix: inline event handlers are ignored by everything except for the javascript context
+
+0.2.7 (hotfix)
+ * Fix stylesheet loading
+
+0.2.6
+ * Add: support for window.location.search and document.cookie (Derek Lindahl)
+ * Add: jsdom.env now has a document configuation option which allows users to change the referer of the document (Derek Lindahl)
+ * Fix: allow users to use different jsdom levels in the same process (sinegar)
+ * Fix: removeAttributeNS no longer has a return value (Jason Davies)
+ * Add: support for encoding/decoding all html entities from html4/5 (papandreou)
+ * Add: jsdom.env() accepts the same features object seen in jsdom.jsdom and friends
+
+0.2.5
+ * Fix: serialize special characters in Element.innerHTML/Element.attributes like a grade A browser (Jason Priestley)
+ * Fix: ensure Element.getElementById only returns elements that are attached to the document
+ * Fix: ensure an Element's id is updated when changing the nodeValue of the 'id' attribute (Felix Gnass)
+ * Add: stacktrace to error reporter (Josh Marshall)
+ * Fix: events now bubble up to the window (Jason Davies)
+ * Add: initial window.location.hash support (Josh Marshall)
+ * Add: Node#insertBefore should do nothing when both params are the same node (Jason Davies)
+ * Add: fixes for DOMAttrModified mutation events (Felix Gnass)
+
+0.2.4
+ * Fix: adding script to invalid/incomplete dom (document.documentElement) now catches the error and passes it in the `.env` callback (Gregory Tomlinson)
+ * Cleanup: trigger and html tests
+ * Add: support for inline event handlers (ie: <div onclick='some.horrible.string()'>) (Brian McDaniel)
+ * Fix: script loading over https (Brian McDaniel) #280
+ * Add: using style.setProperty updates the style attribute (Jimmy Mabey).
+ * Add: invalid markup is reported as an error and attached to the associated element and document
+ * Fix: crash when setChild() failes to create new DOM element (John Hurliman)
+ * Added test for issue #287.
+ * Added support for inline event handlers.
+ * Moved frame tests to test/window/frame.js and cleaned up formatting.
+ * Moved script execution tests to test/window/script.js.
+ * Fix a crash when setChild() fails to create a new DOM element
+ * Override CSSOM to update style attribute
+
+0.2.3
+ * Fix: segfault due to window being garbage collected prematurely
+ NOTE: you must manually close the window to free memory (window.close())
+
+0.2.2
+ * Switch to Contextify to manage the window's script execution.
+ * Fix: allow nodelists to have a length of 0 and toArray to return an empty array
+ * Fix: style serialization; issues #230 and #259
+ * Fix: Incomplete DOCTYPE causes JavaScript error
+ * Fix: indentation, removed outdated debug code and trailing whitespace.
+ * Prevent JavaScript error when parsing incomplete <!DOCTYPE>. Closes #259.
+ * Adding a test from brianmcd that ensures that setTimeout callbacks execute in the context of the window
+ * Fixes issue 250: make document.parentWindow===window work
+ * Added test to ensure that timer callbacks execute in the window context.
+ * Fixes 2 issues in ResourceQueue
+ * Make frame/iframe load/process scripts if the parent has the features enabled
+
+0.2.1
+ * Javascript execution fixes [#248, #163, #179]
+ * XPath (Yonathan and Daniel Cassidy)
+ * Start of cssom integration (Yonathan)
+ * Conversion of tests to nodeunit! (Martin Davis)
+ * Added sizzle tests, only failing 3/15
+ * Set the title node's textContent rather than its innerHTML [#242]. (Andreas Lind Petersen)
+ * The textContent getter now walks the DOM and extract the text properly. (Andreas Lind Petersen)
+ * Empty scripts won't cause jsdom.env to hang [#172] (Karuna Sagar)
+ * Every document has either a body or a frameset [#82]. (Karuna Sagar)
+ * Added the ability to grab a level by string + feature. ie: jsdom.level(2, 'html') (Aria Stewart)
+ * Cleaned up htmlencoding and fixed character (de)entification [#147, #177] (Andreas Lind Petersen)
+ * htmlencoding.HTMLDecode: Fixed decoding of `&lt;`, `&gt;`, `&amp;`, and `&apos;`. Closes #147 and #177. ()
+ * Require dom level as a string or object. (Aria Stewart)
+ * JS errors ar triggered on the script element, not document. (Yonathan)
+ * Added configuration property 'headers' for HTTP request headers. (antonj)
+ * Attr.specified is readonly - Karuna Sagar
+ * Removed return value from setAttributeNS() [#207] (Karuna Sagar)
+ * Pass the correct script filename to runInContext. (robin)
+ * Add http referrer support for the download() function. (Robin)
+ * First attempt at fixing the horrible memory leak via window.stopTimers() (d-ash)
+ * Use vm instead of evals binding (d-ash)
+ * Add a way to set the encoding of the jsdom.env html request.
+ * Fixed various typos/lint problems (d-ash)
+ * The first parameter download is now the object returned by URL.parse(). (Robin)
+ * Fixed serialization of elements with a style attribute.
+ * Added src config option to jsdom.env() (Jerry Sievert)
+ * Removed dead code from getNamedItemNS() (Karuna Sagar)
+ * Changes to language/javascript so jsdom would work on v0.5.0-pre (Gord Tanner)
+ * Correct spelling of "Hierarchy request error" (Daniel Cassidy)
+ * Node and Exception type constants are available in all levels. (Daniel Cassidy)
+ * Use \n instead of \r\n during serialization
+ * Fixed auto-insertion of body/html tags (Adrian Makowski)
+ * Adopt unowned nodes when added to the tree. (Aria Stewart)
+ * Fix the selected and defaultSelected fields of `option` element. - Yonathan
+ * Fix: EventTarget.getListeners() now returns a shallow copy so that listeners can be safely removed while an event is being dispatched. (Felix Gnass)
+ * Added removeEventListener() to DOMWindow (Felix Gnass)
+ * Added the ability to pre-load scripts for jsdom.env() (Jerry Sievert)
+ * Mutation event tests/fixes (Felix Gnass)
+ * Changed HTML serialization code to (optionally) pretty print while traversing the tree instead of doing a regexp-based postprocessing. (Andreas Lind Petersen)
+ * Relative and absolute urls now work as expected
+ * setNamedItem no longer sets Node.parentNode [#153] (Karuna Sagar)
+ * Added missing semicolon after entity name - Felix Gnass
+ * Added NodeList#indexOf implementation/tests (Karuna Sagar)
+ * resourceLoader.download now works correctly with https and redirects (waslogic)
+ * Scheme-less URLs default to the current protocol [#87] (Alexander Flatter)
+ * Simplification the prevSibling(), appendChild(), insertBefore() and replaceChild() code (Karuna Sagar)
+ * Javascript errors use core.Node.trigger (Alexander Flatter)
+ * Add core.Document.trigger in level1/core and level2/events; Make DOMWindow.console use it (Alexander Flatter)
+ * Resource resolver fixes (Alexander Flatter)
+ * Fix serialization of doctypes with new lines [#148] (Karuna Sagar)
+ * Child nodes are calculated immediately instead of after .length is called [#169, #171, #176] (Karuna Sagar)
+
View
21 example/browser/browser.js
@@ -1,6 +1,3 @@
-
-var sys = require('sys');
-
var dom = require('../../lib/jsdom/level2/html').dom.level2.html;
var browser = require('../../lib/jsdom/browser/index').windowAugmentation(dom);
@@ -24,14 +21,14 @@ el2.id = 'foo2bar';
el2.innerHTML = '<em class="odd">This is a test</em> This <strong>is another</strong> test ';
browser.document.body.appendChild(el2);
-sys.puts('getElementByid(foo2bar): ' + browser.document.getElementById('foo2bar'));
-sys.puts('getElementByid(foo): ' + browser.document.getElementById('foo'));
-sys.puts('getElementByTagName(em): ' + browser.document.getElementsByTagName('em'));
-sys.puts('getElementByClassName(odd): ' + browser.document.getElementsByClassName('odd'));
+console.log('getElementByid(foo2bar): ' + browser.document.getElementById('foo2bar'));
+console.log('getElementByid(foo): ' + browser.document.getElementById('foo'));
+console.log('getElementByTagName(em): ' + browser.document.getElementsByTagName('em'));
+console.log('getElementByClassName(odd): ' + browser.document.getElementsByClassName('odd'));
-sys.puts('');
-sys.puts('document.body.outerHTML: ');
-sys.puts(document.body.outerHTML);
+console.log('');
+console.log('document.body.outerHTML: ');
+console.log(document.body.outerHTML);
-sys.puts('document.outerHTML: ');
-sys.puts(document.outerHTML);
+console.log('document.outerHTML: ');
+console.log(document.outerHTML);
View
21 example/node-xml/run.js
@@ -1,5 +1,4 @@
-var sys = require('sys'),
- dom = require("../../lib/jsdom/level1/core").dom.level1.core;
+var dom = require("../../lib/jsdom/level1/core").dom.level1.core;
// git clone git://github.com/robrighter/node-xml.git into ~/.node_libraries
var xml = require("node-xml/lib/node-xml");
@@ -9,36 +8,36 @@ var currentElement = doc;
var totalElements = 0;
var parser = new xml.SaxParser(function(cb) {
cb.onStartDocument(function() {
-
+
});
cb.onEndDocument(function() {
- sys.puts((doc.getElementsByTagName("*").length === totalElements) ? "success" : "fail");
+ console.log((doc.getElementsByTagName("*").length === totalElements) ? "success" : "fail");
});
cb.onStartElementNS(function(elem, attrs, prefix, uri, namespaces) {
totalElements++;
var element = doc.createElement(elem);
currentElement.appendChild(element);
currentElement = element;
- sys.puts("=> Started: " + elem + " uri="+uri +" (Attributes: " + JSON.stringify(attrs) + " )");
+ console.log("=> Started: " + elem + " uri="+uri +" (Attributes: " + JSON.stringify(attrs) + " )");
});
cb.onEndElementNS(function(elem, prefix, uri) {
currentElement = currentElement.parentNode;
- sys.puts("<= End: " + elem + " uri="+uri + "\n");
+ console.log("<= End: " + elem + " uri="+uri + "\n");
});
cb.onCharacters(function(chars) {
-
+
});
cb.onCdata(function(cdata) {
- sys.puts('<CDATA>'+cdata+"</CDATA>");
+ console.log('<CDATA>'+cdata+"</CDATA>");
});
cb.onComment(function(msg) {
- sys.puts('<COMMENT>'+msg+"</COMMENT>");
+ console.log('<COMMENT>'+msg+"</COMMENT>");
});
cb.onWarning(function(msg) {
- sys.puts('<WARNING>'+msg+"</WARNING>");
+ console.log('<WARNING>'+msg+"</WARNING>");
});
cb.onError(function(msg) {
- sys.puts('<ERROR>'+JSON.stringify(msg)+"</ERROR>");
+ console.log('<ERROR>'+JSON.stringify(msg)+"</ERROR>");
});
});
View
93 example/pure/pure.js
@@ -11,7 +11,7 @@
*/
exports.pureInit = function(window, document) {
var $p, pure = $p = function(){
- var sel = arguments[0],
+ var sel = arguments[0],
ctxt = false;
if(typeof sel === 'string'){
@@ -39,7 +39,7 @@ exports.pureInit = function(window, document) {
default:
templates = [sel];
}
-
+
for(var i = 0, ii = templates.length; i < ii; i++){
plugins[i] = templates[i];
}
@@ -56,7 +56,7 @@ exports.pureInit = function(window, document) {
IMG:'src',
INPUT:'value'
};
-
+
return plugins;
@@ -74,7 +74,7 @@ exports.pureInit = function(window, document) {
}
throw('pure error: ' + e);
}
-
+
//return a new instance of plugins
function getPlugins(){
var plugins = $p.plugins,
@@ -86,14 +86,14 @@ exports.pureInit = function(window, document) {
f.prototype.render = plugins.render || render;
f.prototype.autoRender = plugins.autoRender || autoRender;
f.prototype.find = plugins.find || find;
-
+
// give the compiler and the error handling to the plugin context
f.prototype._compiler = compiler;
f.prototype._error = error;
-
+
return new f();
}
-
+
// returns the outer HTML of a node
function outerHTML(node){
// if IE take the internal method otherwise build one
@@ -111,7 +111,7 @@ exports.pureInit = function(window, document) {
function isArray(o){
return Object.prototype.toString.call( o ) === "[object Array]";
}
-
+
// returns the string generator function
function wrapquote(qfn, f){
return function(ctxt){
@@ -135,7 +135,7 @@ exports.pureInit = function(window, document) {
}
return leaf;
};
-
+
// default find using querySelector when available on the browser
function find(n, sel){
if(typeof n === 'string'){
@@ -148,7 +148,7 @@ exports.pureInit = function(window, document) {
error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine');
}
}
-
+
// create a function that concatenates constant string
// sections (given in parts) and the results of called
// functions to fill in the gaps between parts (fns).
@@ -164,7 +164,7 @@ exports.pureInit = function(window, document) {
for(var i = 1; i < n; i++){
fnVal = fns[i]( ctxt );
pVal = parts[i];
-
+
// if the value is empty and attribute, remove it
if(fnVal === ''){
attLine = strs[ strs.length - 1 ];
@@ -173,7 +173,7 @@ exports.pureInit = function(window, document) {
pVal = pVal.substr( 1 );
}
}
-
+
strs[ strs.length ] = fnVal;
strs[ strs.length ] = pVal;
}
@@ -190,7 +190,7 @@ exports.pureInit = function(window, document) {
if(m[1] === 'item'){
error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.');
}
- if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE)
+ if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE)
m[2] = function(ctxt){return ctxt.context;};
}
return {name: m[1], sel: m[2]};
@@ -257,12 +257,12 @@ exports.pureInit = function(window, document) {
if( !m ){
error( 'bad selector syntax: ' + sel );
}
-
+
prepend = m[1];
selector = m[2];
attr = m[3];
append = m[4];
-
+
if(selector === '.' || ( !selector && attr ) ){
target[0] = dom;
}else{
@@ -278,7 +278,7 @@ exports.pureInit = function(window, document) {
append = sel.append;
target = [dom];
}
-
+
if( prepend || append ){
if( prepend && append ){
error('append/prepend cannot take place at the same time');
@@ -328,7 +328,7 @@ exports.pureInit = function(window, document) {
}
};
}else{
- getstr = function(node){
+ getstr = function(node){
return node.innerHTML;
};
setstr = function(node, s, ap){
@@ -340,21 +340,21 @@ exports.pureInit = function(window, document) {
}
};
}
- quotefn = function(s){
+ quotefn = function(s){
return s;
};
}
var setfn;
if(prepend){
- setfn = function(node, s){
+ setfn = function(node, s){
setstr( node, s + getstr( node ) , true);
};
}else if(append){
- setfn = function(node, s){
+ setfn = function(node, s){
setstr( node, getstr( node ) + s , true);
};
}else{
- setfn = function(node, s){
+ setfn = function(node, s){
setstr( node, s );
};
}
@@ -388,16 +388,16 @@ exports.pureInit = function(window, document) {
a.sort(sorter);
}
//loop on array
- for(var i = 0, ii = a.length || 0; i < ii; i++){
- buildArg(i, temp);
+ for(var i = 0, ii = a.length || 0; i < ii; i++){
+ buildArg(i, temp);
}
}else{
if(typeof sorter !== 'undefined'){
error('sort is only available on arrays, not objects');
}
//loop on collections
for(var prop in a){
- a.hasOwnProperty( prop ) && buildArg(prop, temp);
+ a.hasOwnProperty( prop ) && buildArg(prop, temp);
}
}
@@ -435,7 +435,7 @@ exports.pureInit = function(window, document) {
itersel = dataselectfn(spec.sel),
target = gettarget(dom, sel, true),
nodes = target.nodes;
-
+
for(i = 0; i < nodes.length; i++){
var node = nodes[i],
inner = compiler(node, dsel);
@@ -444,7 +444,7 @@ exports.pureInit = function(window, document) {
setsig(target, fns.length - 1);
}
}
-
+
function getAutoNodes(n, data){
var ns = n.getElementsByTagName('*'),
an = [],
@@ -458,7 +458,7 @@ exports.pureInit = function(window, document) {
if(ni.nodeType === 1 && ni.className !== ''){
//when a className is found
cs = ni.className.split(' ');
- // for each className
+ // for each className
for(j = 0, jj=cs.length;j<jj;j++){
cj = cs[j];
// check if it is related to a context property
@@ -470,15 +470,15 @@ exports.pureInit = function(window, document) {
ni.className = ni.className.replace('@'+cspec.attr, '');
if(isNodeValue){
cspec.attr = false;
- }
+ }
}
an.push({n:ni, cspec:cspec});
}
}
}
}
return an;
-
+
function checkClass(c, tagName){
// read the class
var ca = c.match(selRx),
@@ -567,18 +567,18 @@ exports.pureInit = function(window, document) {
}
}
}
- // convert node to a string
+ // convert node to a string
var h = outerHTML(dom), pfns = [];
// IE adds an unremovable "selected, value" attribute
// hard replace while waiting for a better solution
h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>");
-
+
// remove attribute prefix
h = h.split(attPfx).join('');
// slice the html string at "Sig"
var parts = h.split( Sig ), p;
- // for each slice add the return string of
+ // for each slice add the return string of
for(var i = 1; i < parts.length; i++){
p = parts[i];
// part is of the form "fn-number:..." as placed there by setsig.
@@ -598,7 +598,7 @@ exports.pureInit = function(window, document) {
}
//compile with the directive as argument
// run the template function on the context argument
- // return an HTML string
+ // return an HTML string
// should replace the template and return this
function render(ctxt, directive){
var fn = typeof directive === 'function' ? directive : plugins.compile( directive, false, this[0] );
@@ -611,7 +611,7 @@ exports.pureInit = function(window, document) {
// compile the template with autoRender
// run the template function on the context argument
- // return an HTML string
+ // return an HTML string
function autoRender(ctxt, directive){
var fn = plugins.compile( directive, ctxt, this[0] );
for(var i = 0, ii = this.length; i < ii; i++){
@@ -620,7 +620,7 @@ exports.pureInit = function(window, document) {
context = null;
return this;
}
-
+
function replaceWith(elm, html){
var tagName = elm.tagName, ne, pa, ep, parent = {TABLE:{}};
if((/TD|TR|TH/).test(tagName)){
@@ -631,8 +631,7 @@ exports.pureInit = function(window, document) {
}else{
pa = document.createElement('SPAN');
}
-
- var sys = require("sys");
+
ep = elm.parentNode;
// avoid IE mem leak
ep.insertBefore(pa, elm);
@@ -643,7 +642,7 @@ exports.pureInit = function(window, document) {
ep.insertBefore(ne, pa);
ep.removeChild(pa);
elm = ne;
-
+
pa = ne = ep = null;
return elm;
}
@@ -665,7 +664,7 @@ exports.pureInit = function(window, document) {
return $(n).cssSelect(sel);
};
}
- DOMAssistant.attach({
+ DOMAssistant.attach({
publicMethods : [ 'compile', 'render', 'autoRender'],
compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); },
render:function(ctxt, directive){ return $( $p(this).render(ctxt, directive) )[0]; },
@@ -704,8 +703,8 @@ exports.pureInit = function(window, document) {
};
}
Element.addMethods({
- compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); },
- render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); },
+ compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); },
+ render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); },
autoRender:function(element, ctxt, directive){ return $p(element).autoRender(ctxt, directive); }
});
},
@@ -717,7 +716,7 @@ exports.pureInit = function(window, document) {
}
},
sly:function(){
- if(typeof document.querySelector === 'undefined'){
+ if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return Sly(sel, n);
};
@@ -727,15 +726,15 @@ exports.pureInit = function(window, document) {
// get lib specifics if available
(function(){
- var libkey =
- typeof dojo !== 'undefined' && 'dojo' ||
+ var libkey =
+ typeof dojo !== 'undefined' && 'dojo' ||
typeof DOMAssistant !== 'undefined' && 'domassistant' ||
- typeof jQuery !== 'undefined' && 'jquery' ||
+ typeof jQuery !== 'undefined' && 'jquery' ||
typeof MooTools !== 'undefined' && 'mootools' ||
- typeof Prototype !== 'undefined' && 'prototype' ||
+ typeof Prototype !== 'undefined' && 'prototype' ||
typeof window.Sizzle !== 'undefined' && 'sizzle' ||
typeof Sly !== 'undefined' && 'sly';
-
+
libkey && $p.libs[libkey]();
})();
return pure;
View
6 example/pure/run.js
@@ -1,7 +1,7 @@
var browser = require("../../lib/jsdom/browser/index");
var dom = new browser.browserAugmentation(require("../../lib/jsdom/level2/html").dom.level2.html);
var sax = require("./sax");
-var sys = require("sys");
+var util = require('util');
// TODO: change this example to use pluggable parser
@@ -83,14 +83,14 @@ doc.implementation = implementation;
doc.innerHTML = '<html><head></head><body><div class="who"></div></body></html>';
var window = {
- alert : function() { sys.puts(sys.inspect(arguments)); },
+ alert : function() { console.log(util.inspect(arguments)); },
document : doc
};
window.Sizzle = require("../sizzle/sizzle").sizzleInit(window, doc);
var $ = require("./pure").pureInit(window, doc);
$("div").autoRender({"who":"Hello Wrrrld"});
-sys.puts(doc.innerHTML);
+console.log(doc.innerHTML);
View
5 example/pure/sax-test.js
@@ -1,10 +1,11 @@
-var sys = require("sys"), sax = require("./sax");
+var util = require("util"),
+ sax = require("./sax");
parser = sax.parser(false);
sax.EVENTS.forEach(function (ev) {
- parser["on" + ev] = function() { sys.puts(sys.inspect(arguments)); };
+ parser["on" + ev] = function() { console.log(util.inspect(arguments)); };
});
parser.write("<span>Welcome,</span> to monkey land").close();
View
48 example/sizzle/run.js
@@ -1,7 +1,7 @@
var browser = require("../../lib/jsdom/browser");
var dom = browser.browserAugmentation(require("../../lib/jsdom/level2/core").dom.level2.core);
-var sys = require("sys");
+var util = require("util");
var doc = new dom.Document("html");
@@ -16,7 +16,7 @@ var sys = require("sys");
doc.createNotationNode("notation1","notation1File", null),
doc.createNotationNode("notation2",null, "notation2File")
);
-
+
// TODO: consider importing the master list of entities
// http://www.w3schools.com/tags/ref_symbols.asp
var entities = new dom.EntityNodeMap(
@@ -34,15 +34,15 @@ var sys = require("sys");
var acronym = doc.createElement("acronym");
acronym.setAttribute("dir", "ltr");
defaultAttributes.setNamedItem(acronym);
-
-
+
+
var doctype = new dom.DocumentType(doc, "html", entities, notations, defaultAttributes);
doc.doctype = doctype;
doc.implementation = implementation;
-
+
doc.appendChild(doc.createComment(" This is comment number 1."));
-
+
var html = doc.createElement("html");
var html = doc.appendChild(html);
var head = doc.createElement("head");
@@ -52,7 +52,7 @@ var sys = require("sys");
meta.setAttribute("http-equiv", "Content-Type");
meta.setAttribute("content", "text/html; charset=UTF-8");
head.appendChild(meta);
-
+
var title = doc.createElement("title")
title.appendChild(doc.createTextNode("hc_staff"));
var title = head.appendChild(title);
@@ -72,7 +72,7 @@ var sys = require("sys");
var genders = [];
var ids = [];
var salaries = [];
-
+
// create 5 employees
for (var i=0; i<5; i++)
{
@@ -83,31 +83,31 @@ var sys = require("sys");
var gender = doc.createElement("var");
var id = doc.createElement("em");
var salary = doc.createElement("sup");
-
- employee.appendChild(doc.createTextNode("\n"));
+
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(id);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(name);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(position);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(salary);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(gender);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
employee.appendChild(address);
- employee.appendChild(doc.createTextNode("\n"));
+ employee.appendChild(doc.createTextNode("\n"));
staff.appendChild(employee);
names.push(name);
employees.push(employee);
- addresses.push(address);
+ addresses.push(address);
genders.push(gender);
positions.push(position);
ids.push(id);
salaries.push(salary);
}
-
+
ids[0].appendChild(doc.createTextNode("EMP0001"));
salaries[0].appendChild(doc.createTextNode("56,000"));
addresses[0].setAttribute("title", "Yes");
@@ -123,7 +123,7 @@ var sys = require("sys");
addresses[1].appendChild(doc.createTextNode("β Dallas, γ\n 98554"));
names[1].appendChild(doc.createTextNode("Martha Raynolds"));
//names[1].appendChild(doc.createCDATASection("This is a CDATASection with EntityReference number 2 &amp;ent2;"));
- //names[1].appendChild(doc.createCDATASection("This is an adjacent CDATASection with a reference to a tab &amp;tab;"));
+ //names[1].appendChild(doc.createCDATASection("This is an adjacent CDATASection with a reference to a tab &amp;tab;"));
genders[1].appendChild(doc.createTextNode("Female"));
positions[1].appendChild(doc.createTextNode("Secretary"));
@@ -145,9 +145,9 @@ var sys = require("sys");
names[3].appendChild(doc.createTextNode("Jeny Oconnor"));
genders[3].appendChild(doc.createTextNode("Female"));
positions[3].appendChild(doc.createTextNode("Personal Director"));
-
+
ids[4].appendChild(doc.createTextNode("EMP0005"));
- salaries[4].appendChild(doc.createTextNode("90,000"));
+ salaries[4].appendChild(doc.createTextNode("90,000"));
addresses[4].setAttribute("title", "No");
addresses[4].id = "theid";
addresses[4].appendChild(doc.createTextNode("1821 Nordic. Road, Irving Texas 98558"));
@@ -156,11 +156,11 @@ var sys = require("sys");
positions[4].appendChild(doc.createTextNode("Computer Specialist"));
//doc.appendChild(doc.createProcessingInstruction("TEST-STYLE", "PIDATA"));
-
+
doc.normalize();
-
+
var sizzleSandbox = {};
var sizzle = require("./sizzle").sizzleInit(sizzleSandbox, doc);
-sys.puts(sys.inspect(sizzle('.classy,p acronym#theid').length));
+console.log(util.inspect(sizzle('.classy,p acronym#theid').length));
View
123 lib/jsdom.js
@@ -1,5 +1,5 @@
-
var dom = exports.dom = require("./jsdom/level3/index").dom,
+ features = require('./jsdom/browser/documentfeatures'),
fs = require("fs"),
pkg = JSON.parse(fs.readFileSync(__dirname + "/../package.json")),
request = require('request'),
@@ -10,6 +10,18 @@ exports.defaultLevel = dom.level3.html;
exports.browserAugmentation = require("./jsdom/browser/index").browserAugmentation;
exports.windowAugmentation = require("./jsdom/browser/index").windowAugmentation;
+// Proxy feature functions to features module.
+['availableDocumentFeatures',
+ 'defaultDocumentFeatures',
+ 'applyDocumentFeatures'].forEach(function (propName) {
+ exports.__defineGetter__(propName, function () {
+ return features[propName];
+ });
+ exports.__defineSetter__(propName, function (val) {
+ return features[propName] = val;
+ });
+});
+
exports.debugMode = false;
var createWindow = exports.createWindow = require("./jsdom/browser/index").createWindow;
@@ -47,12 +59,12 @@ exports.jsdom = function (html, level, options) {
new browser.HTMLDocument(options) :
new browser.Document(options);
- exports.applyDocumentFeatures(doc, options.features);
-
- if (!!html) {
- doc.write(html + '');
- } else {
+ features.applyDocumentFeatures(doc, options.features);
+
+ if (typeof html === 'undefined' || html === null) {
doc.write('<html><head></head><body></body></html>');
+ } else {
+ doc.write(html + '');
}
if (doc.close && !options.deferClose) {
@@ -91,55 +103,6 @@ exports.html = function(html, level, options) {
return exports.jsdom(html, level, options);
};
-exports.availableDocumentFeatures = [
- 'FetchExternalResources',
- 'ProcessExternalResources',
- 'MutationEvents',
- 'QuerySelector'
-];
-
-exports.defaultDocumentFeatures = {
- "FetchExternalResources" : ['script'/*, 'img', 'css', 'frame', 'link'*/],
- "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
- "MutationEvents" : '2.0',
- "QuerySelector" : false
-};
-
-exports.applyDocumentFeatures = function(doc, features) {
- var i, maxFeatures = exports.availableDocumentFeatures.length,
- defaultFeatures = exports.defaultDocumentFeatures,
- j,
- k,
- featureName,
- featureSource;
-
- features = features || {};
-
- for (i=0; i<maxFeatures; i++) {
- featureName = exports.availableDocumentFeatures[i];
- if (typeof features[featureName] !== 'undefined') {
- featureSource = features[featureName];
- } else if (defaultFeatures[featureName]) {
- featureSource = defaultFeatures[featureName];
- } else {
- continue;
- }
-
- doc.implementation.removeFeature(featureName);
-
- if (typeof featureSource !== 'undefined') {
- if (featureSource instanceof Array) {
- k = featureSource.length;
- for (j=0; j<k; j++) {
- doc.implementation.addFeature(featureName, featureSource[j]);
- }
- } else {
- doc.implementation.addFeature(featureName, featureSource);
- }
- }
- }
-};
-
exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {
if (!window || !window.document) { return; }
@@ -195,23 +158,28 @@ exports.env = exports.jsdom.env = function() {
config.src = [config.src];
}
- var
+ var
options = {
- features: {
+ features: config.features || {
'FetchExternalResources' : false,
'ProcessExternalResources' : false
},
url: config.url
},
window = exports.html(html, null, options).createWindow(),
- features = window.document.implementation._features,
+ features = JSON.parse(JSON.stringify(window.document.implementation._features)),
docsLoaded = 0,
- totalDocs = config.scripts.length,
+ totalDocs = config.scripts.length + config.src.length,
readyState = null,
errors = null;
if (!window || !window.document) {
- return callback(new Error('JSDOM: a window object could not be created.'));
+ return callback(new Error('JSDOM: a window object could not be created.'));
+ }
+
+ if( config.document ) {
+ window.document._referrer = config.document.referrer;
+ window.document._cookie = config.document.cookie;
}
window.document.implementation.addFeature('FetchExternalResources', ['script']);
@@ -222,6 +190,11 @@ exports.env = exports.jsdom.env = function() {
docsLoaded++;
if (docsLoaded >= totalDocs) {
window.document.implementation._features = features;
+
+ if (errors) {
+ errors = errors.concat(window.document.errors || []);
+ }
+
callback(errors, window);
}
}
@@ -242,7 +215,17 @@ exports.env = exports.jsdom.env = function() {
};
script.src = src;
- window.document.documentElement.appendChild(script);
+ try {
+ // project against invalid dom
+ // ex: http://www.google.com/foo#bar
+ window.document.documentElement.appendChild(script);
+ } catch(e) {
+ if(!errors) {
+ errors=[];
+ }
+ errors.push(e.error || e.message);
+ scriptComplete();
+ }
});
config.src.forEach(function(src) {
@@ -266,7 +249,7 @@ exports.env = exports.jsdom.env = function() {
window.document.documentElement.removeChild(script);
});
} else {
- callback(errors, window);
+ scriptComplete();
}
};
@@ -281,12 +264,13 @@ exports.env = exports.jsdom.env = function() {
var url = URL.parse(config.html);
config.url = config.url || url.href;
if (url.hostname) {
- request({ uri: url,
- encoding: config.encoding || 'utf8',
- headers: config.headers || {}
- },
- function(err, request, body) {
- processHTML(err, body);
+ request({
+ uri : url,
+ encoding : config.encoding || 'utf8',
+ headers : config.headers || {}
+ },
+ function(err, request, body) {
+ processHTML(err, body);
});
} else {
fs.readFile(url.pathname, processHTML);
@@ -322,7 +306,8 @@ exports.env.processArguments = function(args) {
'done' : true,
'scripts' : false,
'config' : false,
- 'url' : false // the URL for location.href if different from html
+ 'url' : false, // the URL for location.href if different from html
+ 'document': false // HTMLDocument properties
},
propKeys = Object.keys(props),
config = {
View
52 lib/jsdom/browser/documentfeatures.js
@@ -0,0 +1,52 @@
+exports.availableDocumentFeatures = [
+ 'FetchExternalResources',
+ 'ProcessExternalResources',
+ 'MutationEvents',
+ 'QuerySelector'
+];
+
+exports.defaultDocumentFeatures = {
+ "FetchExternalResources" : ['script'/*, 'img', 'css', 'frame', 'link'*/],
+ "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
+ "MutationEvents" : '2.0',
+ "QuerySelector" : false
+};
+
+exports.applyDocumentFeatures = function(doc, features) {
+ var i, maxFeatures = exports.availableDocumentFeatures.length,
+ defaultFeatures = exports.defaultDocumentFeatures,
+ j,
+ k,
+ featureName,
+ featureSource;
+
+ features = features || {};
+
+ for (i=0; i<maxFeatures; i++) {
+ featureName = exports.availableDocumentFeatures[i];
+ if (typeof features[featureName] !== 'undefined') {
+ featureSource = features[featureName];
+ // We have to check the lowercase version also because the Document feature
+ // methods convert everything to lowercase.
+ } else if (typeof features[featureName.toLowerCase()] !== 'undefined') {
+ featureSource = features[featureName.toLowerCase()];
+ } else if (defaultFeatures[featureName]) {
+ featureSource = defaultFeatures[featureName];
+ } else {
+ continue;
+ }
+
+ doc.implementation.removeFeature(featureName);
+
+ if (typeof featureSource !== 'undefined') {
+ if (featureSource instanceof Array) {
+ k = featureSource.length;
+ for (j=0; j<k; j++) {
+ doc.implementation.addFeature(featureName, featureSource[j]);
+ }
+ } else {
+ doc.implementation.addFeature(featureName, featureSource);
+ }
+ }
+ }
+};
View
140 lib/jsdom/browser/domtohtml.js
@@ -4,111 +4,73 @@ var isXHTML = false;
//List from node-htmlparser
var singleTags = {
- area: 1,
- base: 1,
- basefont: 1,
- br: 1,
- col: 1,
- frame: 1,
- hr: 1,
- img: 1,
- input: 1,
- isindex: 1,
- link: 1,
- meta: 1,
- param: 1,
- embed: 1
+ area: 1,
+ base: 1,
+ basefont: 1,
+ br: 1,
+ col: 1,
+ frame: 1,
+ hr: 1,
+ img: 1,
+ input: 1,
+ isindex: 1,
+ link: 1,
+ meta: 1,
+ param: 1,
+ embed: 1
};
var expr = {
upperCaseChars: /([A-Z])/g,
breakBetweenTags: /(<(\/?\w+).*?>)(?=<(?!\/\2))/gi,
singleTag: (function() {
- var tags = [];
- for (var i in singleTags) {
- tags.push(i);
- }
- return new RegExp('<' + tags.join('|<'), 'i');
+ var tags = [];
+ for (var i in singleTags) {
+ tags.push(i);
+ }
+ return new RegExp('<' + tags.join('|<'), 'i');
})()
};
var uncanon = function(str, letter) {
- return '-' + letter.toLowerCase();
+ return '-' + letter.toLowerCase();
};
var HTMLEncode = require('./htmlencoding').HTMLEncode;
-var styleIgnore = {
- top: 1,
- left: 1,
- length : 1,
- _importants : 1
-};
-
exports.stringifyElement = function stringifyElement(element) {
- var ret = { start : '', end : '' }, attributes = [], i,
- attribute = null;
- if (element.tagName) {
- var tagName = element.tagName.toLowerCase(),
+ var tagName = element.tagName.toLowerCase(),
ret = {
- start: "<" + tagName,
- end:''
- }
+ start: "<" + tagName,
+ end:''
+ },
+ attributes = [],
+ i,
+ attribute = null;
+
+ if (element.attributes.length) {
+ ret.start += " ";
+ for (i = 0; i<element.attributes.length; i++) {
+ attribute = element.attributes.item(i);
+ attributes.push(attribute.name + '="' +
+ HTMLEncode(attribute.nodeValue, true) + '"');
}
+ }
+ ret.start += attributes.join(" ");
- //sys.puts('Checking Attributes: ' + element._attributes.length);
- //sys.puts(sys.inspect(element));
- if (element.attributes && element.attributes.length) {
- ret.start += " ";
- for (i = 0; i<element.attributes.length; i++) {
- attribute = element.attributes.item(i);
- attributes.push(attribute.name + '="' +
- HTMLEncode(attribute.nodeValue) + '"');
- }
- }
- ret.start += attributes.join(" ");
-
- if (element.style) {
- var
- styleAttrs = [],
- keys = Object.keys(element.style),
- i, key,
- l=keys.length;
- for (i=0; i<l; i++) {
- key = keys[i];
- value = element.style[key];
-
- if (!styleIgnore[key] && typeof value !== 'function') {
- var use = true;
- if (i === 'position' && value === 'static') {
- use = false;
- }
- if (element.style[i] === '') {
- use = false;
- }
- if (use) {
- styleAttrs.push(key.replace(expr.upperCaseChars, uncanon) + ': ' +
- HTMLEncode(value));
- }
- }
- }
- if (styleAttrs.length) {
- ret.start += ' style="' + styleAttrs.join('; ') + '"';
- }
+ if (singleTags[tagName]) {
+ if (isXHTML) {
+ ret.start += "/";
}
- if (singleTags[tagName]) {
- if (isXHTML) {
- ret.start += "/";
- }
- ret.start += ">";
- ret.end = '';
- } else {
- ret.start += ">";
- ret.end = "</" + tagName + ">";
- }
+ ret.start += ">";
+ ret.end = '';
+ } else {
+ ret.start += ">";
+ ret.end = "</" + tagName + ">";
+ }
- return ret;
+ return ret;
};
var rawTextElements = /SCRIPT|STYLE/i;
@@ -124,7 +86,7 @@ function stringifyDoctype (doctype) {
dt += ' PUBLIC "' + doctype.publicId + '" ';
}
if (!doctype.publicId && doctype.systemId) {
- dt += ' SYSTEM '
+ dt += ' SYSTEM ';
}
if (doctype.systemId) {
// System ID may contain double quotes OR single quotes, not never both.
@@ -147,8 +109,7 @@ exports.makeHtmlGenerator = function makeHtmlGenerator(indentUnit, eol) {
curIndent = curIndent || "";
if (node) {
if (node.nodeType &&
- node.nodeType === node.ENTITY_REFERENCE_NODE)
- {
+ node.nodeType === node.ENTITY_REFERENCE_NODE) {
node = node._entity;
}
@@ -174,13 +135,13 @@ exports.makeHtmlGenerator = function makeHtmlGenerator(indentUnit, eol) {
}
ret += current.end + eol;
} else {
- ret += ((rawText ? node.nodeValue : HTMLEncode(node.nodeValue)) || '') + current.end + eol;
+ ret += ((rawText ? node.nodeValue : HTMLEncode(node.nodeValue, false)) || '') + current.end + eol;
}
break;
case node.TEXT_NODE:
// Skip pure whitespace nodes if we're indenting
if (!indentUnit || !/^[\s\n]*$/.test(node.nodeValue)) {
- ret += (rawText ? node.nodeValue : HTMLEncode(node.nodeValue)) || '';
+ ret += (rawText ? node.nodeValue : HTMLEncode(node.nodeValue, false)) || '';
}
break;
case node.COMMENT_NODE:
@@ -196,7 +157,6 @@ exports.makeHtmlGenerator = function makeHtmlGenerator(indentUnit, eol) {
break;
}
}
- //require('sys').puts(require('sys').inspect(ret));
return ret;
};
};
View
1,611 lib/jsdom/browser/htmlencoding.js
@@ -1,145 +1,1506 @@
-var entityCharCodes = {
- 'quot': 34,
- 'amp': 38,
- 'apos': 39,
- 'lt': 60,
- 'gt': 62,
- 'nbsp': 160,
- 'iexcl': 161,
- 'cent': 162,
- 'pound': 163,
- 'curren': 164,
- 'yen': 165,
- 'brvbar': 166,
- 'sect': 167,
- 'uml': 168,
- 'copy': 169,
- 'ordf': 170,
- 'laquo': 171,
- 'not': 172,
- 'shy': 173,
- 'reg': 174,
- 'macr': 175,
- 'deg': 176,
- 'plusmn': 177,
- 'sup2': 178,
- 'sup3': 179,
- 'acute': 180,
- 'micro': 181,
- 'para': 182,
- 'middot': 183,
- 'cedil': 184,
- 'sup1': 185,
- 'ordm': 186,
- 'raquo': 187,
- 'frac14': 188,
- 'frac12': 189,
- 'frac34': 190,
- 'iquest': 191,
- 'Agrave': 192,
- 'Aacute': 193,
- 'Acirc': 194,
- 'Atilde': 195,
- 'Auml': 196,
- 'Aring': 197,
- 'AElig': 198,
- 'Ccedil': 199,
- 'Egrave': 200,
- 'Eacute': 201,
- 'Ecirc': 202,
- 'Euml': 203,
- 'Igrave': 204,
- 'Iacute': 205,
- 'Icirc': 206,
- 'Iuml': 207,
- 'ETH': 208,
- 'Ntilde': 209,
- 'Ograve': 210,
- 'Oacute': 211,
- 'Ocirc': 212,
- 'Otilde': 213,
- 'Ouml': 214,
- 'times': 215,
- 'Oslash': 216,
- 'Ugrave': 217,
- 'Uacute': 218,
- 'Ucirc': 219,
- 'Uuml': 220,
- 'Yacute': 221,
- 'THORN': 222,
- 'szlig': 223,
- 'agrave': 224,
- 'aacute': 225,
- 'acirc': 226,
- 'atilde': 227,
- 'auml': 228,
- 'aring': 229,
- 'aelig': 230,
- 'ccedil': 231,
- 'egrave': 232,
- 'eacute': 233,
- 'ecirc': 234,
- 'euml': 235,
- 'igrave': 236,
- 'iacute': 237,
- 'icirc': 238,
- 'iuml': 239,
- 'eth': 240,
- 'ntilde': 241,
- 'ograve': 242,
- 'oacute': 243,
- 'ocirc': 244,
- 'otilde': 245,
- 'ouml': 246,
- 'divide': 247,
- 'oslash': 248,
- 'ugrave': 249,
- 'uacute': 250,
- 'ucirc': 251,
- 'uuml': 252,
- 'yacute': 253,
- 'thorn': 254,
- 'yuml': 255
+var charByEntityName = {
+ 'quot': '"',
+ 'amp': '&',
+ 'apos': '\'',
+ 'lt': '<',
+ 'gt': '>',
+ 'nbsp': ' ',
+ 'iexcl': '¡',
+ 'cent': '¢',
+ 'pound': '£',
+ 'curren': '¤',
+ 'yen': '¥',
+ 'brvbar': '¦',
+ 'sect': '§',
+ 'uml': '¨',
+ 'copy': '©',
+ 'ordf': 'ª',
+ 'laquo': '«',
+ 'not': '¬',
+ 'shy': '­',
+ 'reg': '®',
+ 'macr': '¯', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'strns': '¯', // http://www.w3.org/TR/html5/named-character-references.html
+ 'deg': '°',
+ 'plusmn': '±', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'pm': '±', // http://www.w3.org/TR/html5/named-character-references.html
+ 'sup2': '²',
+ 'sup3': '³',
+ 'acute': '´',
+ 'micro': 'µ',
+ 'para': '',
+ 'middot': '·',
+ 'cedil': '¸',
+ 'sup1': '¹',
+ 'ordm': 'º',
+ 'raquo': '»',
+ 'frac14': '¼',
+ 'half': '½', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'frac12': '½', // http://www.w3.org/TR/html5/named-character-references.html
+ 'frac34': '¾',
+ 'iquest': '¿',
+ 'Agrave': 'À',
+ 'Aacute': 'Á',
+ 'Acirc': 'Â',
+ 'Atilde': 'Ã',
+ 'Auml': 'Ä',
+ 'Aring': 'Å', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'angst': 'Å', // http://www.w3.org/TR/html5/named-character-references.html
+ 'AElig': 'Æ',
+ 'Ccedil': 'Ç',
+ 'Egrave': 'È',
+ 'Eacute': 'É',
+ 'Ecirc': 'Ê',
+ 'Euml': 'Ë',
+ 'Igrave': 'Ì',
+ 'Iacute': 'Í',
+ 'Icirc': 'Î',
+ 'Iuml': 'Ï',
+ 'ETH': 'Ð',
+ 'Ntilde': 'Ñ',
+ 'Ograve': 'Ò',
+ 'Oacute': 'Ó',
+ 'Ocirc': 'Ô',
+ 'Otilde': 'Õ',
+ 'Ouml': 'Ö',
+ 'times': '×',
+ 'Oslash': 'Ø',
+ 'Ugrave': 'Ù',
+ 'Uacute': 'Ú',
+ 'Ucirc': 'Û',
+ 'Uuml': 'Ü',
+ 'Yacute': 'Ý',
+ 'THORN': 'Þ',
+ 'szlig': 'ß',
+ 'agrave': 'à',
+ 'aacute': 'á',
+ 'acirc': 'â',
+ 'atilde': 'ã',
+ 'auml': 'ä',
+ 'aring': 'å',
+ 'aelig': 'æ',
+ 'ccedil': 'ç',
+ 'egrave': 'è',
+ 'eacute': 'é',
+ 'ecirc': 'ê',
+ 'euml': 'ë',
+ 'igrave': 'ì',
+ 'iacute': 'í',
+ 'icirc': 'î',
+ 'iuml': 'ï',
+ 'eth': 'ð',
+ 'ntilde': 'ñ',
+ 'ograve': 'ò',
+ 'oacute': 'ó',
+ 'ocirc': 'ô',
+ 'otilde': 'õ',
+ 'ouml': 'ö',
+ 'divide': '÷',
+ 'oslash': 'ø',
+ 'ugrave': 'ù',
+ 'uacute': 'ú',
+ 'ucirc': 'û',
+ 'uuml': 'ü',
+ 'yacute': 'ý',
+ 'thorn': 'þ',
+ 'yuml': 'ÿ',
+ 'Amacr': 'Ā',
+ 'amacr': 'ā',
+ 'Abreve': 'Ă',
+ 'abreve': 'ă',
+ 'Aogon': 'Ą',
+ 'aogon': 'ą',
+ 'Cacute': 'Ć',
+ 'cacute': 'ć',
+ 'Ccirc': 'Ĉ',
+ 'ccirc': 'ĉ',
+ 'Cdot': 'Ċ',
+ 'cdot': 'ċ',
+ 'Ccaron': 'Č',
+ 'ccaron': 'č',
+ 'Dcaron': 'Ď',
+ 'dcaron': 'ď',
+ 'Dstrok': 'Đ',
+ 'dstrok': 'đ',
+ 'Emacr': 'Ē',
+ 'emacr': 'ē',
+ 'Edot': 'Ė',
+ 'edot': 'ė',
+ 'Eogon': 'Ę',
+ 'eogon': 'ę',
+ 'Ecaron': 'Ě',
+ 'ecaron': 'ě',
+ 'Gcirc': 'Ĝ',
+ 'gcirc': 'ĝ',
+ 'Gbreve': 'Ğ',
+ 'gbreve': 'ğ',
+ 'Gdot': 'Ġ',
+ 'gdot': 'ġ',
+ 'Gcedil': 'Ģ',
+ 'Hcirc': 'Ĥ',
+ 'hcirc': 'ĥ',
+ 'Hstrok': 'Ħ',
+ 'hstrok': 'ħ',
+ 'Itilde': 'Ĩ',
+ 'itilde': 'ĩ',
+ 'Imacr': 'Ī',
+ 'imacr': 'ī',
+ 'Iogon': 'Į',
+ 'iogon': 'į',
+ 'Idot': 'İ',
+ 'inodot': 'ı',
+ 'IJlig': 'IJ',
+ 'ijlig': 'ij',
+ 'Jcirc': 'Ĵ',
+ 'jcirc': 'ĵ',
+ 'Kcedil': 'Ķ',
+ 'kcedil': 'ķ',
+ 'kgreen': 'ĸ',
+ 'Lacute': 'Ĺ',
+ 'lacute': 'ĺ',
+ 'Lcedil': 'Ļ',
+ 'lcedil': 'ļ',
+ 'Lcaron': 'Ľ',
+ 'lcaron': 'ľ',
+ 'Lmidot': 'Ŀ',
+ 'lmidot': 'ŀ',
+ 'Lstrok': 'Ł',
+ 'lstrok': 'ł',
+ 'Nacute': 'Ń',
+ 'nacute': 'ń',
+ 'Ncedil': 'Ņ',
+ 'ncedil': 'ņ',
+ 'Ncaron': 'Ň',
+ 'ncaron': 'ň',
+ 'napos': 'ʼn',
+ 'ENG': 'Ŋ',
+ 'eng': 'ŋ',
+ 'Omacr': 'Ō',
+ 'omacr': 'ō',
+ 'Odblac': 'Ő',
+ 'odblac': 'ő',
+ 'OElig': 'Œ',
+ 'oelig': 'œ',
+ 'Racute': 'Ŕ',
+ 'racute': 'ŕ',
+ 'Rcedil': 'Ŗ',
+ 'rcedil': 'ŗ',
+ 'Rcaron': 'Ř',
+ 'rcaron': 'ř',
+ 'Sacute': 'Ś',
+ 'sacute': 'ś',
+ 'Scirc': 'Ŝ',
+ 'scirc': 'ŝ',
+ 'Scedil': 'Ş',
+ 'scedil': 'ş',
+ 'Scaron': 'Š',
+ 'scaron': 'š',
+ 'Tcedil': 'Ţ',
+ 'tcedil': 'ţ',
+ 'Tcaron': 'Ť',
+ 'tcaron': 'ť',
+ 'Tstrok': 'Ŧ',
+ 'tstrok': 'ŧ',
+ 'Utilde': 'Ũ',
+ 'utilde': 'ũ',
+ 'Umacr': 'Ū',
+ 'umacr': 'ū',
+ 'Ubreve': 'Ŭ',
+ 'ubreve': 'ŭ',
+ 'Uring': 'Ů',
+ 'uring': 'ů',
+ 'Udblac': 'Ű',
+ 'udblac': 'ű',
+ 'Uogon': 'Ų',
+ 'uogon': 'ų',
+ 'Wcirc': 'Ŵ',
+ 'wcirc': 'ŵ',
+ 'Ycirc': 'Ŷ',
+ 'ycirc': 'ŷ',
+ 'Yuml': 'Ÿ',
+ 'Zacute': 'Ź',
+ 'zacute': 'ź',
+ 'Zdot': 'Ż',
+ 'zdot': 'ż',
+ 'Zcaron': 'Ž',
+ 'zcaron': 'ž',
+ 'fnof': 'ƒ',
+ 'imped': 'Ƶ',
+ 'gacute': 'ǵ',
+ 'jmath': 'ȷ',
+ 'circ': 'ˆ',
+ 'caron': 'ˇ',
+ 'breve': '˘',
+ 'dot': '˙',
+ 'ring': '˚',
+ 'ogon': '˛',
+ 'tilde': '˜',
+ 'dblac': '˝',
+ 'Alpha': 'Α',
+ 'Beta': 'Β',
+ 'Gamma': 'Γ',
+ 'Delta': 'Δ',
+ 'Epsilon': 'Ε',
+ 'Zeta': 'Ζ',
+ 'Eta': 'Η',
+ 'Theta': 'Θ',
+ 'Iota': 'Ι',
+ 'Kappa': 'Κ',
+ 'Lambda': 'Λ',
+ 'Mu': 'Μ',
+ 'Nu': 'Ν',
+ 'Xi': 'Ξ',
+ 'Omicron': 'Ο',
+ 'Pi': 'Π',
+ 'Rho': 'Ρ',
+ 'Sigma': 'Σ',
+ 'Tau': 'Τ',
+ 'Upsilon': 'Υ',
+ 'Phi': 'Φ',
+ 'Chi': 'Χ',
+ 'Psi': 'Ψ',
+ 'Omega': 'Ω', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'ohm': 'Ω', // http://www.w3.org/TR/html5/named-character-references.html
+ 'alpha': 'α',
+ 'beta': 'β',
+ 'gamma': 'γ',
+ 'delta': 'δ',
+ 'epsilon': 'ε',
+ 'zeta': 'ζ',
+ 'eta': 'η',
+ 'theta': 'θ',
+ 'iota': 'ι',
+ 'kappa': 'κ',
+ 'lambda': 'λ',
+ 'mu': 'μ',
+ 'nu': 'ν',
+ 'xi': 'ξ',
+ 'omicron': 'ο',
+ 'pi': 'π',
+ 'rho': 'ρ',
+ 'sigmaf': 'ς', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'varsigma': 'ς', // http://www.w3.org/TR/html5/named-character-references.html
+ 'sigma': 'σ',
+ 'tau': 'τ',
+ 'upsilon': 'υ',
+ 'phi': 'φ',
+ 'chi': 'χ',
+ 'psi': 'ψ',
+ 'omega': 'ω',
+ 'thetasym': 'ϑ', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'vartheta': 'ϑ', // http://www.w3.org/TR/html5/named-character-references.html
+ 'upsih': 'ϒ',
+ 'varphi': 'ϕ',
+ 'piv': 'ϖ', // http://www.w3.org/TR/html4/sgml/entities.html
+ 'varpi': 'ϖ', // http://www.w3.org/TR/html5/named-character-references.html
+ 'Gammad': 'Ϝ',
+ 'gammad': 'ϝ',
+ 'varkappa': 'ϰ',
+ 'varrho': 'ϱ',
+ 'varepsilon': 'ϵ',
+ 'bepsi': '϶',
+ 'IOcy': 'Ё',
+ 'DJcy': 'Ђ',
+ 'GJcy': 'Ѓ',
+ 'Jukcy': 'Є',
+ 'DScy': 'Ѕ',
+ 'Iukcy': 'І',
+ 'YIcy': 'Ї',
+ 'Jsercy': 'Ј',
+ 'LJcy': 'Љ',
+ 'NJcy': 'Њ',
+ 'TSHcy': 'Ћ',
+ 'KJcy': 'Ќ',
+ 'Ubrcy': 'Ў',
+ 'DZcy': 'Џ',
+ 'Acy': 'А',
+ 'Bcy': 'Б',
+ 'Vcy': 'В',