Comparing version 0.2.15 to 0.2.16
@@ -51,2 +51,7 @@ var dom = exports.dom = require("./jsdom/level3/index").dom, | ||
module.parent.filename; | ||
options.url = options.url.replace(/\\/g, '/'); | ||
if (options.url[0] !== '/') { | ||
options.url = '/' + options.url; | ||
} | ||
options.url = 'file://' + options.url; | ||
} | ||
@@ -53,0 +58,0 @@ |
@@ -0,0 +0,0 @@ exports.availableDocumentFeatures = [ |
@@ -0,0 +0,0 @@ var charByEntityName = { |
@@ -418,7 +418,7 @@ var http = require('http'), | ||
dom.Document.prototype.__defineGetter__('outerHTML', function() { | ||
return domToHtml(this); | ||
return domToHtml(this, true); | ||
}); | ||
dom.Element.prototype.__defineGetter__('outerHTML', function() { | ||
return domToHtml(this); | ||
return domToHtml(this, true); | ||
}); | ||
@@ -425,0 +425,0 @@ |
@@ -737,3 +737,3 @@ /* | ||
// Level2/core clean off empty nodes | ||
if (child.value === "") { | ||
if (child.nodeValue === "") { | ||
this.removeChild(child); | ||
@@ -752,3 +752,3 @@ i--; | ||
// remove the child and decrement i | ||
prevChild.appendData(child.value); | ||
prevChild.appendData(child.nodeValue); | ||
@@ -847,3 +847,3 @@ this.removeChild(child); | ||
var name = arg.name; | ||
var name = arg.name || arg.tagName; | ||
var ret = this._nodes[name]; | ||
@@ -999,3 +999,2 @@ if (!ret) { | ||
get name() { return this.nodeName;}, | ||
/* returns string */ | ||
@@ -1027,4 +1026,6 @@ getAttribute: function(/* string */ name) { | ||
} else { | ||
// We're an element. | ||
self._ownerDocument.parentWindow.run(value); | ||
// We're an element. Use awesome hacks to get the correct `this` context for the inline event handler. | ||
self._ownerDocument.parentWindow.__tempContextForInlineEventHandler = self; | ||
self._ownerDocument.parentWindow.run("(function () {" + value + "}).call(window.__tempContextForInlineEventHandler);"); | ||
delete self.ownerDocument.parentWindow.__tempContextForInlineEventHandler; | ||
} | ||
@@ -1410,3 +1411,3 @@ }; | ||
this._nodeValue = (value) ? value + "" : ""; | ||
this._nodeValue = value + ""; | ||
}; | ||
@@ -1607,4 +1608,2 @@ core.CharacterData.prototype = { | ||
get attributes() { return null;}, | ||
get value() { return this._nodeValue;}, | ||
set value(value) { this.nodeValue = value;}, | ||
@@ -1611,0 +1610,0 @@ /* returns Text */ |
@@ -75,8 +75,3 @@ /* DOM Level2 Events implemented as described here: | ||
events.HTMLEvent = function(eventType) { | ||
events.Event.call(this, eventType); | ||
}; | ||
events.HTMLEvent.prototype.__proto__ = events.Event.prototype; | ||
events.UIEvent = function(eventType) { | ||
@@ -405,3 +400,3 @@ events.Event.call(this, eventType); | ||
case "MouseEvents": return new events.MouseEvent(eventType); | ||
case "HTMLEvents": return new events.HTMLEvent(eventType); | ||
case "HTMLEvents": return new events.Event(eventType); | ||
} | ||
@@ -408,0 +403,0 @@ return new events.Event(eventType); |
@@ -143,3 +143,3 @@ var core = require("./core").dom.level2.core, | ||
readFile: function(url, callback) { | ||
fs.readFile(url.replace(/^file:\/\//, ""), 'utf8', callback); | ||
fs.readFile(url.replace(/^file:\/\//, "").replace(/^\/([a-z]):\//i, '$1:/'), 'utf8', callback); | ||
} | ||
@@ -179,3 +179,3 @@ }; | ||
} | ||
if (n.normalize) { | ||
if (typeof n === 'object' && n.normalize) { // see GH-491 | ||
return n.normalize(s); | ||
@@ -780,6 +780,6 @@ } | ||
get text() { | ||
return (this.hasAttribute('value')) ? this.getAttribute('value') : this.innerHTML; | ||
return this.innerHTML; | ||
}, | ||
get value() { | ||
return (this.hasAttribute('value')) ? this.getAttribute('value') : this.innerHTML; | ||
return (this.hasAttribute('value')) ? this.getAttribute('value') : this.innerHTML; | ||
}, | ||
@@ -854,7 +854,11 @@ set value(val) { | ||
get checked() { | ||
return !!this.getAttribute('checked'); | ||
return !!this._attributes.getNamedItem('checked'); | ||
}, | ||
set checked(checked) { | ||
this._initDefaultChecked(); | ||
this.setAttribute('checked', checked); | ||
if (checked) { | ||
this.setAttribute('checked', 'checked'); | ||
} else { | ||
this.removeAttribute('checked'); | ||
} | ||
}, | ||
@@ -1303,3 +1307,3 @@ get value() { | ||
for (i; i<l; i++) { | ||
ret.push(children.item(i).value); | ||
ret.push(children.item(i).nodeValue); | ||
} | ||
@@ -1306,0 +1310,0 @@ |
@@ -0,0 +0,0 @@ exports.dom = { |
@@ -0,0 +0,0 @@ exports.javascript = function(element, code, filename) { |
@@ -5,2 +5,3 @@ var core = require("./core").dom.level2.core, | ||
cssom = require("cssom"), | ||
cssstyle = require("cssstyle"), | ||
assert = require('assert'); | ||
@@ -32,3 +33,3 @@ | ||
core.CSSImportRule = cssom.CSSImportRule; | ||
core.CSSStyleDeclaration = cssom.CSSStyleDeclaration; | ||
core.CSSStyleDeclaration = cssstyle.CSSStyleDeclaration; | ||
@@ -125,3 +126,3 @@ // Relavant specs | ||
* @param {string} data | ||
* @param {cssom.CSSStyleDeclaration} style | ||
* @param {cssstyle.CSSStyleDeclaration} style | ||
*/ | ||
@@ -185,3 +186,3 @@ function evaluateStyleAttribute(data) { | ||
if (!style) { | ||
style = this._cssStyleDeclaration = new cssom.CSSStyleDeclaration(); | ||
style = this._cssStyleDeclaration = new cssstyle.CSSStyleDeclaration(); | ||
if (!this.getAttributeNode('style')) { | ||
@@ -188,0 +189,0 @@ this.setAttribute('style', ''); |
@@ -0,0 +0,0 @@ var events = require("../level2/events").dom.level2.events; |
@@ -0,0 +0,0 @@ var core = require("./core").dom.level3.core, |
module.exports.dom = { | ||
level3 : { | ||
core : require("./core").dom.level3.core, | ||
xpath : require("./xpath").xpath, | ||
xpath : require("./xpath"), | ||
events : require("./events").dom.level3.events, | ||
@@ -6,0 +6,0 @@ html : require("./html").dom.level3.html, |
@@ -0,0 +0,0 @@ // w3c Load/Save functionality: http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/ |
/*! | ||
* Sizzle CSS Selector Engine | ||
* Copyright 2011, The Dojo Foundation | ||
* Released under the MIT, BSD, and GPL Licenses. | ||
* More information: http://sizzlejs.com/ | ||
* Copyright 2012 jQuery Foundation and other contributors | ||
* Released under the MIT license | ||
* http://sizzlejs.com/ | ||
*/ | ||
// Patch for jsdom | ||
module.exports = function(document){ | ||
module.exports = function( document ) { | ||
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, | ||
expando = "sizcache" + (Math.random() + '').replace('.', ''), | ||
done = 0, | ||
toString = Object.prototype.toString, | ||
hasDuplicate = false, | ||
var dirruns, | ||
cachedruns, | ||
assertGetIdNotName, | ||
Expr, | ||
getText, | ||
isXML, | ||
contains, | ||
compile, | ||
sortOrder, | ||
hasDuplicate, | ||
baseHasDuplicate = true, | ||
rBackslash = /\\/g, | ||
rReturn = /\r\n/g, | ||
rNonWord = /\W/; | ||
strundefined = "undefined", | ||
expando = ( "sizcache" + Math.random() ).replace( ".", "" ), | ||
// Here we check if the JavaScript engine is using some sort of | ||
// optimization where it does not always call our comparision | ||
// function. If that is the case, discard the hasDuplicate value. | ||
// Thus far that includes Google Chrome. | ||
[0, 0].sort(function() { | ||
baseHasDuplicate = false; | ||
return 0; | ||
}); | ||
// jsdom document = window.document, | ||
docElem = document.documentElement, | ||
done = 0, | ||
slice = [].slice, | ||
push = [].push, | ||
var Sizzle = function( selector, context, results, seed ) { | ||
results = results || []; | ||
// PATCH for jsdom | ||
// context = context || document; | ||
// See: https://github.com/tmpvar/jsdom/issues/375 | ||
context = context || seed[0].ownerDocument; | ||
var origContext = context; | ||
// Augment a function for special use by Sizzle | ||
markFunction = function( fn, value ) { | ||
fn[ expando ] = value || true; | ||
return fn; | ||
}, | ||
if ( context.nodeType !== 1 && context.nodeType !== 9 ) { | ||
return []; | ||
} | ||
createCache = function() { | ||
var cache = {}, | ||
keys = []; | ||
if ( !selector || typeof selector !== "string" ) { | ||
return results; | ||
} | ||
return markFunction(function( key, value ) { | ||
// Only keep the most recent entries | ||
if ( keys.push( key ) > Expr.cacheLength ) { | ||
delete cache[ keys.shift() ]; | ||
} | ||
var m, set, checkSet, extra, ret, cur, pop, i, | ||
prune = true, | ||
contextXML = Sizzle.isXML( context ), | ||
parts = [], | ||
soFar = selector; | ||
return (cache[ key ] = value); | ||
}, cache ); | ||
}, | ||
// Reset the position of the chunker regexp (start from head) | ||
do { | ||
chunker.exec( "" ); | ||
m = chunker.exec( soFar ); | ||
classCache = createCache(), | ||
tokenCache = createCache(), | ||
compilerCache = createCache(), | ||
if ( m ) { | ||
soFar = m[3]; | ||
// Regex | ||
parts.push( m[1] ); | ||
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace | ||
whitespace = "[\\x20\\t\\r\\n\\f]", | ||
// http://www.w3.org/TR/css3-syntax/#characters | ||
characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", | ||
if ( m[2] ) { | ||
extra = m[3]; | ||
break; | ||
} | ||
} | ||
} while ( m ); | ||
// Loosely modeled on CSS identifier characters | ||
// An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) | ||
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier | ||
identifier = characterEncoding.replace( "w", "w#" ), | ||
if ( parts.length > 1 && origPOS.exec( selector ) ) { | ||
// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors | ||
operators = "([*^$|!~]?=)", | ||
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + | ||
"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", | ||
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { | ||
set = posProcess( parts[0] + parts[1], context, seed ); | ||
// Prefer arguments not in parens/brackets, | ||
// then attribute selectors and non-pseudos (denoted by :), | ||
// then anything else | ||
// These preferences are here to reduce the number of selectors | ||
// needing tokenize in the PSEUDO preFilter | ||
pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", | ||
} else { | ||
set = Expr.relative[ parts[0] ] ? | ||
[ context ] : | ||
Sizzle( parts.shift(), context ); | ||
// For matchExpr.POS and matchExpr.needsContext | ||
pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)", | ||
while ( parts.length ) { | ||
selector = parts.shift(); | ||
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter | ||
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), | ||
if ( Expr.relative[ selector ] ) { | ||
selector += parts.shift(); | ||
} | ||
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), | ||
rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), | ||
rpseudo = new RegExp( pseudos ), | ||
set = posProcess( selector, set, seed ); | ||
} | ||
} | ||
// Easily-parseable/retrievable ID or TAG or CLASS selectors | ||
rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, | ||
} else { | ||
// Take a shortcut and set the context if the root selector is an ID | ||
// (but not if it'll be faster if the inner selector is an ID) | ||
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && | ||
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { | ||
rnot = /^:not/, | ||
rsibling = /[\x20\t\r\n\f]*[+~]/, | ||
rendsWithNot = /:not\($/, | ||
ret = Sizzle.find( parts.shift(), context, contextXML ); | ||
context = ret.expr ? | ||
Sizzle.filter( ret.expr, ret.set )[0] : | ||
ret.set[0]; | ||
} | ||
rheader = /h\d/i, | ||
rinputs = /input|select|textarea|button/i, | ||
if ( context ) { | ||
ret = seed ? | ||
{ expr: parts.pop(), set: makeArray(seed) } : | ||
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); | ||
rbackslash = /\\(?!\\)/g, | ||
set = ret.expr ? | ||
Sizzle.filter( ret.expr, ret.set ) : | ||
ret.set; | ||
matchExpr = { | ||
"ID": new RegExp( "^#(" + characterEncoding + ")" ), | ||
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), | ||
"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), | ||
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), | ||
"ATTR": new RegExp( "^" + attributes ), | ||
"PSEUDO": new RegExp( "^" + pseudos ), | ||
"CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + | ||
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + | ||
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ), | ||
"POS": new RegExp( pos, "ig" ), | ||
// For use in libraries implementing .is() | ||
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) | ||
}, | ||
if ( parts.length > 0 ) { | ||
checkSet = makeArray( set ); | ||
// Support | ||
} else { | ||
prune = false; | ||
} | ||
// Used for testing something on an element | ||
assert = function( fn ) { | ||
var div = document.createElement("div"); | ||
while ( parts.length ) { | ||
cur = parts.pop(); | ||
pop = cur; | ||
try { | ||
return fn( div ); | ||
} catch (e) { | ||
return false; | ||
} finally { | ||
// release memory in IE | ||
div = null; | ||
} | ||
}, | ||
if ( !Expr.relative[ cur ] ) { | ||
cur = ""; | ||
} else { | ||
pop = parts.pop(); | ||
} | ||
// Check if getElementsByTagName("*") returns only elements | ||
assertTagNameNoComments = assert(function( div ) { | ||
div.appendChild( document.createComment("") ); | ||
return !div.getElementsByTagName("*").length; | ||
}), | ||
if ( pop == null ) { | ||
pop = context; | ||
} | ||
// Check if getAttribute returns normalized href attributes | ||
assertHrefNotNormalized = assert(function( div ) { | ||
div.innerHTML = "<a href='#'></a>"; | ||
return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && | ||
div.firstChild.getAttribute("href") === "#"; | ||
}), | ||
Expr.relative[ cur ]( checkSet, pop, contextXML ); | ||
} | ||
// Check if attributes should be retrieved by attribute nodes | ||
assertAttributes = assert(function( div ) { | ||
div.innerHTML = "<select></select>"; | ||
var type = typeof div.lastChild.getAttribute("multiple"); | ||
// IE8 returns a string for some attributes even when not present | ||
return type !== "boolean" && type !== "string"; | ||
}), | ||
} else { | ||
checkSet = parts = []; | ||
// Check if getElementsByClassName can be trusted | ||
assertUsableClassName = assert(function( div ) { | ||
// Opera can't find a second classname (in 9.6) | ||
div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; | ||
if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { | ||
return false; | ||
} | ||
} | ||
if ( !checkSet ) { | ||
checkSet = set; | ||
} | ||
// Safari 3.2 caches class attributes and doesn't catch changes | ||
div.lastChild.className = "e"; | ||
return div.getElementsByClassName("e").length === 2; | ||
}), | ||
if ( !checkSet ) { | ||
Sizzle.error( cur || selector ); | ||
} | ||
// Check if getElementById returns elements by name | ||
// Check if getElementsByName privileges form controls or returns elements by ID | ||
assertUsableName = assert(function( div ) { | ||
// Inject content | ||
div.id = expando + 0; | ||
div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; | ||
docElem.insertBefore( div, docElem.firstChild ); | ||
if ( toString.call(checkSet) === "[object Array]" ) { | ||
if ( !prune ) { | ||
results.push.apply( results, checkSet ); | ||
// Test | ||
var pass = document.getElementsByName && | ||
// buggy browsers will return fewer than the correct 2 | ||
document.getElementsByName( expando ).length === 2 + | ||
// buggy browsers will return more than the correct 0 | ||
document.getElementsByName( expando + 0 ).length; | ||
assertGetIdNotName = !document.getElementById( expando ); | ||
} else if ( context && context.nodeType === 1 ) { | ||
for ( i = 0; checkSet[i] != null; i++ ) { | ||
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { | ||
results.push( set[i] ); | ||
} | ||
} | ||
// Cleanup | ||
docElem.removeChild( div ); | ||
} else { | ||
for ( i = 0; checkSet[i] != null; i++ ) { | ||
if ( checkSet[i] && checkSet[i].nodeType === 1 ) { | ||
results.push( set[i] ); | ||
} | ||
} | ||
} | ||
return pass; | ||
}); | ||
} else { | ||
makeArray( checkSet, results ); | ||
} | ||
if ( extra ) { | ||
Sizzle( extra, origContext, results, seed ); | ||
Sizzle.uniqueSort( results ); | ||
} | ||
return results; | ||
}; | ||
Sizzle.uniqueSort = function( results ) { | ||
if ( sortOrder ) { | ||
hasDuplicate = baseHasDuplicate; | ||
results.sort( sortOrder ); | ||
if ( hasDuplicate ) { | ||
for ( var i = 1; i < results.length; i++ ) { | ||
if ( results[i] === results[ i - 1 ] ) { | ||
results.splice( i--, 1 ); | ||
} | ||
} | ||
// If slice is not available, provide a backup | ||
try { | ||
slice.call( docElem.childNodes, 0 )[0].nodeType; | ||
} catch ( e ) { | ||
slice = function( i ) { | ||
var elem, results = []; | ||
for ( ; (elem = this[i]); i++ ) { | ||
results.push( elem ); | ||
} | ||
} | ||
return results; | ||
}; | ||
} | ||
return results; | ||
}; | ||
function Sizzle( selector, context, results, seed ) { | ||
results = results || []; | ||
// PATCH for jsdom | ||
// context = context || document; | ||
// See: https://github.com/tmpvar/jsdom/issues/375 | ||
context = context || seed[0].ownerDocument; | ||
var match, elem, xml, m, | ||
nodeType = context.nodeType; | ||
Sizzle.matches = function( expr, set ) { | ||
return Sizzle( expr, null, null, set ); | ||
}; | ||
Sizzle.matchesSelector = function( node, expr ) { | ||
return Sizzle( expr, null, null, [node] ).length > 0; | ||
}; | ||
Sizzle.find = function( expr, context, isXML ) { | ||
var set, i, len, match, type, left; | ||
if ( !expr ) { | ||
if ( nodeType !== 1 && nodeType !== 9 ) { | ||
return []; | ||
} | ||
for ( i = 0, len = Expr.order.length; i < len; i++ ) { | ||
type = Expr.order[i]; | ||
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { | ||
left = match[1]; | ||
match.splice( 1, 1 ); | ||
if ( left.substr( left.length - 1 ) !== "\\" ) { | ||
match[1] = (match[1] || "").replace( rBackslash, "" ); | ||
set = Expr.find[ type ]( match, context, isXML ); | ||
if ( set != null ) { | ||
expr = expr.replace( Expr.match[ type ], "" ); | ||
break; | ||
} | ||
} | ||
} | ||
if ( !selector || typeof selector !== "string" ) { | ||
return results; | ||
} | ||
if ( !set ) { | ||
set = typeof context.getElementsByTagName !== "undefined" ? | ||
context.getElementsByTagName( "*" ) : | ||
[]; | ||
} | ||
xml = isXML( context ); | ||
return { set: set, expr: expr }; | ||
}; | ||
Sizzle.filter = function( expr, set, inplace, not ) { | ||
var match, anyFound, | ||
type, found, item, filter, left, | ||
i, pass, | ||
old = expr, | ||
result = [], | ||
curLoop = set, | ||
isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); | ||
while ( expr && set.length ) { | ||
for ( type in Expr.filter ) { | ||
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { | ||
filter = Expr.filter[ type ]; | ||
left = match[1]; | ||
anyFound = false; | ||
match.splice(1,1); | ||
if ( left.substr( left.length - 1 ) === "\\" ) { | ||
continue; | ||
} | ||
if ( curLoop === result ) { | ||
result = []; | ||
} | ||
if ( Expr.preFilter[ type ] ) { | ||
match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); | ||
if ( !match ) { | ||
anyFound = found = true; | ||
} else if ( match === true ) { | ||
continue; | ||
} | ||
} | ||
if ( match ) { | ||
for ( i = 0; (item = curLoop[i]) != null; i++ ) { | ||
if ( item ) { | ||
found = filter( item, match, i, curLoop ); | ||
pass = not ^ found; | ||
if ( inplace && found != null ) { | ||
if ( pass ) { | ||
anyFound = true; | ||
} else { | ||
curLoop[i] = false; | ||
} | ||
} else if ( pass ) { | ||
result.push( item ); | ||
anyFound = true; | ||
} | ||
if ( !xml && !seed ) { | ||
if ( (match = rquickExpr.exec( selector )) ) { | ||
// Speed-up: Sizzle("#ID") | ||
if ( (m = match[1]) ) { | ||
if ( nodeType === 9 ) { | ||
elem = context.getElementById( m ); | ||
// Check parentNode to catch when Blackberry 4.6 returns | ||
// nodes that are no longer in the document #6963 | ||
if ( elem && elem.parentNode ) { | ||
// Handle the case where IE, Opera, and Webkit return items | ||
// by name instead of ID | ||
if ( elem.id === m ) { | ||
results.push( elem ); | ||
return results; | ||
} | ||
} else { | ||
return results; | ||
} | ||
} | ||
if ( found !== undefined ) { | ||
if ( !inplace ) { | ||
curLoop = result; | ||
} else { | ||
// Context is not a document | ||
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && | ||
contains( context, elem ) && elem.id === m ) { | ||
results.push( elem ); | ||
return results; | ||
} | ||
expr = expr.replace( Expr.match[ type ], "" ); | ||
if ( !anyFound ) { | ||
return []; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
// Improper expression | ||
if ( expr === old ) { | ||
if ( anyFound == null ) { | ||
Sizzle.error( expr ); | ||
// Speed-up: Sizzle("TAG") | ||
} else if ( match[2] ) { | ||
push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); | ||
return results; | ||
} else { | ||
break; | ||
// Speed-up: Sizzle(".CLASS") | ||
} else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { | ||
push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); | ||
return results; | ||
} | ||
} | ||
old = expr; | ||
} | ||
return curLoop; | ||
// All others | ||
return select( selector, context, results, seed, xml ); | ||
} | ||
Sizzle.matches = function( expr, elements ) { | ||
return Sizzle( expr, null, null, elements ); | ||
}; | ||
Sizzle.error = function( msg ) { | ||
throw new Error( "Syntax error, unrecognized expression: " + msg ); | ||
Sizzle.matchesSelector = function( elem, expr ) { | ||
return Sizzle( expr, null, null, [ elem ] ).length > 0; | ||
}; | ||
// Returns a function to use in pseudos for input types | ||
function createInputPseudo( type ) { | ||
return function( elem ) { | ||
var name = elem.nodeName.toLowerCase(); | ||
return name === "input" && elem.type === type; | ||
}; | ||
} | ||
// Returns a function to use in pseudos for buttons | ||
function createButtonPseudo( type ) { | ||
return function( elem ) { | ||
var name = elem.nodeName.toLowerCase(); | ||
return (name === "input" || name === "button") && elem.type === type; | ||
}; | ||
} | ||
/** | ||
* Utility function for retreiving the text value of an array of DOM nodes | ||
* Utility function for retrieving the text value of an array of DOM nodes | ||
* @param {Array|Element} elem | ||
*/ | ||
var getText = Sizzle.getText = function( elem ) { | ||
var i, node, | ||
nodeType = elem.nodeType, | ||
ret = ""; | ||
getText = Sizzle.getText = function( elem ) { | ||
var node, | ||
ret = "", | ||
i = 0, | ||
nodeType = elem.nodeType; | ||
if ( nodeType ) { | ||
if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { | ||
// Use textContent || innerText for elements | ||
if ( typeof elem.textContent === 'string' ) { | ||
// Use textContent for elements | ||
// innerText usage removed for consistency of new lines (see #11153) | ||
if ( typeof elem.textContent === "string" ) { | ||
return elem.textContent; | ||
} else if ( typeof elem.innerText === 'string' ) { | ||
// Replace IE's carriage returns | ||
return elem.innerText.replace( rReturn, '' ); | ||
} else { | ||
// Traverse it's children | ||
for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { | ||
// Traverse its children | ||
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { | ||
ret += getText( elem ); | ||
@@ -361,10 +314,9 @@ } | ||
} | ||
// Do not include comment or processing instruction nodes | ||
} else { | ||
// If no nodeType, this is expected to be an array | ||
for ( i = 0; (node = elem[i]); i++ ) { | ||
for ( ; (node = elem[i]); i++ ) { | ||
// Do not traverse comment nodes | ||
if ( node.nodeType !== 8 ) { | ||
ret += getText( node ); | ||
} | ||
ret += getText( node ); | ||
} | ||
@@ -375,272 +327,414 @@ } | ||
var Expr = Sizzle.selectors = { | ||
order: [ "ID", "NAME", "TAG" ], | ||
isXML = Sizzle.isXML = function isXML( elem ) { | ||
// documentElement is verified for cases where it doesn't yet exist | ||
// (such as loading iframes in IE - #4833) | ||
var documentElement = elem && (elem.ownerDocument || elem).documentElement; | ||
return documentElement ? documentElement.nodeName !== "HTML" : false; | ||
}; | ||
match: { | ||
ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, | ||
CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, | ||
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, | ||
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, | ||
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, | ||
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, | ||
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, | ||
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ | ||
}, | ||
// Element contains another | ||
contains = Sizzle.contains = docElem.contains ? | ||
function( a, b ) { | ||
var adown = a.nodeType === 9 ? a.documentElement : a, | ||
bup = b && b.parentNode; | ||
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); | ||
} : | ||
docElem.compareDocumentPosition ? | ||
function( a, b ) { | ||
return b && !!( a.compareDocumentPosition( b ) & 16 ); | ||
} : | ||
function( a, b ) { | ||
while ( (b = b.parentNode) ) { | ||
if ( b === a ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
leftMatch: {}, | ||
Sizzle.attr = function( elem, name ) { | ||
var attr, | ||
xml = isXML( elem ); | ||
attrMap: { | ||
"class": "className", | ||
"for": "htmlFor" | ||
}, | ||
if ( !xml ) { | ||
name = name.toLowerCase(); | ||
} | ||
if ( Expr.attrHandle[ name ] ) { | ||
return Expr.attrHandle[ name ]( elem ); | ||
} | ||
if ( assertAttributes || xml ) { | ||
return elem.getAttribute( name ); | ||
} | ||
attr = elem.getAttributeNode( name ); | ||
return attr ? | ||
typeof elem[ name ] === "boolean" ? | ||
elem[ name ] ? name : null : | ||
attr.specified ? attr.value : null : | ||
null; | ||
}; | ||
attrHandle: { | ||
href: function( elem ) { | ||
return elem.getAttribute( "href" ); | ||
}, | ||
type: function( elem ) { | ||
return elem.getAttribute( "type" ); | ||
} | ||
}, | ||
Expr = Sizzle.selectors = { | ||
relative: { | ||
"+": function(checkSet, part){ | ||
var isPartStr = typeof part === "string", | ||
isTag = isPartStr && !rNonWord.test( part ), | ||
isPartStrNotTag = isPartStr && !isTag; | ||
// Can be adjusted by the user | ||
cacheLength: 50, | ||
if ( isTag ) { | ||
part = part.toLowerCase(); | ||
} | ||
createPseudo: markFunction, | ||
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { | ||
if ( (elem = checkSet[i]) ) { | ||
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} | ||
match: matchExpr, | ||
checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? | ||
elem || false : | ||
elem === part; | ||
} | ||
} | ||
order: new RegExp( "ID|TAG" + | ||
(assertUsableName ? "|NAME" : "") + | ||
(assertUsableClassName ? "|CLASS" : "") | ||
), | ||
if ( isPartStrNotTag ) { | ||
Sizzle.filter( part, checkSet, true ); | ||
// IE6/7 return a modified href | ||
attrHandle: assertHrefNotNormalized ? | ||
{} : | ||
{ | ||
"href": function( elem ) { | ||
return elem.getAttribute( "href", 2 ); | ||
}, | ||
"type": function( elem ) { | ||
return elem.getAttribute("type"); | ||
} | ||
}, | ||
">": function( checkSet, part ) { | ||
var elem, | ||
isPartStr = typeof part === "string", | ||
i = 0, | ||
l = checkSet.length; | ||
find: { | ||
"ID": assertGetIdNotName ? | ||
function( id, context, xml ) { | ||
if ( typeof context.getElementById !== strundefined && !xml ) { | ||
var m = context.getElementById( id ); | ||
// Check parentNode to catch when Blackberry 4.6 returns | ||
// nodes that are no longer in the document #6963 | ||
return m && m.parentNode ? [m] : []; | ||
} | ||
} : | ||
function( id, context, xml ) { | ||
if ( typeof context.getElementById !== strundefined && !xml ) { | ||
var m = context.getElementById( id ); | ||
if ( isPartStr && !rNonWord.test( part ) ) { | ||
part = part.toLowerCase(); | ||
return m ? | ||
m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? | ||
[m] : | ||
undefined : | ||
[]; | ||
} | ||
}, | ||
for ( ; i < l; i++ ) { | ||
elem = checkSet[i]; | ||
if ( elem ) { | ||
var parent = elem.parentNode; | ||
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; | ||
} | ||
"TAG": assertTagNameNoComments ? | ||
function( tag, context ) { | ||
if ( typeof context.getElementsByTagName !== strundefined ) { | ||
return context.getElementsByTagName( tag ); | ||
} | ||
} : | ||
function( tag, context ) { | ||
var results = context.getElementsByTagName( tag ); | ||
} else { | ||
for ( ; i < l; i++ ) { | ||
elem = checkSet[i]; | ||
// Filter out possible comments | ||
if ( tag === "*" ) { | ||
var elem, | ||
tmp = [], | ||
i = 0; | ||
if ( elem ) { | ||
checkSet[i] = isPartStr ? | ||
elem.parentNode : | ||
elem.parentNode === part; | ||
for ( ; (elem = results[i]); i++ ) { | ||
if ( elem.nodeType === 1 ) { | ||
tmp.push( elem ); | ||
} | ||
} | ||
return tmp; | ||
} | ||
return results; | ||
}, | ||
if ( isPartStr ) { | ||
Sizzle.filter( part, checkSet, true ); | ||
} | ||
"NAME": function( tag, context ) { | ||
if ( typeof context.getElementsByName !== strundefined ) { | ||
return context.getElementsByName( name ); | ||
} | ||
}, | ||
"": function(checkSet, part, isXML){ | ||
var nodeCheck, | ||
doneName = done++, | ||
checkFn = dirCheck; | ||
"CLASS": function( className, context, xml ) { | ||
if ( typeof context.getElementsByClassName !== strundefined && !xml ) { | ||
return context.getElementsByClassName( className ); | ||
} | ||
} | ||
}, | ||
if ( typeof part === "string" && !rNonWord.test( part ) ) { | ||
part = part.toLowerCase(); | ||
nodeCheck = part; | ||
checkFn = dirNodeCheck; | ||
relative: { | ||
">": { dir: "parentNode", first: true }, | ||
" ": { dir: "parentNode" }, | ||
"+": { dir: "previousSibling", first: true }, | ||
"~": { dir: "previousSibling" } | ||
}, | ||
preFilter: { | ||
"ATTR": function( match ) { | ||
match[1] = match[1].replace( rbackslash, "" ); | ||
// Move the given value to match[3] whether quoted or unquoted | ||
match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); | ||
if ( match[2] === "~=" ) { | ||
match[3] = " " + match[3] + " "; | ||
} | ||
checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); | ||
return match.slice( 0, 4 ); | ||
}, | ||
"~": function( checkSet, part, isXML ) { | ||
var nodeCheck, | ||
doneName = done++, | ||
checkFn = dirCheck; | ||
"CHILD": function( match ) { | ||
/* matches from matchExpr.CHILD | ||
1 type (only|nth|...) | ||
2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) | ||
3 xn-component of xn+y argument ([+-]?\d*n|) | ||
4 sign of xn-component | ||
5 x of xn-component | ||
6 sign of y-component | ||
7 y of y-component | ||
*/ | ||
match[1] = match[1].toLowerCase(); | ||
if ( typeof part === "string" && !rNonWord.test( part ) ) { | ||
part = part.toLowerCase(); | ||
nodeCheck = part; | ||
checkFn = dirNodeCheck; | ||
} | ||
if ( match[1] === "nth" ) { | ||
// nth-child requires argument | ||
if ( !match[2] ) { | ||
Sizzle.error( match[0] ); | ||
} | ||
checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); | ||
} | ||
}, | ||
// numeric x and y parameters for Expr.filter.CHILD | ||
// remember that false/true cast respectively to 0/1 | ||
match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); | ||
match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); | ||
find: { | ||
ID: function( match, context, isXML ) { | ||
if ( typeof context.getElementById !== "undefined" && !isXML ) { | ||
var m = context.getElementById(match[1]); | ||
// Check parentNode to catch when Blackberry 4.6 returns | ||
// nodes that are no longer in the document #6963 | ||
return m && m.parentNode ? [m] : []; | ||
// other types prohibit arguments | ||
} else if ( match[2] ) { | ||
Sizzle.error( match[0] ); | ||
} | ||
return match; | ||
}, | ||
NAME: function( match, context ) { | ||
if ( typeof context.getElementsByName !== "undefined" ) { | ||
var ret = [], | ||
results = context.getElementsByName( match[1] ); | ||
"PSEUDO": function( match, context, xml ) { | ||
var unquoted, excess; | ||
if ( matchExpr["CHILD"].test( match[0] ) ) { | ||
return null; | ||
} | ||
for ( var i = 0, l = results.length; i < l; i++ ) { | ||
if ( results[i].getAttribute("name") === match[1] ) { | ||
ret.push( results[i] ); | ||
} | ||
if ( match[3] ) { | ||
match[2] = match[3]; | ||
} else if ( (unquoted = match[4]) ) { | ||
// Only check arguments that contain a pseudo | ||
if ( rpseudo.test(unquoted) && | ||
// Get excess from tokenize (recursively) | ||
(excess = tokenize( unquoted, context, xml, true )) && | ||
// advance to the next closing parenthesis | ||
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { | ||
// excess is a negative index | ||
unquoted = unquoted.slice( 0, excess ); | ||
match[0] = match[0].slice( 0, excess ); | ||
} | ||
match[2] = unquoted; | ||
} | ||
return ret.length === 0 ? null : ret; | ||
// Return only captures needed by the pseudo filter method (type and argument) | ||
return match.slice( 0, 3 ); | ||
} | ||
}, | ||
filter: { | ||
"ID": assertGetIdNotName ? | ||
function( id ) { | ||
id = id.replace( rbackslash, "" ); | ||
return function( elem ) { | ||
return elem.getAttribute("id") === id; | ||
}; | ||
} : | ||
function( id ) { | ||
id = id.replace( rbackslash, "" ); | ||
return function( elem ) { | ||
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); | ||
return node && node.value === id; | ||
}; | ||
}, | ||
"TAG": function( nodeName ) { | ||
if ( nodeName === "*" ) { | ||
return function() { return true; }; | ||
} | ||
nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); | ||
return function( elem ) { | ||
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; | ||
}; | ||
}, | ||
TAG: function( match, context ) { | ||
if ( typeof context.getElementsByTagName !== "undefined" ) { | ||
return context.getElementsByTagName( match[1] ); | ||
"CLASS": function( className ) { | ||
var pattern = classCache[ expando ][ className ]; | ||
if ( !pattern ) { | ||
pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); | ||
} | ||
} | ||
}, | ||
preFilter: { | ||
CLASS: function( match, curLoop, inplace, result, not, isXML ) { | ||
match = " " + match[1].replace( rBackslash, "" ) + " "; | ||
return function( elem ) { | ||
return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); | ||
}; | ||
}, | ||
if ( isXML ) { | ||
return match; | ||
"ATTR": function( name, operator, check ) { | ||
if ( !operator ) { | ||
return function( elem ) { | ||
return Sizzle.attr( elem, name ) != null; | ||
}; | ||
} | ||
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { | ||
if ( elem ) { | ||
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { | ||
if ( !inplace ) { | ||
result.push( elem ); | ||
} | ||
return function( elem ) { | ||
var result = Sizzle.attr( elem, name ), | ||
value = result + ""; | ||
} else if ( inplace ) { | ||
curLoop[i] = false; | ||
} | ||
if ( result == null ) { | ||
return operator === "!="; | ||
} | ||
} | ||
return false; | ||
switch ( operator ) { | ||
case "=": | ||
return value === check; | ||
case "!=": | ||
return value !== check; | ||
case "^=": | ||
return check && value.indexOf( check ) === 0; | ||
case "*=": | ||
return check && value.indexOf( check ) > -1; | ||
case "$=": | ||
return check && value.substr( value.length - check.length ) === check; | ||
case "~=": | ||
return ( " " + value + " " ).indexOf( check ) > -1; | ||
case "|=": | ||
return value === check || value.substr( 0, check.length + 1 ) === check + "-"; | ||
} | ||
}; | ||
}, | ||
ID: function( match ) { | ||
return match[1].replace( rBackslash, "" ); | ||
}, | ||
"CHILD": function( type, argument, first, last ) { | ||
TAG: function( match, curLoop ) { | ||
return match[1].replace( rBackslash, "" ).toLowerCase(); | ||
}, | ||
if ( type === "nth" ) { | ||
var doneName = done++; | ||
CHILD: function( match ) { | ||
if ( match[1] === "nth" ) { | ||
if ( !match[2] ) { | ||
Sizzle.error( match[0] ); | ||
} | ||
return function( elem ) { | ||
var parent, diff, | ||
count = 0, | ||
node = elem; | ||
match[2] = match[2].replace(/^\+|\s*/g, ''); | ||
if ( first === 1 && last === 0 ) { | ||
return true; | ||
} | ||
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' | ||
var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( | ||
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || | ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); | ||
parent = elem.parentNode; | ||
// calculate the numbers (first)n+(last) including if they are negative | ||
match[2] = (test[1] + (test[2] || 1)) - 0; | ||
match[3] = test[3] - 0; | ||
} | ||
else if ( match[2] ) { | ||
Sizzle.error( match[0] ); | ||
} | ||
if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { | ||
for ( node = parent.firstChild; node; node = node.nextSibling ) { | ||
if ( node.nodeType === 1 ) { | ||
node.sizset = ++count; | ||
if ( node === elem ) { | ||
break; | ||
} | ||
} | ||
} | ||
// TODO: Move to normal caching system | ||
match[0] = done++; | ||
parent[ expando ] = doneName; | ||
} | ||
return match; | ||
}, | ||
diff = elem.sizset - last; | ||
ATTR: function( match, curLoop, inplace, result, not, isXML ) { | ||
var name = match[1] = match[1].replace( rBackslash, "" ); | ||
if ( first === 0 ) { | ||
return diff === 0; | ||
if ( !isXML && Expr.attrMap[name] ) { | ||
match[1] = Expr.attrMap[name]; | ||
} else { | ||
return ( diff % first === 0 && diff / first >= 0 ); | ||
} | ||
}; | ||
} | ||
// Handle if an un-quoted value was used | ||
match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); | ||
return function( elem ) { | ||
var node = elem; | ||
if ( match[2] === "~=" ) { | ||
match[4] = " " + match[4] + " "; | ||
} | ||
switch ( type ) { | ||
case "only": | ||
case "first": | ||
while ( (node = node.previousSibling) ) { | ||
if ( node.nodeType === 1 ) { | ||
return false; | ||
} | ||
} | ||
return match; | ||
}, | ||
if ( type === "first" ) { | ||
return true; | ||
} | ||
PSEUDO: function( match, curLoop, inplace, result, not ) { | ||
if ( match[1] === "not" ) { | ||
// If we're dealing with a complex expression, or a simple one | ||
if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { | ||
match[3] = Sizzle(match[3], null, null, curLoop); | ||
node = elem; | ||
} else { | ||
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); | ||
/* falls through */ | ||
case "last": | ||
while ( (node = node.nextSibling) ) { | ||
if ( node.nodeType === 1 ) { | ||
return false; | ||
} | ||
} | ||
if ( !inplace ) { | ||
result.push.apply( result, ret ); | ||
} | ||
return false; | ||
return true; | ||
} | ||
}; | ||
}, | ||
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { | ||
return true; | ||
"PSEUDO": function( pseudo, argument, context, xml ) { | ||
// pseudo-class names are case-insensitive | ||
// http://www.w3.org/TR/selectors/#pseudo-classes | ||
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters | ||
var args, | ||
fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; | ||
if ( !fn ) { | ||
Sizzle.error( "unsupported pseudo: " + pseudo ); | ||
} | ||
return match; | ||
}, | ||
// The user may use createPseudo to indicate that | ||
// arguments are needed to create the filter function | ||
// just as Sizzle does | ||
if ( !fn[ expando ] ) { | ||
if ( fn.length > 1 ) { | ||
args = [ pseudo, pseudo, "", argument ]; | ||
return function( elem ) { | ||
return fn( elem, 0, args ); | ||
}; | ||
} | ||
return fn; | ||
} | ||
POS: function( match ) { | ||
match.unshift( true ); | ||
return match; | ||
return fn( argument, context, xml ); | ||
} | ||
}, | ||
filters: { | ||
enabled: function( elem ) { | ||
return elem.disabled === false && elem.type !== "hidden"; | ||
pseudos: { | ||
"not": markFunction(function( selector, context, xml ) { | ||
// Trim the selector passed to compile | ||
// to avoid treating leading and trailing | ||
// spaces as combinators | ||
var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); | ||
return function( elem ) { | ||
return !matcher( elem ); | ||
}; | ||
}), | ||
"enabled": function( elem ) { | ||
return elem.disabled === false; | ||
}, | ||
disabled: function( elem ) { | ||
"disabled": function( elem ) { | ||
return elem.disabled === true; | ||
}, | ||
checked: function( elem ) { | ||
return elem.checked === true; | ||
"checked": function( elem ) { | ||
// In CSS3, :checked should return both checked and selected elements | ||
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked | ||
var nodeName = elem.nodeName.toLowerCase(); | ||
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); | ||
}, | ||
selected: function( elem ) { | ||
"selected": function( elem ) { | ||
// Accessing this property makes selected-by-default | ||
@@ -655,250 +749,118 @@ // options in Safari work properly | ||
parent: function( elem ) { | ||
return !!elem.firstChild; | ||
"parent": function( elem ) { | ||
return !Expr.pseudos["empty"]( elem ); | ||
}, | ||
empty: function( elem ) { | ||
return !elem.firstChild; | ||
"empty": function( elem ) { | ||
// http://www.w3.org/TR/selectors/#empty-pseudo | ||
// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), | ||
// not comment, processing instructions, or others | ||
// Thanks to Diego Perini for the nodeName shortcut | ||
// Greater than "@" means alpha characters (specifically not starting with "#" or "?") | ||
var nodeType; | ||
elem = elem.firstChild; | ||
while ( elem ) { | ||
if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { | ||
return false; | ||
} | ||
elem = elem.nextSibling; | ||
} | ||
return true; | ||
}, | ||
has: function( elem, i, match ) { | ||
return !!Sizzle( match[3], elem ).length; | ||
}, | ||
"contains": markFunction(function( text ) { | ||
return function( elem ) { | ||
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; | ||
}; | ||
}), | ||
header: function( elem ) { | ||
return (/h\d/i).test( elem.nodeName ); | ||
"has": markFunction(function( selector ) { | ||
return function( elem ) { | ||
return Sizzle( selector, elem ).length > 0; | ||
}; | ||
}), | ||
"header": function( elem ) { | ||
return rheader.test( elem.nodeName ); | ||
}, | ||
text: function( elem ) { | ||
var attr = elem.getAttribute( "type" ), type = elem.type; | ||
"text": function( elem ) { | ||
var type, attr; | ||
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) | ||
// use getAttribute instead to test this case | ||
return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); | ||
return elem.nodeName.toLowerCase() === "input" && | ||
(type = elem.type) === "text" && | ||
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); | ||
}, | ||
radio: function( elem ) { | ||
return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; | ||
}, | ||
// Input types | ||
"radio": createInputPseudo("radio"), | ||
"checkbox": createInputPseudo("checkbox"), | ||
"file": createInputPseudo("file"), | ||
"password": createInputPseudo("password"), | ||
"image": createInputPseudo("image"), | ||
checkbox: function( elem ) { | ||
return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; | ||
}, | ||
"submit": createButtonPseudo("submit"), | ||
"reset": createButtonPseudo("reset"), | ||
file: function( elem ) { | ||
return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; | ||
}, | ||
password: function( elem ) { | ||
return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; | ||
}, | ||
submit: function( elem ) { | ||
"button": function( elem ) { | ||
var name = elem.nodeName.toLowerCase(); | ||
return (name === "input" || name === "button") && "submit" === elem.type; | ||
return name === "input" && elem.type === "button" || name === "button"; | ||
}, | ||
image: function( elem ) { | ||
return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; | ||
"input": function( elem ) { | ||
return rinputs.test( elem.nodeName ); | ||
}, | ||
reset: function( elem ) { | ||
var name = elem.nodeName.toLowerCase(); | ||
return (name === "input" || name === "button") && "reset" === elem.type; | ||
"focus": function( elem ) { | ||
var doc = elem.ownerDocument; | ||
return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); | ||
}, | ||
button: function( elem ) { | ||
var name = elem.nodeName.toLowerCase(); | ||
return name === "input" && "button" === elem.type || name === "button"; | ||
}, | ||
input: function( elem ) { | ||
return (/input|select|textarea|button/i).test( elem.nodeName ); | ||
}, | ||
focus: function( elem ) { | ||
"active": function( elem ) { | ||
return elem === elem.ownerDocument.activeElement; | ||
} | ||
}, | ||
setFilters: { | ||
first: function( elem, i ) { | ||
return i === 0; | ||
"first": function( elements, argument, not ) { | ||
return not ? elements.slice( 1 ) : [ elements[0] ]; | ||
}, | ||
last: function( elem, i, match, array ) { | ||
return i === array.length - 1; | ||
"last": function( elements, argument, not ) { | ||
var elem = elements.pop(); | ||
return not ? elements : [ elem ]; | ||
}, | ||
even: function( elem, i ) { | ||
return i % 2 === 0; | ||
}, | ||
odd: function( elem, i ) { | ||
return i % 2 === 1; | ||
}, | ||
lt: function( elem, i, match ) { | ||
return i < match[3] - 0; | ||
}, | ||
gt: function( elem, i, match ) { | ||
return i > match[3] - 0; | ||
}, | ||
nth: function( elem, i, match ) { | ||
return match[3] - 0 === i; | ||
}, | ||
eq: function( elem, i, match ) { | ||
return match[3] - 0 === i; | ||
} | ||
}, | ||
filter: { | ||
PSEUDO: function( elem, match, i, array ) { | ||
var name = match[1], | ||
filter = Expr.filters[ name ]; | ||
if ( filter ) { | ||
return filter( elem, i, match, array ); | ||
} else if ( name === "contains" ) { | ||
return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; | ||
} else if ( name === "not" ) { | ||
var not = match[3]; | ||
for ( var j = 0, l = not.length; j < l; j++ ) { | ||
if ( not[j] === elem ) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} else { | ||
Sizzle.error( name ); | ||
"even": function( elements, argument, not ) { | ||
var results = [], | ||
i = not ? 1 : 0, | ||
len = elements.length; | ||
for ( ; i < len; i = i + 2 ) { | ||
results.push( elements[i] ); | ||
} | ||
return results; | ||
}, | ||
CHILD: function( elem, match ) { | ||
var first, last, | ||
doneName, parent, cache, | ||
count, diff, | ||
type = match[1], | ||
node = elem; | ||
switch ( type ) { | ||
case "only": | ||
case "first": | ||
while ( (node = node.previousSibling) ) { | ||
if ( node.nodeType === 1 ) { | ||
return false; | ||
} | ||
} | ||
if ( type === "first" ) { | ||
return true; | ||
} | ||
node = elem; | ||
/* falls through */ | ||
case "last": | ||
while ( (node = node.nextSibling) ) { | ||
if ( node.nodeType === 1 ) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
case "nth": | ||
first = match[2]; | ||
last = match[3]; | ||
if ( first === 1 && last === 0 ) { | ||
return true; | ||
} | ||
doneName = match[0]; | ||
parent = elem.parentNode; | ||
if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { | ||
count = 0; | ||
for ( node = parent.firstChild; node; node = node.nextSibling ) { | ||
if ( node.nodeType === 1 ) { | ||
node.nodeIndex = ++count; | ||
} | ||
} | ||
parent[ expando ] = doneName; | ||
} | ||
diff = elem.nodeIndex - last; | ||
if ( first === 0 ) { | ||
return diff === 0; | ||
} else { | ||
return ( diff % first === 0 && diff / first >= 0 ); | ||
} | ||
"odd": function( elements, argument, not ) { | ||
var results = [], | ||
i = not ? 0 : 1, | ||
len = elements.length; | ||
for ( ; i < len; i = i + 2 ) { | ||
results.push( elements[i] ); | ||
} | ||
return results; | ||
}, | ||
ID: function( elem, match ) { | ||
return elem.nodeType === 1 && elem.getAttribute("id") === match; | ||
"lt": function( elements, argument, not ) { | ||
return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); | ||
}, | ||
TAG: function( elem, match ) { | ||
return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; | ||
"gt": function( elements, argument, not ) { | ||
return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); | ||
}, | ||
CLASS: function( elem, match ) { | ||
return (" " + (elem.className || elem.getAttribute("class")) + " ") | ||
.indexOf( match ) > -1; | ||
}, | ||
ATTR: function( elem, match ) { | ||
var name = match[1], | ||
result = Sizzle.attr ? | ||
Sizzle.attr( elem, name ) : | ||
Expr.attrHandle[ name ] ? | ||
Expr.attrHandle[ name ]( elem ) : | ||
elem[ name ] != null ? | ||
elem[ name ] : | ||
elem.getAttribute( name ), | ||
value = result + "", | ||
type = match[2], | ||
check = match[4]; | ||
return result == null ? | ||
type === "!=" : | ||
!type && Sizzle.attr ? | ||
result != null : | ||
type === "=" ? | ||
value === check : | ||
type === "*=" ? | ||
value.indexOf(check) >= 0 : | ||
type === "~=" ? | ||
(" " + value + " ").indexOf(check) >= 0 : | ||
!check ? | ||
value && result !== false : | ||
type === "!=" ? | ||
value !== check : | ||
type === "^=" ? | ||
value.indexOf(check) === 0 : | ||
type === "$=" ? | ||
value.substr(value.length - check.length) === check : | ||
type === "|=" ? | ||
value === check || value.substr(0, check.length + 1) === check + "-" : | ||
false; | ||
}, | ||
POS: function( elem, match, i, array ) { | ||
var name = match[2], | ||
filter = Expr.setFilters[ name ]; | ||
if ( filter ) { | ||
return filter( elem, i, match, array ); | ||
} | ||
"eq": function( elements, argument, not ) { | ||
var elem = elements.splice( +argument, 1 ); | ||
return not ? elements : elem; | ||
} | ||
@@ -908,63 +870,22 @@ } | ||
var origPOS = Expr.match.POS, | ||
fescape = function(all, num){ | ||
return "\\" + (num - 0 + 1); | ||
}; | ||
function siblingCheck( a, b, ret ) { | ||
if ( a === b ) { | ||
return ret; | ||
} | ||
for ( var type in Expr.match ) { | ||
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); | ||
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); | ||
} | ||
// Expose origPOS | ||
// "global" as in regardless of relation to brackets/parens | ||
Expr.match.globalPOS = origPOS; | ||
var cur = a.nextSibling; | ||
var makeArray = function( array, results ) { | ||
array = Array.prototype.slice.call( array, 0 ); | ||
while ( cur ) { | ||
if ( cur === b ) { | ||
return -1; | ||
} | ||
if ( results ) { | ||
results.push.apply( results, array ); | ||
return results; | ||
cur = cur.nextSibling; | ||
} | ||
return array; | ||
}; | ||
// Perform a simple check to determine if the browser is capable of | ||
// converting a NodeList to an array using builtin methods. | ||
// Also verifies that the returned array holds DOM nodes | ||
// (which is not the case in the Blackberry browser) | ||
try { | ||
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; | ||
// Provide a fallback method if it does not work | ||
} catch( e ) { | ||
makeArray = function( array, results ) { | ||
var i = 0, | ||
ret = results || []; | ||
if ( toString.call(array) === "[object Array]" ) { | ||
Array.prototype.push.apply( ret, array ); | ||
} else { | ||
if ( typeof array.length === "number" ) { | ||
for ( var l = array.length; i < l; i++ ) { | ||
ret.push( array[i] ); | ||
} | ||
} else { | ||
for ( ; array[i]; i++ ) { | ||
ret.push( array[i] ); | ||
} | ||
} | ||
} | ||
return ret; | ||
}; | ||
return 1; | ||
} | ||
var sortOrder, siblingCheck; | ||
if ( document.documentElement.compareDocumentPosition ) { | ||
sortOrder = function( a, b ) { | ||
sortOrder = docElem.compareDocumentPosition ? | ||
function( a, b ) { | ||
if ( a === b ) { | ||
@@ -975,11 +896,8 @@ hasDuplicate = true; | ||
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { | ||
return a.compareDocumentPosition ? -1 : 1; | ||
} | ||
return a.compareDocumentPosition(b) & 4 ? -1 : 1; | ||
}; | ||
} else { | ||
sortOrder = function( a, b ) { | ||
return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? | ||
a.compareDocumentPosition : | ||
a.compareDocumentPosition(b) & 4 | ||
) ? -1 : 1; | ||
} : | ||
function( a, b ) { | ||
// The nodes are identical, we can exit early | ||
@@ -1044,412 +962,590 @@ if ( a === b ) { | ||
siblingCheck = function( a, b, ret ) { | ||
if ( a === b ) { | ||
return ret; | ||
} | ||
// Always assume the presence of duplicates if sort doesn't | ||
// pass them to our comparison function (as in Google Chrome). | ||
[0, 0].sort( sortOrder ); | ||
baseHasDuplicate = !hasDuplicate; | ||
var cur = a.nextSibling; | ||
// Document sorting and removing duplicates | ||
Sizzle.uniqueSort = function( results ) { | ||
var elem, | ||
i = 1; | ||
while ( cur ) { | ||
if ( cur === b ) { | ||
return -1; | ||
hasDuplicate = baseHasDuplicate; | ||
results.sort( sortOrder ); | ||
if ( hasDuplicate ) { | ||
for ( ; (elem = results[i]); i++ ) { | ||
if ( elem === results[ i - 1 ] ) { | ||
results.splice( i--, 1 ); | ||
} | ||
cur = cur.nextSibling; | ||
} | ||
} | ||
return 1; | ||
}; | ||
} | ||
return results; | ||
}; | ||
// Check to see if the browser returns elements by name when | ||
// querying by getElementById (and provide a workaround) | ||
(function(){ | ||
// We're going to inject a fake input element with a specified name | ||
var form = document.createElement("div"), | ||
id = "script" + (new Date()).getTime(), | ||
root = document.documentElement; | ||
Sizzle.error = function( msg ) { | ||
throw new Error( "Syntax error, unrecognized expression: " + msg ); | ||
}; | ||
form.innerHTML = "<a name='" + id + "'/>"; | ||
function tokenize( selector, context, xml, parseOnly ) { | ||
var matched, match, tokens, type, | ||
soFar, groups, group, i, | ||
preFilters, filters, | ||
checkContext = !xml && context !== document, | ||
// Token cache should maintain spaces | ||
key = ( checkContext ? "<s>" : "" ) + selector.replace( rtrim, "$1<s>" ), | ||
cached = tokenCache[ expando ][ key ]; | ||
// Inject it into the root element, check its status, and remove it quickly | ||
root.insertBefore( form, root.firstChild ); | ||
if ( cached ) { | ||
return parseOnly ? 0 : slice.call( cached, 0 ); | ||
} | ||
// The workaround has to do additional checks after a getElementById | ||
// Which slows things down for other browsers (hence the branching) | ||
if ( document.getElementById( id ) ) { | ||
Expr.find.ID = function( match, context, isXML ) { | ||
if ( typeof context.getElementById !== "undefined" && !isXML ) { | ||
var m = context.getElementById(match[1]); | ||
soFar = selector; | ||
groups = []; | ||
i = 0; | ||
preFilters = Expr.preFilter; | ||
filters = Expr.filter; | ||
return m ? | ||
m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? | ||
[m] : | ||
undefined : | ||
[]; | ||
while ( soFar ) { | ||
// Comma and first run | ||
if ( !matched || (match = rcomma.exec( soFar )) ) { | ||
if ( match ) { | ||
soFar = soFar.slice( match[0].length ); | ||
tokens.selector = group; | ||
} | ||
}; | ||
groups.push( tokens = [] ); | ||
group = ""; | ||
Expr.filter.ID = function( elem, match ) { | ||
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); | ||
// Need to make sure we're within a narrower context if necessary | ||
// Adding a descendant combinator will generate what is needed | ||
if ( checkContext ) { | ||
soFar = " " + soFar; | ||
} | ||
} | ||
return elem.nodeType === 1 && node && node.nodeValue === match; | ||
}; | ||
} | ||
matched = false; | ||
root.removeChild( form ); | ||
// Combinators | ||
if ( (match = rcombinators.exec( soFar )) ) { | ||
group += match[0]; | ||
soFar = soFar.slice( match[0].length ); | ||
// release memory in IE | ||
root = form = null; | ||
})(); | ||
// Cast descendant combinators to space | ||
matched = tokens.push({ | ||
part: match.pop().replace( rtrim, " " ), | ||
string: match[0], | ||
captures: match | ||
}); | ||
} | ||
(function(){ | ||
// Check to see if the browser returns only elements | ||
// when doing getElementsByTagName("*") | ||
// Filters | ||
for ( type in filters ) { | ||
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || | ||
( match = preFilters[ type ](match, context, xml) )) ) { | ||
// Create a fake element | ||
var div = document.createElement("div"); | ||
div.appendChild( document.createComment("") ); | ||
group += match[0]; | ||
soFar = soFar.slice( match[0].length ); | ||
matched = tokens.push({ | ||
part: type, | ||
string: match.shift(), | ||
captures: match | ||
}); | ||
} | ||
} | ||
// Make sure no comments are found | ||
if ( div.getElementsByTagName("*").length > 0 ) { | ||
Expr.find.TAG = function( match, context ) { | ||
var results = context.getElementsByTagName( match[1] ); | ||
if ( !matched ) { | ||
break; | ||
} | ||
} | ||
// Filter out possible comments | ||
if ( match[1] === "*" ) { | ||
var tmp = []; | ||
// Attach the full group as a selector | ||
if ( group ) { | ||
tokens.selector = group; | ||
} | ||
for ( var i = 0; results[i]; i++ ) { | ||
if ( results[i].nodeType === 1 ) { | ||
tmp.push( results[i] ); | ||
} | ||
} | ||
// Return the length of the invalid excess | ||
// if we're just parsing | ||
// Otherwise, throw an error or return tokens | ||
return parseOnly ? | ||
soFar.length : | ||
soFar ? | ||
Sizzle.error( selector ) : | ||
// Cache the tokens | ||
slice.call( tokenCache(key, groups), 0 ); | ||
} | ||
results = tmp; | ||
} | ||
function addCombinator( matcher, combinator, context, xml ) { | ||
var dir = combinator.dir, | ||
doneName = done++; | ||
return results; | ||
if ( !matcher ) { | ||
// If there is no matcher to check, check against the context | ||
matcher = function( elem ) { | ||
return elem === context; | ||
}; | ||
} | ||
return combinator.first ? | ||
function( elem ) { | ||
while ( (elem = elem[ dir ]) ) { | ||
if ( elem.nodeType === 1 ) { | ||
return matcher( elem ) && elem; | ||
} | ||
} | ||
} : | ||
xml ? | ||
function( elem ) { | ||
while ( (elem = elem[ dir ]) ) { | ||
if ( elem.nodeType === 1 ) { | ||
if ( matcher( elem ) ) { | ||
return elem; | ||
} | ||
} | ||
} | ||
} : | ||
function( elem ) { | ||
var cache, | ||
dirkey = doneName + "." + dirruns, | ||
cachedkey = dirkey + "." + cachedruns; | ||
while ( (elem = elem[ dir ]) ) { | ||
if ( elem.nodeType === 1 ) { | ||
if ( (cache = elem[ expando ]) === cachedkey ) { | ||
return elem.sizset; | ||
} else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { | ||
if ( elem.sizset ) { | ||
return elem; | ||
} | ||
} else { | ||
elem[ expando ] = cachedkey; | ||
if ( matcher( elem ) ) { | ||
elem.sizset = true; | ||
return elem; | ||
} | ||
elem.sizset = false; | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
// Check to see if an attribute returns normalized href attributes | ||
div.innerHTML = "<a href='#'></a>"; | ||
function addMatcher( higher, deeper ) { | ||
return higher ? | ||
function( elem ) { | ||
var result = deeper( elem ); | ||
return result && higher( result === true ? elem : result ); | ||
} : | ||
deeper; | ||
} | ||
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && | ||
div.firstChild.getAttribute("href") !== "#" ) { | ||
// ["TAG", ">", "ID", " ", "CLASS"] | ||
function matcherFromTokens( tokens, context, xml ) { | ||
var token, matcher, | ||
i = 0; | ||
Expr.attrHandle.href = function( elem ) { | ||
return elem.getAttribute( "href", 2 ); | ||
}; | ||
for ( ; (token = tokens[i]); i++ ) { | ||
if ( Expr.relative[ token.part ] ) { | ||
matcher = addCombinator( matcher, Expr.relative[ token.part ], context, xml ); | ||
} else { | ||
matcher = addMatcher( matcher, Expr.filter[ token.part ].apply(null, token.captures.concat( context, xml )) ); | ||
} | ||
} | ||
// release memory in IE | ||
div = null; | ||
})(); | ||
return matcher; | ||
} | ||
// Patch for jsdom | ||
if ( document.querySelectorAll && false ) { | ||
(function(){ | ||
var oldSizzle = Sizzle, | ||
div = document.createElement("div"), | ||
id = "__sizzle__"; | ||
div.innerHTML = "<p class='TEST'></p>"; | ||
// Safari can't handle uppercase or unicode characters when | ||
// in quirks mode. | ||
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { | ||
return; | ||
function matcherFromGroupMatchers( matchers ) { | ||
return function( elem ) { | ||
var matcher, | ||
j = 0; | ||
for ( ; (matcher = matchers[j]); j++ ) { | ||
if ( matcher(elem) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
} | ||
Sizzle = function( query, context, extra, seed ) { | ||
context = context || document; | ||
compile = Sizzle.compile = function( selector, context, xml ) { | ||
var group, i, len, | ||
cached = compilerCache[ expando ][ selector ]; | ||
// Only use querySelectorAll on non-XML documents | ||
// (ID selectors don't work in non-HTML documents) | ||
if ( !seed && !Sizzle.isXML(context) ) { | ||
// See if we find a selector to speed up | ||
var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); | ||
// Return a cached group function if already generated (context dependent) | ||
if ( cached && cached.context === context ) { | ||
return cached; | ||
} | ||
if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { | ||
// Speed-up: Sizzle("TAG") | ||
if ( match[1] ) { | ||
return makeArray( context.getElementsByTagName( query ), extra ); | ||
// Generate a function of recursive functions that can be used to check each element | ||
group = tokenize( selector, context, xml ); | ||
for ( i = 0, len = group.length; i < len; i++ ) { | ||
group[i] = matcherFromTokens(group[i], context, xml); | ||
} | ||
// Speed-up: Sizzle(".CLASS") | ||
} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { | ||
return makeArray( context.getElementsByClassName( match[2] ), extra ); | ||
} | ||
} | ||
// Cache the compiled function | ||
cached = compilerCache( selector, matcherFromGroupMatchers(group) ); | ||
cached.context = context; | ||
cached.runs = cached.dirruns = 0; | ||
return cached; | ||
}; | ||
if ( context.nodeType === 9 ) { | ||
// Speed-up: Sizzle("body") | ||
// The body element only exists once, optimize finding it | ||
if ( query === "body" && context.body ) { | ||
return makeArray( [ context.body ], extra ); | ||
function multipleContexts( selector, contexts, results, seed ) { | ||
var i = 0, | ||
len = contexts.length; | ||
for ( ; i < len; i++ ) { | ||
Sizzle( selector, contexts[i], results, seed ); | ||
} | ||
} | ||
// Speed-up: Sizzle("#ID") | ||
} else if ( match && match[3] ) { | ||
var elem = context.getElementById( match[3] ); | ||
function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { | ||
var results, | ||
fn = Expr.setFilters[ posfilter.toLowerCase() ]; | ||
// Check parentNode to catch when Blackberry 4.6 returns | ||
// nodes that are no longer in the document #6963 | ||
if ( elem && elem.parentNode ) { | ||
// Handle the case where IE and Opera return items | ||
// by name instead of ID | ||
if ( elem.id === match[3] ) { | ||
return makeArray( [ elem ], extra ); | ||
} | ||
if ( !fn ) { | ||
Sizzle.error( posfilter ); | ||
} | ||
} else { | ||
return makeArray( [], extra ); | ||
} | ||
} | ||
if ( selector || !(results = seed) ) { | ||
multipleContexts( selector || "*", contexts, (results = []), seed ); | ||
} | ||
try { | ||
return makeArray( context.querySelectorAll(query), extra ); | ||
} catch(qsaError) {} | ||
return results.length > 0 ? fn( results, argument, not ) : []; | ||
} | ||
// qSA works strangely on Element-rooted queries | ||
// We can work around this by specifying an extra ID on the root | ||
// and working up from there (Thanks to Andrew Dupont for the technique) | ||
// IE 8 doesn't work on object elements | ||
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { | ||
var oldContext = context, | ||
old = context.getAttribute( "id" ), | ||
nid = old || id, | ||
hasParent = context.parentNode, | ||
relativeHierarchySelector = /^\s*[+~]/.test( query ); | ||
function handlePOS( groups, context, results, seed ) { | ||
var group, part, j, groupLen, token, selector, | ||
anchor, elements, match, matched, | ||
lastIndex, currentContexts, not, | ||
i = 0, | ||
len = groups.length, | ||
rpos = matchExpr["POS"], | ||
// This is generated here in case matchExpr["POS"] is extended | ||
rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), | ||
// This is for making sure non-participating | ||
// matching groups are represented cross-browser (IE6-8) | ||
setUndefined = function() { | ||
var i = 1, | ||
len = arguments.length - 2; | ||
for ( ; i < len; i++ ) { | ||
if ( arguments[i] === undefined ) { | ||
match[i] = undefined; | ||
} | ||
} | ||
}; | ||
if ( !old ) { | ||
context.setAttribute( "id", nid ); | ||
} else { | ||
nid = nid.replace( /'/g, "\\$&" ); | ||
} | ||
if ( relativeHierarchySelector && hasParent ) { | ||
context = context.parentNode; | ||
} | ||
for ( ; i < len; i++ ) { | ||
group = groups[i]; | ||
part = ""; | ||
elements = seed; | ||
for ( j = 0, groupLen = group.length; j < groupLen; j++ ) { | ||
token = group[j]; | ||
selector = token.string; | ||
if ( token.part === "PSEUDO" ) { | ||
// Reset regex index to 0 | ||
rpos.exec(""); | ||
anchor = 0; | ||
while ( (match = rpos.exec( selector )) ) { | ||
matched = true; | ||
lastIndex = rpos.lastIndex = match.index + match[0].length; | ||
if ( lastIndex > anchor ) { | ||
part += selector.slice( anchor, match.index ); | ||
anchor = lastIndex; | ||
currentContexts = [ context ]; | ||
try { | ||
if ( !relativeHierarchySelector || hasParent ) { | ||
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); | ||
if ( rcombinators.test(part) ) { | ||
if ( elements ) { | ||
currentContexts = elements; | ||
} | ||
elements = seed; | ||
} | ||
} catch(pseudoError) { | ||
} finally { | ||
if ( !old ) { | ||
oldContext.removeAttribute( "id" ); | ||
if ( (not = rendsWithNot.test( part )) ) { | ||
part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); | ||
anchor++; | ||
} | ||
if ( match.length > 1 ) { | ||
match[0].replace( rposgroups, setUndefined ); | ||
} | ||
elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); | ||
} | ||
part = ""; | ||
} | ||
} | ||
return oldSizzle(query, context, extra, seed); | ||
}; | ||
if ( !matched ) { | ||
part += selector; | ||
} | ||
matched = false; | ||
} | ||
for ( var prop in oldSizzle ) { | ||
Sizzle[ prop ] = oldSizzle[ prop ]; | ||
if ( part ) { | ||
if ( rcombinators.test(part) ) { | ||
multipleContexts( part, elements || [ context ], results, seed ); | ||
} else { | ||
Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); | ||
} | ||
} else { | ||
push.apply( results, elements ); | ||
} | ||
} | ||
// release memory in IE | ||
div = null; | ||
})(); | ||
// Do not sort if this is a single filter | ||
return len === 1 ? results : Sizzle.uniqueSort( results ); | ||
} | ||
(function(){ | ||
var html = document.documentElement, | ||
matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; | ||
function select( selector, context, results, seed, xml ) { | ||
// Remove excessive whitespace | ||
selector = selector.replace( rtrim, "$1" ); | ||
var elements, matcher, cached, elem, | ||
i, tokens, token, lastToken, findContext, type, | ||
match = tokenize( selector, context, xml ), | ||
contextNodeType = context.nodeType; | ||
if ( matches ) { | ||
// Check to see if it's possible to do matchesSelector | ||
// on a disconnected node (IE 9 fails this) | ||
var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), | ||
pseudoWorks = false; | ||
// POS handling | ||
if ( matchExpr["POS"].test(selector) ) { | ||
return handlePOS( match, context, results, seed ); | ||
} | ||
try { | ||
// This should fail with an exception | ||
// Gecko does not error, returns false instead | ||
matches.call( document.documentElement, "[test!='']:sizzle" ); | ||
if ( seed ) { | ||
elements = slice.call( seed, 0 ); | ||
} catch( pseudoError ) { | ||
pseudoWorks = true; | ||
} | ||
// To maintain document order, only narrow the | ||
// set if there is one group | ||
} else if ( match.length === 1 ) { | ||
Sizzle.matchesSelector = function( node, expr ) { | ||
// Make sure that attribute selectors are quoted | ||
expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); | ||
// Take a shortcut and set the context if the root selector is an ID | ||
if ( (tokens = slice.call( match[0], 0 )).length > 2 && | ||
(token = tokens[0]).part === "ID" && | ||
contextNodeType === 9 && !xml && | ||
Expr.relative[ tokens[1].part ] ) { | ||
if ( !Sizzle.isXML( node ) ) { | ||
try { | ||
if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { | ||
var ret = matches.call( node, expr ); | ||
// IE 9's matchesSelector returns false on disconnected nodes | ||
if ( ret || !disconnectedMatch || | ||
// As well, disconnected nodes are said to be in a document | ||
// fragment in IE 9, so check for that | ||
node.document && node.document.nodeType !== 11 ) { | ||
return ret; | ||
} | ||
} | ||
} catch(e) {} | ||
context = Expr.find["ID"]( token.captures[0].replace( rbackslash, "" ), context, xml )[0]; | ||
if ( !context ) { | ||
return results; | ||
} | ||
return Sizzle(expr, null, null, [node]).length > 0; | ||
}; | ||
} | ||
})(); | ||
selector = selector.slice( tokens.shift().string.length ); | ||
} | ||
(function(){ | ||
var div = document.createElement("div"); | ||
findContext = ( (match = rsibling.exec( tokens[0].string )) && !match.index && context.parentNode ) || context; | ||
div.innerHTML = "<div class='test e'></div><div class='test'></div>"; | ||
// Reduce the set if possible | ||
lastToken = ""; | ||
for ( i = tokens.length - 1; i >= 0; i-- ) { | ||
token = tokens[i]; | ||
type = token.part; | ||
lastToken = token.string + lastToken; | ||
if ( Expr.relative[ type ] ) { | ||
break; | ||
} | ||
if ( Expr.order.test(type) ) { | ||
elements = Expr.find[ type ]( token.captures[0].replace( rbackslash, "" ), findContext, xml ); | ||
if ( elements == null ) { | ||
continue; | ||
} else { | ||
selector = selector.slice( 0, selector.length - lastToken.length ) + | ||
lastToken.replace( matchExpr[ type ], "" ); | ||
// Opera can't find a second classname (in 9.6) | ||
// Also, make sure that getElementsByClassName actually exists | ||
if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { | ||
return; | ||
} | ||
if ( !selector ) { | ||
push.apply( results, slice.call(elements, 0) ); | ||
} | ||
// Safari caches class attributes, doesn't catch changes (in 3.2) | ||
div.lastChild.className = "e"; | ||
if ( div.getElementsByClassName("e").length === 1 ) { | ||
return; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
Expr.order.splice(1, 0, "CLASS"); | ||
Expr.find.CLASS = function( match, context, isXML ) { | ||
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { | ||
return context.getElementsByClassName(match[1]); | ||
// Only loop over the given elements once | ||
if ( selector ) { | ||
matcher = compile( selector, context, xml ); | ||
dirruns = matcher.dirruns++; | ||
if ( elements == null ) { | ||
elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); | ||
} | ||
}; | ||
// release memory in IE | ||
div = null; | ||
})(); | ||
for ( i = 0; (elem = elements[i]); i++ ) { | ||
cachedruns = matcher.runs++; | ||
if ( matcher(elem) ) { | ||
results.push( elem ); | ||
} | ||
} | ||
} | ||
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { | ||
for ( var i = 0, l = checkSet.length; i < l; i++ ) { | ||
var elem = checkSet[i]; | ||
return results; | ||
} | ||
if ( elem ) { | ||
var match = false; | ||
// Patch for jsdom | ||
// we use sizzle to implement querySelectorAll, so sizzle cannot use it | ||
if ( document.querySelectorAll && false ) { | ||
(function() { | ||
var disconnectedMatch, | ||
oldSelect = select, | ||
rescape = /'|\\/g, | ||
rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, | ||
rbuggyQSA = [], | ||
// matchesSelector(:active) reports false when true (IE9/Opera 11.5) | ||
// A support test would require too much code (would include document ready) | ||
// just skip matchesSelector for :active | ||
rbuggyMatches = [":active"], | ||
matches = docElem.matchesSelector || | ||
docElem.mozMatchesSelector || | ||
docElem.webkitMatchesSelector || | ||
docElem.oMatchesSelector || | ||
docElem.msMatchesSelector; | ||
elem = elem[dir]; | ||
// Build QSA regex | ||
// Regex strategy adopted from Diego Perini | ||
assert(function( div ) { | ||
// Select is set to empty string on purpose | ||
// This is to test IE's treatment of not explictly | ||
// setting a boolean content attribute, | ||
// since its presence should be enough | ||
// http://bugs.jquery.com/ticket/12359 | ||
div.innerHTML = "<select><option selected=''></option></select>"; | ||
while ( elem ) { | ||
if ( elem[ expando ] === doneName ) { | ||
match = checkSet[elem.sizset]; | ||
break; | ||
} | ||
// IE8 - Some boolean attributes are not treated correctly | ||
if ( !div.querySelectorAll("[selected]").length ) { | ||
rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); | ||
} | ||
if ( elem.nodeType === 1 && !isXML ){ | ||
elem[ expando ] = doneName; | ||
elem.sizset = i; | ||
} | ||
// Webkit/Opera - :checked should return selected option elements | ||
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked | ||
// IE8 throws error here (do not put tests after this one) | ||
if ( !div.querySelectorAll(":checked").length ) { | ||
rbuggyQSA.push(":checked"); | ||
} | ||
}); | ||
if ( elem.nodeName.toLowerCase() === cur ) { | ||
match = elem; | ||
break; | ||
} | ||
assert(function( div ) { | ||
elem = elem[dir]; | ||
// Opera 10-12/IE9 - ^= $= *= and empty values | ||
// Should not select anything | ||
div.innerHTML = "<p test=''></p>"; | ||
if ( div.querySelectorAll("[test^='']").length ) { | ||
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); | ||
} | ||
checkSet[i] = match; | ||
} | ||
} | ||
} | ||
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) | ||
// IE8 throws error here (do not put tests after this one) | ||
div.innerHTML = "<input type='hidden'/>"; | ||
if ( !div.querySelectorAll(":enabled").length ) { | ||
rbuggyQSA.push(":enabled", ":disabled"); | ||
} | ||
}); | ||
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { | ||
for ( var i = 0, l = checkSet.length; i < l; i++ ) { | ||
var elem = checkSet[i]; | ||
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); | ||
if ( elem ) { | ||
var match = false; | ||
select = function( selector, context, results, seed, xml ) { | ||
// Only use querySelectorAll when not filtering, | ||
// when this is not xml, | ||
// and when no QSA bugs apply | ||
if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { | ||
if ( context.nodeType === 9 ) { | ||
try { | ||
push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); | ||
return results; | ||
} catch(qsaError) {} | ||
// qSA works strangely on Element-rooted queries | ||
// We can work around this by specifying an extra ID on the root | ||
// and working up from there (Thanks to Andrew Dupont for the technique) | ||
// IE 8 doesn't work on object elements | ||
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { | ||
var groups, i, len, | ||
old = context.getAttribute("id"), | ||
nid = old || expando, | ||
newContext = rsibling.test( selector ) && context.parentNode || context; | ||
elem = elem[dir]; | ||
if ( old ) { | ||
nid = nid.replace( rescape, "\\$&" ); | ||
} else { | ||
context.setAttribute( "id", nid ); | ||
} | ||
while ( elem ) { | ||
if ( elem[ expando ] === doneName ) { | ||
match = checkSet[elem.sizset]; | ||
break; | ||
} | ||
if ( elem.nodeType === 1 ) { | ||
if ( !isXML ) { | ||
elem[ expando ] = doneName; | ||
elem.sizset = i; | ||
groups = tokenize(selector, context, xml); | ||
// Trailing space is unnecessary | ||
// There is always a context check | ||
nid = "[id='" + nid + "']"; | ||
for ( i = 0, len = groups.length; i < len; i++ ) { | ||
groups[i] = nid + groups[i].selector; | ||
} | ||
if ( typeof cur !== "string" ) { | ||
if ( elem === cur ) { | ||
match = true; | ||
break; | ||
try { | ||
push.apply( results, slice.call( newContext.querySelectorAll( | ||
groups.join(",") | ||
), 0 ) ); | ||
return results; | ||
} catch(qsaError) { | ||
} finally { | ||
if ( !old ) { | ||
context.removeAttribute("id"); | ||
} | ||
} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { | ||
match = elem; | ||
break; | ||
} | ||
} | ||
elem = elem[dir]; | ||
} | ||
checkSet[i] = match; | ||
} | ||
} | ||
} | ||
return oldSelect( selector, context, results, seed, xml ); | ||
}; | ||
if ( document.documentElement.contains ) { | ||
Sizzle.contains = function( a, b ) { | ||
return a !== b && (a.contains ? a.contains(b) : true); | ||
}; | ||
if ( matches ) { | ||
assert(function( div ) { | ||
// Check to see if it's possible to do matchesSelector | ||
// on a disconnected node (IE 9) | ||
disconnectedMatch = matches.call( div, "div" ); | ||
} else if ( document.documentElement.compareDocumentPosition ) { | ||
Sizzle.contains = function( a, b ) { | ||
return !!(a.compareDocumentPosition(b) & 16); | ||
}; | ||
// This should fail with an exception | ||
// Gecko does not error, returns false instead | ||
try { | ||
matches.call( div, "[test!='']:sizzle" ); | ||
rbuggyMatches.push( matchExpr["PSEUDO"].source, matchExpr["POS"].source, "!=" ); | ||
} catch ( e ) {} | ||
}); | ||
} else { | ||
Sizzle.contains = function() { | ||
return false; | ||
}; | ||
} | ||
// rbuggyMatches always contains :active, so no need for a length check | ||
rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); | ||
Sizzle.isXML = function( elem ) { | ||
// documentElement is verified for cases where it doesn't yet exist | ||
// (such as loading iframes in IE - #4833) | ||
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; | ||
Sizzle.matchesSelector = function( elem, expr ) { | ||
// Make sure that attribute selectors are quoted | ||
expr = expr.replace( rattributeQuotes, "='$1']" ); | ||
return documentElement ? documentElement.nodeName !== "HTML" : false; | ||
}; | ||
// rbuggyMatches always contains :active, so no need for an existence check | ||
if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { | ||
try { | ||
var ret = matches.call( elem, expr ); | ||
var posProcess = function( selector, context, seed ) { | ||
var match, | ||
tmpSet = [], | ||
later = "", | ||
root = context.nodeType ? [context] : context; | ||
// IE 9's matchesSelector returns false on disconnected nodes | ||
if ( ret || disconnectedMatch || | ||
// As well, disconnected nodes are said to be in a document | ||
// fragment in IE 9 | ||
elem.document && elem.document.nodeType !== 11 ) { | ||
return ret; | ||
} | ||
} catch(e) {} | ||
} | ||
// Position selectors must be done after the filter | ||
// And so must :not(positional) so we move all PSEUDOs to the end | ||
while ( (match = Expr.match.PSEUDO.exec( selector )) ) { | ||
later += match[0]; | ||
selector = selector.replace( Expr.match.PSEUDO, "" ); | ||
} | ||
return Sizzle( expr, null, null, [ elem ] ).length > 0; | ||
}; | ||
} | ||
})(); | ||
} | ||
selector = Expr.relative[selector] ? selector + "*" : selector; | ||
// Deprecated | ||
Expr.setFilters["nth"] = Expr.setFilters["eq"]; | ||
for ( var i = 0, l = root.length; i < l; i++ ) { | ||
Sizzle( selector, root[i], tmpSet, seed ); | ||
} | ||
// Back-compat | ||
Expr.filters = Expr.pseudos; | ||
return Sizzle.filter( later, tmpSet ); | ||
}; | ||
// EXPOSE | ||
/* patch for jsdom */ | ||
return Sizzle; | ||
/* | ||
if ( typeof define === "function" && define.amd ) { | ||
define(function() { return Sizzle; }); | ||
} else { | ||
window.Sizzle = Sizzle; | ||
} | ||
*/ | ||
// EXPOSE | ||
}; | ||
}; |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ Copyright (c) 2010 Elijah Insua |
{ | ||
"name": "jsdom", | ||
"version": "0.2.15", | ||
"description": "A javascript implementation of the W3C DOM", | ||
"version": "0.2.16", | ||
"description": "A JavaScript implementation of the W3C DOM", | ||
"keywords": [ | ||
"dom", | ||
"w3c", | ||
"javascript" | ||
"html" | ||
], | ||
"maintainers": [{ | ||
"name": "Elijah Insua", | ||
"email": "tmpvar@gmail.com", | ||
"url": "http://tmpvar.com" | ||
}], | ||
"maintainers": [ | ||
{ | ||
"name": "Elijah Insua", | ||
"email": "tmpvar@gmail.com", | ||
"url": "http://tmpvar.com" | ||
}, | ||
{ | ||
"name": "Domenic Denicola", | ||
"email": "domenic@domenicdenicola.com", | ||
"url": "http://domenicdenicola.com" | ||
} | ||
], | ||
"contributors": [ | ||
@@ -57,3 +64,2 @@ { | ||
"name": "Sam Ruby", | ||
"email": "N/A", | ||
"url": "http://intertwingly.net/blog/" | ||
@@ -154,2 +160,7 @@ }, | ||
"email" : "jroberts@logitech.com" | ||
}, | ||
{ | ||
"name" : "Chad Walker", | ||
"email" : "chad@chad-cat-lore-eddie.com", | ||
"url" : "https://github.com/chad3814" | ||
} | ||
@@ -179,3 +190,4 @@ ], | ||
"request" : "2.x", | ||
"cssom" : "0.2.x", | ||
"cssom" : ">=0.2.2", | ||
"cssstyle" : "0.2.x", | ||
"contextify" : "0.1.x" | ||
@@ -191,7 +203,9 @@ }, | ||
}, | ||
"engines" : { "node" : ">=0.1.9" }, | ||
"directories": { | ||
"lib": "./lib/jsdom" | ||
}, | ||
"scripts": { | ||
"test": "node ./test/runner" | ||
}, | ||
"main": "./lib/jsdom" | ||
} |
402
README.md
# jsdom | ||
A javascript implementation of the W3C DOM. | ||
A JavaScript implementation of the W3C DOM. | ||
## Install | ||
npm install jsdom | ||
```bash | ||
$ npm install jsdom | ||
``` | ||
or | ||
git clone http://github.com/tmpvar/jsdom.git | ||
cd jsdom | ||
npm link | ||
## Human contact | ||
see: [mailing list][] | ||
see: [mailing list](http://groups.google.com/group/jsdom) | ||
[mailing list]: http://groups.google.com/group/jsdom | ||
## Easymode | ||
@@ -30,101 +21,108 @@ | ||
// Count all of the links from the nodejs build page | ||
var jsdom = require("jsdom"); | ||
```js | ||
// 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.js"], | ||
function (errors, window) { | ||
console.log("there have been", window.$("a").length, "nodejs releases!"); | ||
} | ||
); | ||
``` | ||
or with raw html | ||
or with raw HTML | ||
// Run some jQuery on a html fragment | ||
var jsdom = require('jsdom'); | ||
```js | ||
// Run some jQuery on a html fragment | ||
var jsdom = require("jsdom"); | ||
jsdom.env('<p><a class="the-link" href="https://github.com/tmpvar/jsdom">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="https://github.com/tmpvar/jsdom">jsdom\'s Homepage</a></p>', | ||
["http://code.jquery.com/jquery.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'); | ||
```js | ||
// 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()); | ||
}); | ||
} | ||
jsdom.env({ | ||
html: "http://news.ycombinator.com/", | ||
scripts: ["http://code.jquery.com/jquery.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 | ||
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(); | ||
```js | ||
// Print all of the news items on hackernews | ||
var jsdom = require("jsdom"); | ||
var fs = require("fs"); | ||
var jquery = fs.readFileSync("./jquery.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()); | ||
}); | ||
} | ||
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` 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], [config], callback) | ||
```js | ||
jsdom.env(html, [scripts], [config], callback); | ||
``` | ||
- `html` (**required**) | ||
May be a url, html fragment, or file | ||
- `html` (**required**): may be a URL, HTML fragment, or file. | ||
- `scripts` (**optional**): may contain files or URLs. | ||
- `config` (**optional**): see below. | ||
- `callback` (**required**): takes two arguments: | ||
- `errors`: an array of errors | ||
- `window`: a brand new window | ||
- `scripts` (**optional**) | ||
May contain files or urls | ||
_example:_ | ||
- `callback` (**required**) | ||
Takes 2 arguments: | ||
- `errors` : array of errors | ||
- `window` : a brand new window | ||
```js | ||
jsdom.env(html, function (errors, window) { | ||
// free memory associated with the window | ||
window.close(); | ||
}); | ||
``` | ||
_example:_ | ||
If you would like to specify a configuration object only: | ||
jsdom.env(html, function(`errors`, `window`) { | ||
// free memory associated with the window | ||
window.close(); | ||
}); | ||
```js | ||
jsdom.env(config); | ||
``` | ||
If you would like to specify a configuration object | ||
- `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, e.g. `'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. | ||
jsdom.env({ /* config */ }) | ||
- 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 | ||
@@ -134,44 +132,47 @@ | ||
var jsdom = require("jsdom").jsdom, | ||
doc = jsdom(markup, level, options), | ||
window = doc.createWindow(); | ||
```js | ||
var jsdom = require("jsdom").jsdom; | ||
var doc = jsdom(markup, level, options); | ||
var window = doc.createWindow(); | ||
``` | ||
- `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. | ||
- `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 (e.g. 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.level(1, 'core')) | ||
```js | ||
var jsdom = require("jsdom"); | ||
var doc = jsdom.jsdom("<html><body></body></html>", jsdom.level(1, "core")); | ||
``` | ||
- `options` see the **Flexibility** section below | ||
- `options` see the **Flexibility** section below. | ||
### Flexibility | ||
One of the goals of jsdom is to be as minimal and light as possible. This section details how | ||
someone can change the behavior of `Document`s on the fly. These features are baked into | ||
the `DOMImplementation` that every `Document` has, and may be tweaked in two ways: | ||
One of the goals of jsdom is to be as minimal and light as possible. This section details how someone can change the behavior of `Document`s on the fly. These features are baked into the `DOMImplementation` that every `Document` has, and may be tweaked in two ways: | ||
1. When you create a new `Document` using the jsdom builder (`require('jsdom').jsdom()`) | ||
1. When you create a new `Document` using the jsdom builder (`require("jsdom").jsdom()`) | ||
var jsdom = require('jsdom').jsdom, | ||
doc = jsdom("<html><body></body></html>", null, { | ||
features: { | ||
FetchExternalResources : ['img'] | ||
} | ||
}); | ||
```js | ||
var jsdom = require("jsdom").jsdom; | ||
var doc = jsdom("<html><body></body></html>", null, { | ||
features: { | ||
FetchExternalResources : ["img"] | ||
} | ||
}); | ||
``` | ||
Do note, that this will only affect the document that is currently being created. All other documents | ||
will use the defaults specified below (see: Default Features) | ||
Do note, that this will only affect the document that is currently being created. All other documents will use the defaults specified below (see: Default Features). | ||
2. Previous to creating any documents you can modify the defaults for all future documents | ||
2. Before creating any documents, you can modify the defaults for all future documents: | ||
require('jsdom').defaultDocumentFeatures = { | ||
FetchExternalResources : ['script'], | ||
ProcessExternalResources : false, | ||
MutationEvents : false, | ||
QuerySelector : false | ||
} | ||
```js | ||
require("jsdom").defaultDocumentFeatures = { | ||
FetchExternalResources: ["script"], | ||
ProcessExternalResources: false, | ||
MutationEvents: false, | ||
QuerySelector: false | ||
}; | ||
``` | ||
#### Default Features | ||
@@ -183,29 +184,37 @@ | ||
`FetchExternalResources` | ||
_Default_: ['script'] | ||
_Allowed_: ['script', 'img', 'css', 'frame', 'link'] or false | ||
Enables/Disables fetching files over the filesystem/http | ||
- _Default_: `["script"]` | ||
- _Allowed_: `["script", "img", "css", "frame", "iframe", "link"]` or `false` | ||
Enables/disables fetching files over the file system/HTTP. | ||
`ProcessExternalResources` | ||
_default_: ['script'] | ||
_allowed_: ['script'] or false | ||
Disabling this will disable script execution (currently only javascript). | ||
- _Default_: `["script"]` | ||
- _Allowed_: `["script"]` or `false` | ||
Disabling this will disable script execution (currently only JavaScript). | ||
`MutationEvents` | ||
_default_: '2.0' | ||
_allowed_ : '2.0' or false | ||
- _Default_: `"2.0"` | ||
- _Allowed_ : `"2.0"` or `false` | ||
Initially enabled to be up to spec. Disable this if you do not need mutation events and want jsdom to be a bit more efficient. | ||
**Note**: `ProcessExternalResources` requires this to be enabled | ||
**Note**: `ProcessExternalResources` requires this to be enabled. | ||
`QuerySelector` | ||
_default_ : false | ||
_allowed_ : true | ||
This feature is backed by [sizzle][] but currently causes problems with some libraries. Enable this if you want `document.querySelector` and friends, but be aware that many libraries feature detect for this, and it may cause you a bit of trouble. | ||
- _Default_ : `false` | ||
- _Allowed_ : `true` | ||
[sizzle]:http://sizzlejs.com/ | ||
This feature is backed by [sizzle][] but currently causes problems with some libraries. Enable this if you want `document.querySelector` and friends, but be aware that many libraries feature detect for this, and it may cause you a bit of trouble. | ||
[sizzle]: http://sizzlejs.com/ | ||
## Canvas | ||
jsdom includes support for using the [canvas](https://npmjs.org/package/canvas) package to extend any `<canvas>` elements with the canvas API. To make this work, you need to include canvas as a dependency in your project, as a peer of jsdom. If jsdom can find the canvas package, it will use it, but if it's not present, then `<canvas>` elements will behave like `<div>`s. | ||
# More Examples | ||
@@ -215,72 +224,103 @@ | ||
var jsdom = require("jsdom"), | ||
window = jsdom.createWindow(); | ||
```js | ||
var jsdom = require("jsdom"); | ||
var window = jsdom.createWindow(); | ||
console.log(window.document); | ||
// output: undefined | ||
console.log(window.document); // output: undefined | ||
``` | ||
## Creating a document | ||
var jsdom = require("jsdom"), | ||
doc = new (jsdom.level(1, 'core').Document)(); | ||
console.log(doc.nodeName); | ||
// outputs: #document | ||
```js | ||
var jsdom = require("jsdom"); | ||
var doc = new (jsdom.level(1, "core").Document)(); | ||
console.log(doc.nodeName); // outputs: #document | ||
``` | ||
## Creating a browser-like BOM/DOM/Window | ||
var jsdom = require("./lib/jsdom").jsdom, | ||
document = jsdom("<html><head></head><body>hello world</body></html>"), | ||
window = document.createWindow(); | ||
```js | ||
var jsdom = require("jsdom").jsdom; | ||
var document = jsdom("<html><head></head><body>hello world</body></html>"); | ||
var window = document.createWindow(); | ||
console.log(window.document.innerHTML); | ||
// output: '<html><head></head><body>hello world</body></html>' | ||
console.log(window.document.innerHTML); | ||
// output: "<html><head></head><body>hello world</body></html>" | ||
console.log(window.innerWidth) | ||
// output: 1024 | ||
console.log(window.innerWidth); | ||
// output: 1024 | ||
console.log(typeof window.document.getElementsByClassName); | ||
// outputs: function | ||
console.log(typeof window.document.getElementsByClassName); | ||
// outputs: function | ||
``` | ||
## jQueryify | ||
var jsdom = require("jsdom"), | ||
window = jsdom.jsdom().createWindow(); | ||
```js | ||
var jsdom = require("jsdom"); | ||
var window = jsdom.jsdom().createWindow(); | ||
jsdom.jQueryify(window, 'http://code.jquery.com/jquery-1.4.2.min.js' , function() { | ||
window.$('body').append('<div class="testing">Hello World, It works</div>'); | ||
console.log(window.$('.testing').text()); | ||
}); | ||
jsdom.jQueryify(window, "http://code.jquery.com/jquery.js", function () { | ||
window.$("body").append('<div class="testing">Hello World, It works</div>'); | ||
console.log(window.$(".testing").text()); | ||
}); | ||
``` | ||
## Passing objects to scripts inside the page | ||
```js | ||
var jsdom = require("jsdom").jsdom; | ||
var window = jsdom().createWindow(); | ||
window.__myObject = { foo: "bar" }; | ||
var scriptEl = window.document.createElement("script"); | ||
scriptEl.src = "anotherScript.js"; | ||
window.document.body.appendChild(scriptEl); | ||
// anotherScript.js will have the ability to read `window.__myObject`, even | ||
// though it originated in Node! | ||
``` | ||
# 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 | ||
``` | ||
level1/core 532/532 100% | ||
level1/html 238/238 100% | ||
level1/svg 527/527 100% | ||
level2/core 283/283 100% | ||
level2/html 694/694 100% | ||
level2/style 5/5 100% | ||
level2/extra 4/4 100% | ||
level2/events 24/24 100% | ||
level3/xpath 93/93 100% | ||
window/index 5/5 100% | ||
window/script 10/10 100% | ||
window/frame 14/14 100% | ||
sizzle/index 12/15 80% | ||
jsdom/index 79/79 100% | ||
jsonp/jsonp 1/1 100% | ||
browser/contextifyReplacement 4/4 100% | ||
browser/index 22/22 100% | ||
------------------------------------------------------ | ||
TOTALS: 3/2550 failed; 99% success | ||
``` | ||
## Running the tests | ||
First you'll want to `npm install -g nodeunit` then `npm install --dev` | ||
First you'll want to `npm install`. To run all the tests, use `npm test`, which just calls `node test/runner`. | ||
Using `test/runner` you can slice and dice which tests your want to run from different levels. Usage is as follows: | ||
Using `test/runner` directly, 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 | ||
``` | ||
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 | ||
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 | ||
``` |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
323
6
385873
6
25
11200
4
+ Addedcssstyle@0.2.x
+ Addedcssom@0.3.80.5.0(transitive)
+ Addedcssstyle@0.2.37(transitive)
- Removedcssom@0.2.5(transitive)
Updatedcssom@>=0.2.2