63 changed files with 29236 additions and 48 deletions
-
579domino/CSSStyleDeclaration.js
-
120domino/CharacterData.js
-
119domino/ChildNode.js
-
39domino/Comment.js
-
80domino/ContainerNode.js
-
12domino/CustomEvent.js
-
134domino/DOMException.js
-
94domino/DOMImplementation.js
-
172domino/DOMTokenList.js
-
884domino/Document.js
-
68domino/DocumentFragment.js
-
36domino/DocumentType.js
-
1202domino/Element.js
-
66domino/Event.js
-
298domino/EventTarget.js
-
92domino/FilteredElementList.js
-
7264domino/HTMLParser.js
-
37domino/Leaf.js
-
44domino/LinkedList.js
-
56domino/Location.js
-
52domino/MouseEvent.js
-
9domino/MutationConstants.js
-
41domino/NamedNodeMap.js
-
17domino/NavigatorID.js
-
738domino/Node.js
-
24domino/NodeFilter.js
-
217domino/NodeIterator.js
-
15domino/NodeList.es5.js
-
12domino/NodeList.es6.js
-
13domino/NodeList.js
-
87domino/NodeTraversal.js
-
168domino/NodeUtils.js
-
26domino/NonDocumentTypeChildNode.js
-
43domino/ProcessingInstruction.js
-
74domino/Text.js
-
336domino/TreeWalker.js
-
19domino/UIEvent.js
-
194domino/URL.js
-
270domino/URLUtils.js
-
62domino/Window.js
-
11domino/WindowTimers.js
-
152domino/attributes.js
-
7domino/config.js
-
6654domino/cssparser.js
-
70domino/defineElement.js
-
7domino/events.js
-
1426domino/htmlelts.js
-
27domino/impl.js
-
5domino/index.d.ts
-
79domino/index.js
-
2938domino/jquery.js
-
933domino/select.js
-
24domino/sloppy.js
-
57domino/svg.js
-
85domino/utils.js
-
91domino/xmlnames.js
-
1535govue/axios.js
-
4govue/govue.js
-
28govue/init.js
-
1183govue/promise.js
-
80jsruntime/runtime.go
-
61main.go
-
14static/use.js
@ -0,0 +1,579 @@ |
|||||
|
"use strict"; |
||||
|
var parserlib = require('./cssparser.js'); |
||||
|
|
||||
|
module.exports = CSSStyleDeclaration; |
||||
|
|
||||
|
function CSSStyleDeclaration(elt) { |
||||
|
this._element = elt; |
||||
|
} |
||||
|
|
||||
|
// Utility function for parsing style declarations
|
||||
|
// Pass in a string like "margin-left: 5px; border-style: solid"
|
||||
|
// and this function returns an object like
|
||||
|
// {"margin-left":"5px", "border-style":"solid"}
|
||||
|
function parseStyles(s) { |
||||
|
var parser = new parserlib.css.Parser(); |
||||
|
var result = { property: Object.create(null), priority: Object.create(null) }; |
||||
|
parser.addListener("property", function(e) { |
||||
|
if (e.invalid) return; // Skip errors
|
||||
|
result.property[e.property.text] = e.value.text; |
||||
|
if (e.important) result.priority[e.property.text] = 'important'; |
||||
|
}); |
||||
|
s = (''+s).replace(/^;/, ''); |
||||
|
parser.parseStyleAttribute(s); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
var NO_CHANGE = {}; // Private marker object
|
||||
|
|
||||
|
CSSStyleDeclaration.prototype = Object.create(Object.prototype, { |
||||
|
|
||||
|
// Return the parsed form of the element's style attribute.
|
||||
|
// If the element's style attribute has never been parsed
|
||||
|
// or if it has changed since the last parse, then reparse it
|
||||
|
// Note that the styles don't get parsed until they're actually needed
|
||||
|
_parsed: { get: function() { |
||||
|
if (!this._parsedStyles || this.cssText !== this._lastParsedText) { |
||||
|
var text = this.cssText; |
||||
|
this._parsedStyles = parseStyles(text); |
||||
|
this._lastParsedText = text; |
||||
|
delete this._names; |
||||
|
} |
||||
|
return this._parsedStyles; |
||||
|
}}, |
||||
|
|
||||
|
// Call this method any time the parsed representation of the
|
||||
|
// style changes. It converts the style properties to a string and
|
||||
|
// sets cssText and the element's style attribute
|
||||
|
_serialize: { value: function() { |
||||
|
var styles = this._parsed; |
||||
|
var s = ""; |
||||
|
|
||||
|
for(var name in styles.property) { |
||||
|
if (s) s += " "; |
||||
|
s += name + ": " + styles.property[name]; |
||||
|
if (styles.priority[name]) { |
||||
|
s += " !" + styles.priority[name]; |
||||
|
} |
||||
|
s += ";"; |
||||
|
} |
||||
|
|
||||
|
this.cssText = s; // also sets the style attribute
|
||||
|
this._lastParsedText = s; // so we don't reparse
|
||||
|
delete this._names; |
||||
|
}}, |
||||
|
|
||||
|
cssText: { |
||||
|
get: function() { |
||||
|
// XXX: this is a CSSStyleDeclaration for an element.
|
||||
|
// A different impl might be necessary for a set of styles
|
||||
|
// associated returned by getComputedStyle(), e.g.
|
||||
|
return this._element.getAttribute("style"); |
||||
|
}, |
||||
|
set: function(value) { |
||||
|
// XXX: I should parse and serialize the value to
|
||||
|
// normalize it and remove errors. FF and chrome do that.
|
||||
|
this._element.setAttribute("style", value); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
length: { get: function() { |
||||
|
if (!this._names) |
||||
|
this._names = Object.getOwnPropertyNames(this._parsed.property); |
||||
|
return this._names.length; |
||||
|
}}, |
||||
|
|
||||
|
item: { value: function(n) { |
||||
|
if (!this._names) |
||||
|
this._names = Object.getOwnPropertyNames(this._parsed.property); |
||||
|
return this._names[n]; |
||||
|
}}, |
||||
|
|
||||
|
getPropertyValue: { value: function(property) { |
||||
|
property = property.toLowerCase(); |
||||
|
return this._parsed.property[property] || ""; |
||||
|
}}, |
||||
|
|
||||
|
getPropertyPriority: { value: function(property) { |
||||
|
property = property.toLowerCase(); |
||||
|
return this._parsed.priority[property] || ""; |
||||
|
}}, |
||||
|
|
||||
|
setProperty: { value: function(property, value, priority) { |
||||
|
property = property.toLowerCase(); |
||||
|
if (value === null || value === undefined) { |
||||
|
value = ""; |
||||
|
} |
||||
|
if (priority === null || priority === undefined) { |
||||
|
priority = ""; |
||||
|
} |
||||
|
|
||||
|
// String coercion
|
||||
|
if (value !== NO_CHANGE) { |
||||
|
value = "" + value; |
||||
|
} |
||||
|
|
||||
|
if (value === "") { |
||||
|
this.removeProperty(property); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (priority !== "" && priority !== NO_CHANGE && |
||||
|
!/^important$/i.test(priority)) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var styles = this._parsed; |
||||
|
if (value === NO_CHANGE) { |
||||
|
if (!styles.property[property]) { |
||||
|
return; // Not a valid property name.
|
||||
|
} |
||||
|
if (priority !== "") { |
||||
|
styles.priority[property] = "important"; |
||||
|
} else { |
||||
|
delete styles.priority[property]; |
||||
|
} |
||||
|
} else { |
||||
|
// We don't just accept the property value. Instead
|
||||
|
// we parse it to ensure that it is something valid.
|
||||
|
// If it contains a semicolon it is invalid
|
||||
|
if (value.indexOf(";") !== -1) return; |
||||
|
|
||||
|
var newprops = parseStyles(property + ":" + value); |
||||
|
if (Object.getOwnPropertyNames(newprops.property).length === 0) { |
||||
|
return; // no valid property found
|
||||
|
} |
||||
|
if (Object.getOwnPropertyNames(newprops.priority).length !== 0) { |
||||
|
return; // if the value included '!important' it wasn't valid.
|
||||
|
} |
||||
|
|
||||
|
// XXX handle shorthand properties
|
||||
|
|
||||
|
for (var p in newprops.property) { |
||||
|
styles.property[p] = newprops.property[p]; |
||||
|
if (priority === NO_CHANGE) { |
||||
|
continue; |
||||
|
} else if (priority !== "") { |
||||
|
styles.priority[p] = "important"; |
||||
|
} else if (styles.priority[p]) { |
||||
|
delete styles.priority[p]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Serialize and update cssText and element.style!
|
||||
|
this._serialize(); |
||||
|
}}, |
||||
|
|
||||
|
setPropertyValue: { value: function(property, value) { |
||||
|
return this.setProperty(property, value, NO_CHANGE); |
||||
|
}}, |
||||
|
|
||||
|
setPropertyPriority: { value: function(property, priority) { |
||||
|
return this.setProperty(property, NO_CHANGE, priority); |
||||
|
}}, |
||||
|
|
||||
|
removeProperty: { value: function(property) { |
||||
|
property = property.toLowerCase(); |
||||
|
var styles = this._parsed; |
||||
|
if (property in styles.property) { |
||||
|
delete styles.property[property]; |
||||
|
delete styles.priority[property]; |
||||
|
|
||||
|
// Serialize and update cssText and element.style!
|
||||
|
this._serialize(); |
||||
|
} |
||||
|
}}, |
||||
|
}); |
||||
|
|
||||
|
var cssProperties = { |
||||
|
alignContent: "align-content", |
||||
|
alignItems: "align-items", |
||||
|
alignmentBaseline: "alignment-baseline", |
||||
|
alignSelf: "align-self", |
||||
|
animation: "animation", |
||||
|
animationDelay: "animation-delay", |
||||
|
animationDirection: "animation-direction", |
||||
|
animationDuration: "animation-duration", |
||||
|
animationFillMode: "animation-fill-mode", |
||||
|
animationIterationCount: "animation-iteration-count", |
||||
|
animationName: "animation-name", |
||||
|
animationPlayState: "animation-play-state", |
||||
|
animationTimingFunction: "animation-timing-function", |
||||
|
backfaceVisibility: "backface-visibility", |
||||
|
background: "background", |
||||
|
backgroundAttachment: "background-attachment", |
||||
|
backgroundClip: "background-clip", |
||||
|
backgroundColor: "background-color", |
||||
|
backgroundImage: "background-image", |
||||
|
backgroundOrigin: "background-origin", |
||||
|
backgroundPosition: "background-position", |
||||
|
backgroundPositionX: "background-position-x", |
||||
|
backgroundPositionY: "background-position-y", |
||||
|
backgroundRepeat: "background-repeat", |
||||
|
backgroundSize: "background-size", |
||||
|
baselineShift: "baseline-shift", |
||||
|
border: "border", |
||||
|
borderBottom: "border-bottom", |
||||
|
borderBottomColor: "border-bottom-color", |
||||
|
borderBottomLeftRadius: "border-bottom-left-radius", |
||||
|
borderBottomRightRadius: "border-bottom-right-radius", |
||||
|
borderBottomStyle: "border-bottom-style", |
||||
|
borderBottomWidth: "border-bottom-width", |
||||
|
borderCollapse: "border-collapse", |
||||
|
borderColor: "border-color", |
||||
|
borderImage: "border-image", |
||||
|
borderImageOutset: "border-image-outset", |
||||
|
borderImageRepeat: "border-image-repeat", |
||||
|
borderImageSlice: "border-image-slice", |
||||
|
borderImageSource: "border-image-source", |
||||
|
borderImageWidth: "border-image-width", |
||||
|
borderLeft: "border-left", |
||||
|
borderLeftColor: "border-left-color", |
||||
|
borderLeftStyle: "border-left-style", |
||||
|
borderLeftWidth: "border-left-width", |
||||
|
borderRadius: "border-radius", |
||||
|
borderRight: "border-right", |
||||
|
borderRightColor: "border-right-color", |
||||
|
borderRightStyle: "border-right-style", |
||||
|
borderRightWidth: "border-right-width", |
||||
|
borderSpacing: "border-spacing", |
||||
|
borderStyle: "border-style", |
||||
|
borderTop: "border-top", |
||||
|
borderTopColor: "border-top-color", |
||||
|
borderTopLeftRadius: "border-top-left-radius", |
||||
|
borderTopRightRadius: "border-top-right-radius", |
||||
|
borderTopStyle: "border-top-style", |
||||
|
borderTopWidth: "border-top-width", |
||||
|
borderWidth: "border-width", |
||||
|
bottom: "bottom", |
||||
|
boxShadow: "box-shadow", |
||||
|
boxSizing: "box-sizing", |
||||
|
breakAfter: "break-after", |
||||
|
breakBefore: "break-before", |
||||
|
breakInside: "break-inside", |
||||
|
captionSide: "caption-side", |
||||
|
clear: "clear", |
||||
|
clip: "clip", |
||||
|
clipPath: "clip-path", |
||||
|
clipRule: "clip-rule", |
||||
|
color: "color", |
||||
|
colorInterpolationFilters: "color-interpolation-filters", |
||||
|
columnCount: "column-count", |
||||
|
columnFill: "column-fill", |
||||
|
columnGap: "column-gap", |
||||
|
columnRule: "column-rule", |
||||
|
columnRuleColor: "column-rule-color", |
||||
|
columnRuleStyle: "column-rule-style", |
||||
|
columnRuleWidth: "column-rule-width", |
||||
|
columns: "columns", |
||||
|
columnSpan: "column-span", |
||||
|
columnWidth: "column-width", |
||||
|
content: "content", |
||||
|
counterIncrement: "counter-increment", |
||||
|
counterReset: "counter-reset", |
||||
|
cssFloat: "float", |
||||
|
cursor: "cursor", |
||||
|
direction: "direction", |
||||
|
display: "display", |
||||
|
dominantBaseline: "dominant-baseline", |
||||
|
emptyCells: "empty-cells", |
||||
|
enableBackground: "enable-background", |
||||
|
fill: "fill", |
||||
|
fillOpacity: "fill-opacity", |
||||
|
fillRule: "fill-rule", |
||||
|
filter: "filter", |
||||
|
flex: "flex", |
||||
|
flexBasis: "flex-basis", |
||||
|
flexDirection: "flex-direction", |
||||
|
flexFlow: "flex-flow", |
||||
|
flexGrow: "flex-grow", |
||||
|
flexShrink: "flex-shrink", |
||||
|
flexWrap: "flex-wrap", |
||||
|
floodColor: "flood-color", |
||||
|
floodOpacity: "flood-opacity", |
||||
|
font: "font", |
||||
|
fontFamily: "font-family", |
||||
|
fontFeatureSettings: "font-feature-settings", |
||||
|
fontSize: "font-size", |
||||
|
fontSizeAdjust: "font-size-adjust", |
||||
|
fontStretch: "font-stretch", |
||||
|
fontStyle: "font-style", |
||||
|
fontVariant: "font-variant", |
||||
|
fontWeight: "font-weight", |
||||
|
glyphOrientationHorizontal: "glyph-orientation-horizontal", |
||||
|
glyphOrientationVertical: "glyph-orientation-vertical", |
||||
|
grid: "grid", |
||||
|
gridArea: "grid-area", |
||||
|
gridAutoColumns: "grid-auto-columns", |
||||
|
gridAutoFlow: "grid-auto-flow", |
||||
|
gridAutoRows: "grid-auto-rows", |
||||
|
gridColumn: "grid-column", |
||||
|
gridColumnEnd: "grid-column-end", |
||||
|
gridColumnGap: "grid-column-gap", |
||||
|
gridColumnStart: "grid-column-start", |
||||
|
gridGap: "grid-gap", |
||||
|
gridRow: "grid-row", |
||||
|
gridRowEnd: "grid-row-end", |
||||
|
gridRowGap: "grid-row-gap", |
||||
|
gridRowStart: "grid-row-start", |
||||
|
gridTemplate: "grid-template", |
||||
|
gridTemplateAreas: "grid-template-areas", |
||||
|
gridTemplateColumns: "grid-template-columns", |
||||
|
gridTemplateRows: "grid-template-rows", |
||||
|
height: "height", |
||||
|
imeMode: "ime-mode", |
||||
|
justifyContent: "justify-content", |
||||
|
kerning: "kerning", |
||||
|
layoutGrid: "layout-grid", |
||||
|
layoutGridChar: "layout-grid-char", |
||||
|
layoutGridLine: "layout-grid-line", |
||||
|
layoutGridMode: "layout-grid-mode", |
||||
|
layoutGridType: "layout-grid-type", |
||||
|
left: "left", |
||||
|
letterSpacing: "letter-spacing", |
||||
|
lightingColor: "lighting-color", |
||||
|
lineBreak: "line-break", |
||||
|
lineHeight: "line-height", |
||||
|
listStyle: "list-style", |
||||
|
listStyleImage: "list-style-image", |
||||
|
listStylePosition: "list-style-position", |
||||
|
listStyleType: "list-style-type", |
||||
|
margin: "margin", |
||||
|
marginBottom: "margin-bottom", |
||||
|
marginLeft: "margin-left", |
||||
|
marginRight: "margin-right", |
||||
|
marginTop: "margin-top", |
||||
|
marker: "marker", |
||||
|
markerEnd: "marker-end", |
||||
|
markerMid: "marker-mid", |
||||
|
markerStart: "marker-start", |
||||
|
mask: "mask", |
||||
|
maxHeight: "max-height", |
||||
|
maxWidth: "max-width", |
||||
|
minHeight: "min-height", |
||||
|
minWidth: "min-width", |
||||
|
msContentZoomChaining: "-ms-content-zoom-chaining", |
||||
|
msContentZooming: "-ms-content-zooming", |
||||
|
msContentZoomLimit: "-ms-content-zoom-limit", |
||||
|
msContentZoomLimitMax: "-ms-content-zoom-limit-max", |
||||
|
msContentZoomLimitMin: "-ms-content-zoom-limit-min", |
||||
|
msContentZoomSnap: "-ms-content-zoom-snap", |
||||
|
msContentZoomSnapPoints: "-ms-content-zoom-snap-points", |
||||
|
msContentZoomSnapType: "-ms-content-zoom-snap-type", |
||||
|
msFlowFrom: "-ms-flow-from", |
||||
|
msFlowInto: "-ms-flow-into", |
||||
|
msFontFeatureSettings: "-ms-font-feature-settings", |
||||
|
msGridColumn: "-ms-grid-column", |
||||
|
msGridColumnAlign: "-ms-grid-column-align", |
||||
|
msGridColumns: "-ms-grid-columns", |
||||
|
msGridColumnSpan: "-ms-grid-column-span", |
||||
|
msGridRow: "-ms-grid-row", |
||||
|
msGridRowAlign: "-ms-grid-row-align", |
||||
|
msGridRows: "-ms-grid-rows", |
||||
|
msGridRowSpan: "-ms-grid-row-span", |
||||
|
msHighContrastAdjust: "-ms-high-contrast-adjust", |
||||
|
msHyphenateLimitChars: "-ms-hyphenate-limit-chars", |
||||
|
msHyphenateLimitLines: "-ms-hyphenate-limit-lines", |
||||
|
msHyphenateLimitZone: "-ms-hyphenate-limit-zone", |
||||
|
msHyphens: "-ms-hyphens", |
||||
|
msImeAlign: "-ms-ime-align", |
||||
|
msOverflowStyle: "-ms-overflow-style", |
||||
|
msScrollChaining: "-ms-scroll-chaining", |
||||
|
msScrollLimit: "-ms-scroll-limit", |
||||
|
msScrollLimitXMax: "-ms-scroll-limit-x-max", |
||||
|
msScrollLimitXMin: "-ms-scroll-limit-x-min", |
||||
|
msScrollLimitYMax: "-ms-scroll-limit-y-max", |
||||
|
msScrollLimitYMin: "-ms-scroll-limit-y-min", |
||||
|
msScrollRails: "-ms-scroll-rails", |
||||
|
msScrollSnapPointsX: "-ms-scroll-snap-points-x", |
||||
|
msScrollSnapPointsY: "-ms-scroll-snap-points-y", |
||||
|
msScrollSnapType: "-ms-scroll-snap-type", |
||||
|
msScrollSnapX: "-ms-scroll-snap-x", |
||||
|
msScrollSnapY: "-ms-scroll-snap-y", |
||||
|
msScrollTranslation: "-ms-scroll-translation", |
||||
|
msTextCombineHorizontal: "-ms-text-combine-horizontal", |
||||
|
msTextSizeAdjust: "-ms-text-size-adjust", |
||||
|
msTouchAction: "-ms-touch-action", |
||||
|
msTouchSelect: "-ms-touch-select", |
||||
|
msUserSelect: "-ms-user-select", |
||||
|
msWrapFlow: "-ms-wrap-flow", |
||||
|
msWrapMargin: "-ms-wrap-margin", |
||||
|
msWrapThrough: "-ms-wrap-through", |
||||
|
opacity: "opacity", |
||||
|
order: "order", |
||||
|
orphans: "orphans", |
||||
|
outline: "outline", |
||||
|
outlineColor: "outline-color", |
||||
|
outlineOffset: "outline-offset", |
||||
|
outlineStyle: "outline-style", |
||||
|
outlineWidth: "outline-width", |
||||
|
overflow: "overflow", |
||||
|
overflowX: "overflow-x", |
||||
|
overflowY: "overflow-y", |
||||
|
padding: "padding", |
||||
|
paddingBottom: "padding-bottom", |
||||
|
paddingLeft: "padding-left", |
||||
|
paddingRight: "padding-right", |
||||
|
paddingTop: "padding-top", |
||||
|
page: "page", |
||||
|
pageBreakAfter: "page-break-after", |
||||
|
pageBreakBefore: "page-break-before", |
||||
|
pageBreakInside: "page-break-inside", |
||||
|
perspective: "perspective", |
||||
|
perspectiveOrigin: "perspective-origin", |
||||
|
pointerEvents: "pointer-events", |
||||
|
position: "position", |
||||
|
quotes: "quotes", |
||||
|
right: "right", |
||||
|
rotate: "rotate", |
||||
|
rubyAlign: "ruby-align", |
||||
|
rubyOverhang: "ruby-overhang", |
||||
|
rubyPosition: "ruby-position", |
||||
|
scale: "scale", |
||||
|
size: "size", |
||||
|
stopColor: "stop-color", |
||||
|
stopOpacity: "stop-opacity", |
||||
|
stroke: "stroke", |
||||
|
strokeDasharray: "stroke-dasharray", |
||||
|
strokeDashoffset: "stroke-dashoffset", |
||||
|
strokeLinecap: "stroke-linecap", |
||||
|
strokeLinejoin: "stroke-linejoin", |
||||
|
strokeMiterlimit: "stroke-miterlimit", |
||||
|
strokeOpacity: "stroke-opacity", |
||||
|
strokeWidth: "stroke-width", |
||||
|
tableLayout: "table-layout", |
||||
|
textAlign: "text-align", |
||||
|
textAlignLast: "text-align-last", |
||||
|
textAnchor: "text-anchor", |
||||
|
textDecoration: "text-decoration", |
||||
|
textIndent: "text-indent", |
||||
|
textJustify: "text-justify", |
||||
|
textKashida: "text-kashida", |
||||
|
textKashidaSpace: "text-kashida-space", |
||||
|
textOverflow: "text-overflow", |
||||
|
textShadow: "text-shadow", |
||||
|
textTransform: "text-transform", |
||||
|
textUnderlinePosition: "text-underline-position", |
||||
|
top: "top", |
||||
|
touchAction: "touch-action", |
||||
|
transform: "transform", |
||||
|
transformOrigin: "transform-origin", |
||||
|
transformStyle: "transform-style", |
||||
|
transition: "transition", |
||||
|
transitionDelay: "transition-delay", |
||||
|
transitionDuration: "transition-duration", |
||||
|
transitionProperty: "transition-property", |
||||
|
transitionTimingFunction: "transition-timing-function", |
||||
|
translate: "translate", |
||||
|
unicodeBidi: "unicode-bidi", |
||||
|
verticalAlign: "vertical-align", |
||||
|
visibility: "visibility", |
||||
|
webkitAlignContent: "-webkit-align-content", |
||||
|
webkitAlignItems: "-webkit-align-items", |
||||
|
webkitAlignSelf: "-webkit-align-self", |
||||
|
webkitAnimation: "-webkit-animation", |
||||
|
webkitAnimationDelay: "-webkit-animation-delay", |
||||
|
webkitAnimationDirection: "-webkit-animation-direction", |
||||
|
webkitAnimationDuration: "-webkit-animation-duration", |
||||
|
webkitAnimationFillMode: "-webkit-animation-fill-mode", |
||||
|
webkitAnimationIterationCount: "-webkit-animation-iteration-count", |
||||
|
webkitAnimationName: "-webkit-animation-name", |
||||
|
webkitAnimationPlayState: "-webkit-animation-play-state", |
||||
|
webkitAnimationTimingFunction: "-webkit-animation-timing-funciton", |
||||
|
webkitAppearance: "-webkit-appearance", |
||||
|
webkitBackfaceVisibility: "-webkit-backface-visibility", |
||||
|
webkitBackgroundClip: "-webkit-background-clip", |
||||
|
webkitBackgroundOrigin: "-webkit-background-origin", |
||||
|
webkitBackgroundSize: "-webkit-background-size", |
||||
|
webkitBorderBottomLeftRadius: "-webkit-border-bottom-left-radius", |
||||
|
webkitBorderBottomRightRadius: "-webkit-border-bottom-right-radius", |
||||
|
webkitBorderImage: "-webkit-border-image", |
||||
|
webkitBorderRadius: "-webkit-border-radius", |
||||
|
webkitBorderTopLeftRadius: "-webkit-border-top-left-radius", |
||||
|
webkitBorderTopRightRadius: "-webkit-border-top-right-radius", |
||||
|
webkitBoxAlign: "-webkit-box-align", |
||||
|
webkitBoxDirection: "-webkit-box-direction", |
||||
|
webkitBoxFlex: "-webkit-box-flex", |
||||
|
webkitBoxOrdinalGroup: "-webkit-box-ordinal-group", |
||||
|
webkitBoxOrient: "-webkit-box-orient", |
||||
|
webkitBoxPack: "-webkit-box-pack", |
||||
|
webkitBoxSizing: "-webkit-box-sizing", |
||||
|
webkitColumnBreakAfter: "-webkit-column-break-after", |
||||
|
webkitColumnBreakBefore: "-webkit-column-break-before", |
||||
|
webkitColumnBreakInside: "-webkit-column-break-inside", |
||||
|
webkitColumnCount: "-webkit-column-count", |
||||
|
webkitColumnGap: "-webkit-column-gap", |
||||
|
webkitColumnRule: "-webkit-column-rule", |
||||
|
webkitColumnRuleColor: "-webkit-column-rule-color", |
||||
|
webkitColumnRuleStyle: "-webkit-column-rule-style", |
||||
|
webkitColumnRuleWidth: "-webkit-column-rule-width", |
||||
|
webkitColumns: "-webkit-columns", |
||||
|
webkitColumnSpan: "-webkit-column-span", |
||||
|
webkitColumnWidth: "-webkit-column-width", |
||||
|
webkitFilter: "-webkit-filter", |
||||
|
webkitFlex: "-webkit-flex", |
||||
|
webkitFlexBasis: "-webkit-flex-basis", |
||||
|
webkitFlexDirection: "-webkit-flex-direction", |
||||
|
webkitFlexFlow: "-webkit-flex-flow", |
||||
|
webkitFlexGrow: "-webkit-flex-grow", |
||||
|
webkitFlexShrink: "-webkit-flex-shrink", |
||||
|
webkitFlexWrap: "-webkit-flex-wrap", |
||||
|
webkitJustifyContent: "-webkit-justify-content", |
||||
|
webkitOrder: "-webkit-order", |
||||
|
webkitPerspective: "-webkit-perspective-origin", |
||||
|
webkitPerspectiveOrigin: "-webkit-perspective-origin", |
||||
|
webkitTapHighlightColor: "-webkit-tap-highlight-color", |
||||
|
webkitTextFillColor: "-webkit-text-fill-color", |
||||
|
webkitTextSizeAdjust: "-webkit-text-size-adjust", |
||||
|
webkitTextStroke: "-webkit-text-stroke", |
||||
|
webkitTextStrokeColor: "-webkit-text-stroke-color", |
||||
|
webkitTextStrokeWidth: "-webkit-text-stroke-width", |
||||
|
webkitTransform: "-webkit-transform", |
||||
|
webkitTransformOrigin: "-webkit-transform-origin", |
||||
|
webkitTransformStyle: "-webkit-transform-style", |
||||
|
webkitTransition: "-webkit-transition", |
||||
|
webkitTransitionDelay: "-webkit-transition-delay", |
||||
|
webkitTransitionDuration: "-webkit-transition-duration", |
||||
|
webkitTransitionProperty: "-webkit-transition-property", |
||||
|
webkitTransitionTimingFunction: "-webkit-transition-timing-function", |
||||
|
webkitUserModify: "-webkit-user-modify", |
||||
|
webkitUserSelect: "-webkit-user-select", |
||||
|
webkitWritingMode: "-webkit-writing-mode", |
||||
|
whiteSpace: "white-space", |
||||
|
widows: "widows", |
||||
|
width: "width", |
||||
|
wordBreak: "word-break", |
||||
|
wordSpacing: "word-spacing", |
||||
|
wordWrap: "word-wrap", |
||||
|
writingMode: "writing-mode", |
||||
|
zIndex: "z-index", |
||||
|
zoom: "zoom", |
||||
|
resize: "resize", |
||||
|
userSelect: "user-select", |
||||
|
}; |
||||
|
|
||||
|
for(var prop in cssProperties) defineStyleProperty(prop); |
||||
|
|
||||
|
function defineStyleProperty(jsname) { |
||||
|
var cssname = cssProperties[jsname]; |
||||
|
Object.defineProperty(CSSStyleDeclaration.prototype, jsname, { |
||||
|
get: function() { |
||||
|
return this.getPropertyValue(cssname); |
||||
|
}, |
||||
|
set: function(value) { |
||||
|
this.setProperty(cssname, value); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (!CSSStyleDeclaration.prototype.hasOwnProperty(cssname)) { |
||||
|
Object.defineProperty(CSSStyleDeclaration.prototype, cssname, { |
||||
|
get: function() { |
||||
|
return this.getPropertyValue(cssname); |
||||
|
}, |
||||
|
set: function(value) { |
||||
|
this.setProperty(cssname, value); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,120 @@ |
|||||
|
/* jshint bitwise: false */ |
||||
|
"use strict"; |
||||
|
module.exports = CharacterData; |
||||
|
|
||||
|
var Leaf = require('./Leaf.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
var ChildNode = require('./ChildNode.js'); |
||||
|
var NonDocumentTypeChildNode = require('./NonDocumentTypeChildNode.js'); |
||||
|
|
||||
|
function CharacterData() { |
||||
|
Leaf.call(this); |
||||
|
} |
||||
|
|
||||
|
CharacterData.prototype = Object.create(Leaf.prototype, { |
||||
|
// DOMString substringData(unsigned long offset,
|
||||
|
// unsigned long count);
|
||||
|
// The substringData(offset, count) method must run these steps:
|
||||
|
//
|
||||
|
// If offset is greater than the context object's
|
||||
|
// length, throw an INDEX_SIZE_ERR exception and
|
||||
|
// terminate these steps.
|
||||
|
//
|
||||
|
// If offset+count is greater than the context
|
||||
|
// object's length, return a DOMString whose value is
|
||||
|
// the UTF-16 code units from the offsetth UTF-16 code
|
||||
|
// unit to the end of data.
|
||||
|
//
|
||||
|
// Return a DOMString whose value is the UTF-16 code
|
||||
|
// units from the offsetth UTF-16 code unit to the
|
||||
|
// offset+countth UTF-16 code unit in data.
|
||||
|
substringData: { value: function substringData(offset, count) { |
||||
|
if (arguments.length < 2) { throw new TypeError("Not enough arguments"); } |
||||
|
// Convert arguments to WebIDL "unsigned long"
|
||||
|
offset = offset >>> 0; |
||||
|
count = count >>> 0; |
||||
|
if (offset > this.data.length || offset < 0 || count < 0) { |
||||
|
utils.IndexSizeError(); |
||||
|
} |
||||
|
return this.data.substring(offset, offset+count); |
||||
|
}}, |
||||
|
|
||||
|
// void appendData(DOMString data);
|
||||
|
// The appendData(data) method must append data to the context
|
||||
|
// object's data.
|
||||
|
appendData: { value: function appendData(data) { |
||||
|
if (arguments.length < 1) { throw new TypeError("Not enough arguments"); } |
||||
|
this.data += String(data); |
||||
|
}}, |
||||
|
|
||||
|
// void insertData(unsigned long offset, DOMString data);
|
||||
|
// The insertData(offset, data) method must run these steps:
|
||||
|
//
|
||||
|
// If offset is greater than the context object's
|
||||
|
// length, throw an INDEX_SIZE_ERR exception and
|
||||
|
// terminate these steps.
|
||||
|
//
|
||||
|
// Insert data into the context object's data after
|
||||
|
// offset UTF-16 code units.
|
||||
|
//
|
||||
|
insertData: { value: function insertData(offset, data) { |
||||
|
return this.replaceData(offset, 0, data); |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// void deleteData(unsigned long offset, unsigned long count);
|
||||
|
// The deleteData(offset, count) method must run these steps:
|
||||
|
//
|
||||
|
// If offset is greater than the context object's
|
||||
|
// length, throw an INDEX_SIZE_ERR exception and
|
||||
|
// terminate these steps.
|
||||
|
//
|
||||
|
// If offset+count is greater than the context
|
||||
|
// object's length var count be length-offset.
|
||||
|
//
|
||||
|
// Starting from offset UTF-16 code units remove count
|
||||
|
// UTF-16 code units from the context object's data.
|
||||
|
deleteData: { value: function deleteData(offset, count) { |
||||
|
return this.replaceData(offset, count, ''); |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// void replaceData(unsigned long offset, unsigned long count,
|
||||
|
// DOMString data);
|
||||
|
//
|
||||
|
// The replaceData(offset, count, data) method must act as
|
||||
|
// if the deleteData() method is invoked with offset and
|
||||
|
// count as arguments followed by the insertData() method
|
||||
|
// with offset and data as arguments and re-throw any
|
||||
|
// exceptions these methods might have thrown.
|
||||
|
replaceData: { value: function replaceData(offset, count, data) { |
||||
|
var curtext = this.data, len = curtext.length; |
||||
|
// Convert arguments to correct WebIDL type
|
||||
|
offset = offset >>> 0; |
||||
|
count = count >>> 0; |
||||
|
data = String(data); |
||||
|
|
||||
|
if (offset > len || offset < 0) utils.IndexSizeError(); |
||||
|
|
||||
|
if (offset+count > len) |
||||
|
count = len - offset; |
||||
|
|
||||
|
var prefix = curtext.substring(0, offset), |
||||
|
suffix = curtext.substring(offset+count); |
||||
|
|
||||
|
this.data = prefix + data + suffix; |
||||
|
}}, |
||||
|
|
||||
|
// Utility method that Node.isEqualNode() calls to test Text and
|
||||
|
// Comment nodes for equality. It is okay to put it here, since
|
||||
|
// Node will have already verified that nodeType is equal
|
||||
|
isEqual: { value: function isEqual(n) { |
||||
|
return this._data === n._data; |
||||
|
}}, |
||||
|
|
||||
|
length: { get: function() { return this.data.length; }} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
Object.defineProperties(CharacterData.prototype, ChildNode); |
||||
|
Object.defineProperties(CharacterData.prototype, NonDocumentTypeChildNode); |
@ -0,0 +1,119 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var LinkedList = require('./LinkedList.js'); |
||||
|
|
||||
|
var createDocumentFragmentFromArguments = function(document, args) { |
||||
|
var docFrag = document.createDocumentFragment(); |
||||
|
|
||||
|
for (var i=0; i<args.length; i++) { |
||||
|
var argItem = args[i]; |
||||
|
var isNode = argItem instanceof Node; |
||||
|
docFrag.appendChild(isNode ? argItem : |
||||
|
document.createTextNode(String(argItem))); |
||||
|
} |
||||
|
|
||||
|
return docFrag; |
||||
|
}; |
||||
|
|
||||
|
// The ChildNode interface contains methods that are particular to `Node`
|
||||
|
// objects that can have a parent. It is implemented by `Element`,
|
||||
|
// `DocumentType`, and `CharacterData` objects.
|
||||
|
var ChildNode = { |
||||
|
|
||||
|
// Inserts a set of Node or String objects in the children list of this
|
||||
|
// ChildNode's parent, just after this ChildNode. String objects are
|
||||
|
// inserted as the equivalent Text nodes.
|
||||
|
after: { value: function after() { |
||||
|
var argArr = Array.prototype.slice.call(arguments); |
||||
|
var parentNode = this.parentNode, nextSibling = this.nextSibling; |
||||
|
if (parentNode === null) { return; } |
||||
|
// Find "viable next sibling"; that is, next one not in argArr
|
||||
|
while (nextSibling && argArr.some(function(v) { return v===nextSibling; })) |
||||
|
nextSibling = nextSibling.nextSibling; |
||||
|
// ok, parent and sibling are saved away since this node could itself
|
||||
|
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
|
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr); |
||||
|
|
||||
|
parentNode.insertBefore(docFrag, nextSibling); |
||||
|
}}, |
||||
|
|
||||
|
// Inserts a set of Node or String objects in the children list of this
|
||||
|
// ChildNode's parent, just before this ChildNode. String objects are
|
||||
|
// inserted as the equivalent Text nodes.
|
||||
|
before: { value: function before() { |
||||
|
var argArr = Array.prototype.slice.call(arguments); |
||||
|
var parentNode = this.parentNode, prevSibling = this.previousSibling; |
||||
|
if (parentNode === null) { return; } |
||||
|
// Find "viable prev sibling"; that is, prev one not in argArr
|
||||
|
while (prevSibling && argArr.some(function(v) { return v===prevSibling; })) |
||||
|
prevSibling = prevSibling.previousSibling; |
||||
|
// ok, parent and sibling are saved away since this node could itself
|
||||
|
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
|
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr); |
||||
|
|
||||
|
var nextSibling = |
||||
|
prevSibling ? prevSibling.nextSibling : parentNode.firstChild; |
||||
|
parentNode.insertBefore(docFrag, nextSibling); |
||||
|
}}, |
||||
|
|
||||
|
// Remove this node from its parent
|
||||
|
remove: { value: function remove() { |
||||
|
if (this.parentNode === null) return; |
||||
|
|
||||
|
// Send mutation events if necessary
|
||||
|
if (this.doc) { |
||||
|
this.doc._preremoveNodeIterators(this); |
||||
|
if (this.rooted) { |
||||
|
this.doc.mutateRemove(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Remove this node from its parents array of children
|
||||
|
// and update the structure id for all ancestors
|
||||
|
this._remove(); |
||||
|
|
||||
|
// Forget this node's parent
|
||||
|
this.parentNode = null; |
||||
|
}}, |
||||
|
|
||||
|
// Remove this node w/o uprooting or sending mutation events
|
||||
|
// (But do update the structure id for all ancestors)
|
||||
|
_remove: { value: function _remove() { |
||||
|
var parent = this.parentNode; |
||||
|
if (parent === null) return; |
||||
|
if (parent._childNodes) { |
||||
|
parent._childNodes.splice(this.index, 1); |
||||
|
} else if (parent._firstChild === this) { |
||||
|
if (this._nextSibling === this) { |
||||
|
parent._firstChild = null; |
||||
|
} else { |
||||
|
parent._firstChild = this._nextSibling; |
||||
|
} |
||||
|
} |
||||
|
LinkedList.remove(this); |
||||
|
parent.modify(); |
||||
|
}}, |
||||
|
|
||||
|
// Replace this node with the nodes or strings provided as arguments.
|
||||
|
replaceWith: { value: function replaceWith() { |
||||
|
var argArr = Array.prototype.slice.call(arguments); |
||||
|
var parentNode = this.parentNode, nextSibling = this.nextSibling; |
||||
|
if (parentNode === null) { return; } |
||||
|
// Find "viable next sibling"; that is, next one not in argArr
|
||||
|
while (nextSibling && argArr.some(function(v) { return v===nextSibling; })) |
||||
|
nextSibling = nextSibling.nextSibling; |
||||
|
// ok, parent and sibling are saved away since this node could itself
|
||||
|
// appear in argArr and we're about to move argArr to a document fragment.
|
||||
|
var docFrag = createDocumentFragmentFromArguments(this.doc, argArr); |
||||
|
if (this.parentNode === parentNode) { |
||||
|
parentNode.replaceChild(docFrag, this); |
||||
|
} else { |
||||
|
// `this` was inserted into docFrag
|
||||
|
parentNode.insertBefore(docFrag, nextSibling); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
module.exports = ChildNode; |
@ -0,0 +1,39 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Comment; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var CharacterData = require('./CharacterData.js'); |
||||
|
|
||||
|
function Comment(doc, data) { |
||||
|
CharacterData.call(this); |
||||
|
this.nodeType = Node.COMMENT_NODE; |
||||
|
this.ownerDocument = doc; |
||||
|
this._data = data; |
||||
|
} |
||||
|
|
||||
|
var nodeValue = { |
||||
|
get: function() { return this._data; }, |
||||
|
set: function(v) { |
||||
|
if (v === null || v === undefined) { v = ''; } else { v = String(v); } |
||||
|
this._data = v; |
||||
|
if (this.rooted) |
||||
|
this.ownerDocument.mutateValue(this); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Comment.prototype = Object.create(CharacterData.prototype, { |
||||
|
nodeName: { value: '#comment' }, |
||||
|
nodeValue: nodeValue, |
||||
|
textContent: nodeValue, |
||||
|
data: { |
||||
|
get: nodeValue.get, |
||||
|
set: function(v) { |
||||
|
nodeValue.set.call(this, v===null ? '' : String(v)); |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
return new Comment(this.ownerDocument, this._data); |
||||
|
}}, |
||||
|
}); |
@ -0,0 +1,80 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = ContainerNode; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var NodeList = require('./NodeList.js'); |
||||
|
|
||||
|
// This class defines common functionality for node subtypes that
|
||||
|
// can have children
|
||||
|
|
||||
|
function ContainerNode() { |
||||
|
Node.call(this); |
||||
|
this._firstChild = this._childNodes = null; |
||||
|
} |
||||
|
|
||||
|
// Primary representation is a circular linked list of siblings
|
||||
|
ContainerNode.prototype = Object.create(Node.prototype, { |
||||
|
|
||||
|
hasChildNodes: { value: function() { |
||||
|
if (this._childNodes) { |
||||
|
return this._childNodes.length > 0; |
||||
|
} |
||||
|
return this._firstChild !== null; |
||||
|
}}, |
||||
|
|
||||
|
childNodes: { get: function() { |
||||
|
this._ensureChildNodes(); |
||||
|
return this._childNodes; |
||||
|
}}, |
||||
|
|
||||
|
firstChild: { get: function() { |
||||
|
if (this._childNodes) { |
||||
|
return this._childNodes.length === 0 ? null : this._childNodes[0]; |
||||
|
} |
||||
|
return this._firstChild; |
||||
|
}}, |
||||
|
|
||||
|
lastChild: { get: function() { |
||||
|
var kids = this._childNodes, first; |
||||
|
if (kids) { |
||||
|
return kids.length === 0 ? null: kids[kids.length-1]; |
||||
|
} |
||||
|
first = this._firstChild; |
||||
|
if (first === null) { return null; } |
||||
|
return first._previousSibling; // circular linked list
|
||||
|
}}, |
||||
|
|
||||
|
_ensureChildNodes: { value: function() { |
||||
|
if (this._childNodes) { return; } |
||||
|
var first = this._firstChild, |
||||
|
kid = first, |
||||
|
childNodes = this._childNodes = new NodeList(); |
||||
|
if (first) do { |
||||
|
childNodes.push(kid); |
||||
|
kid = kid._nextSibling; |
||||
|
} while (kid !== first); // circular linked list
|
||||
|
this._firstChild = null; // free memory
|
||||
|
}}, |
||||
|
|
||||
|
// Remove all of this node's children. This is a minor
|
||||
|
// optimization that only calls modify() once.
|
||||
|
removeChildren: { value: function removeChildren() { |
||||
|
var root = this.rooted ? this.ownerDocument : null, |
||||
|
next = this.firstChild, |
||||
|
kid; |
||||
|
while (next !== null) { |
||||
|
kid = next; |
||||
|
next = kid.nextSibling; |
||||
|
|
||||
|
if (root) root.mutateRemove(kid); |
||||
|
kid.parentNode = null; |
||||
|
} |
||||
|
if (this._childNodes) { |
||||
|
this._childNodes.length = 0; |
||||
|
} else { |
||||
|
this._firstChild = null; |
||||
|
} |
||||
|
this.modify(); // Update last modified type once only
|
||||
|
}}, |
||||
|
|
||||
|
}); |
@ -0,0 +1,12 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = CustomEvent; |
||||
|
|
||||
|
var Event = require('./Event.js'); |
||||
|
|
||||
|
function CustomEvent(type, dictionary) { |
||||
|
// Just use the superclass constructor to initialize
|
||||
|
Event.call(this, type, dictionary); |
||||
|
} |
||||
|
CustomEvent.prototype = Object.create(Event.prototype, { |
||||
|
constructor: { value: CustomEvent } |
||||
|
}); |
@ -0,0 +1,134 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = DOMException; |
||||
|
|
||||
|
var INDEX_SIZE_ERR = 1; |
||||
|
var HIERARCHY_REQUEST_ERR = 3; |
||||
|
var WRONG_DOCUMENT_ERR = 4; |
||||
|
var INVALID_CHARACTER_ERR = 5; |
||||
|
var NO_MODIFICATION_ALLOWED_ERR = 7; |
||||
|
var NOT_FOUND_ERR = 8; |
||||
|
var NOT_SUPPORTED_ERR = 9; |
||||
|
var INVALID_STATE_ERR = 11; |
||||
|
var SYNTAX_ERR = 12; |
||||
|
var INVALID_MODIFICATION_ERR = 13; |
||||
|
var NAMESPACE_ERR = 14; |
||||
|
var INVALID_ACCESS_ERR = 15; |
||||
|
var TYPE_MISMATCH_ERR = 17; |
||||
|
var SECURITY_ERR = 18; |
||||
|
var NETWORK_ERR = 19; |
||||
|
var ABORT_ERR = 20; |
||||
|
var URL_MISMATCH_ERR = 21; |
||||
|
var QUOTA_EXCEEDED_ERR = 22; |
||||
|
var TIMEOUT_ERR = 23; |
||||
|
var INVALID_NODE_TYPE_ERR = 24; |
||||
|
var DATA_CLONE_ERR = 25; |
||||
|
|
||||
|
// Code to name
|
||||
|
var names = [ |
||||
|
null, // No error with code 0
|
||||
|
'INDEX_SIZE_ERR', |
||||
|
null, // historical
|
||||
|
'HIERARCHY_REQUEST_ERR', |
||||
|
'WRONG_DOCUMENT_ERR', |
||||
|
'INVALID_CHARACTER_ERR', |
||||
|
null, // historical
|
||||
|
'NO_MODIFICATION_ALLOWED_ERR', |
||||
|
'NOT_FOUND_ERR', |
||||
|
'NOT_SUPPORTED_ERR', |
||||
|
'INUSE_ATTRIBUTE_ERR', // historical
|
||||
|
'INVALID_STATE_ERR', |
||||
|
'SYNTAX_ERR', |
||||
|
'INVALID_MODIFICATION_ERR', |
||||
|
'NAMESPACE_ERR', |
||||
|
'INVALID_ACCESS_ERR', |
||||
|
null, // historical
|
||||
|
'TYPE_MISMATCH_ERR', |
||||
|
'SECURITY_ERR', |
||||
|
'NETWORK_ERR', |
||||
|
'ABORT_ERR', |
||||
|
'URL_MISMATCH_ERR', |
||||
|
'QUOTA_EXCEEDED_ERR', |
||||
|
'TIMEOUT_ERR', |
||||
|
'INVALID_NODE_TYPE_ERR', |
||||
|
'DATA_CLONE_ERR', |
||||
|
]; |
||||
|
|
||||
|
// Code to message
|
||||
|
// These strings are from the 13 May 2011 Editor's Draft of DOM Core.
|
||||
|
// http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
|
||||
|
// Copyright © 2011 W3C® (MIT, ERCIM, Keio), All Rights Reserved.
|
||||
|
// Used under the terms of the W3C Document License:
|
||||
|
// http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231
|
||||
|
var messages = [ |
||||
|
null, // No error with code 0
|
||||
|
'INDEX_SIZE_ERR (1): the index is not in the allowed range', |
||||
|
null, |
||||
|
'HIERARCHY_REQUEST_ERR (3): the operation would yield an incorrect nodes model', |
||||
|
'WRONG_DOCUMENT_ERR (4): the object is in the wrong Document, a call to importNode is required', |
||||
|
'INVALID_CHARACTER_ERR (5): the string contains invalid characters', |
||||
|
null, |
||||
|
'NO_MODIFICATION_ALLOWED_ERR (7): the object can not be modified', |
||||
|
'NOT_FOUND_ERR (8): the object can not be found here', |
||||
|
'NOT_SUPPORTED_ERR (9): this operation is not supported', |
||||
|
'INUSE_ATTRIBUTE_ERR (10): setAttributeNode called on owned Attribute', |
||||
|
'INVALID_STATE_ERR (11): the object is in an invalid state', |
||||
|
'SYNTAX_ERR (12): the string did not match the expected pattern', |
||||
|
'INVALID_MODIFICATION_ERR (13): the object can not be modified in this way', |
||||
|
'NAMESPACE_ERR (14): the operation is not allowed by Namespaces in XML', |
||||
|
'INVALID_ACCESS_ERR (15): the object does not support the operation or argument', |
||||
|
null, |
||||
|
'TYPE_MISMATCH_ERR (17): the type of the object does not match the expected type', |
||||
|
'SECURITY_ERR (18): the operation is insecure', |
||||
|
'NETWORK_ERR (19): a network error occurred', |
||||
|
'ABORT_ERR (20): the user aborted an operation', |
||||
|
'URL_MISMATCH_ERR (21): the given URL does not match another URL', |
||||
|
'QUOTA_EXCEEDED_ERR (22): the quota has been exceeded', |
||||
|
'TIMEOUT_ERR (23): a timeout occurred', |
||||
|
'INVALID_NODE_TYPE_ERR (24): the supplied node is invalid or has an invalid ancestor for this operation', |
||||
|
'DATA_CLONE_ERR (25): the object can not be cloned.' |
||||
|
]; |
||||
|
|
||||
|
// Name to code
|
||||
|
var constants = { |
||||
|
INDEX_SIZE_ERR: INDEX_SIZE_ERR, |
||||
|
DOMSTRING_SIZE_ERR: 2, // historical
|
||||
|
HIERARCHY_REQUEST_ERR: HIERARCHY_REQUEST_ERR, |
||||
|
WRONG_DOCUMENT_ERR: WRONG_DOCUMENT_ERR, |
||||
|
INVALID_CHARACTER_ERR: INVALID_CHARACTER_ERR, |
||||
|
NO_DATA_ALLOWED_ERR: 6, // historical
|
||||
|
NO_MODIFICATION_ALLOWED_ERR: NO_MODIFICATION_ALLOWED_ERR, |
||||
|
NOT_FOUND_ERR: NOT_FOUND_ERR, |
||||
|
NOT_SUPPORTED_ERR: NOT_SUPPORTED_ERR, |
||||
|
INUSE_ATTRIBUTE_ERR: 10, // historical
|
||||
|
INVALID_STATE_ERR: INVALID_STATE_ERR, |
||||
|
SYNTAX_ERR: SYNTAX_ERR, |
||||
|
INVALID_MODIFICATION_ERR: INVALID_MODIFICATION_ERR, |
||||
|
NAMESPACE_ERR: NAMESPACE_ERR, |
||||
|
INVALID_ACCESS_ERR: INVALID_ACCESS_ERR, |
||||
|
VALIDATION_ERR: 16, // historical
|
||||
|
TYPE_MISMATCH_ERR: TYPE_MISMATCH_ERR, |
||||
|
SECURITY_ERR: SECURITY_ERR, |
||||
|
NETWORK_ERR: NETWORK_ERR, |
||||
|
ABORT_ERR: ABORT_ERR, |
||||
|
URL_MISMATCH_ERR: URL_MISMATCH_ERR, |
||||
|
QUOTA_EXCEEDED_ERR: QUOTA_EXCEEDED_ERR, |
||||
|
TIMEOUT_ERR: TIMEOUT_ERR, |
||||
|
INVALID_NODE_TYPE_ERR: INVALID_NODE_TYPE_ERR, |
||||
|
DATA_CLONE_ERR: DATA_CLONE_ERR |
||||
|
}; |
||||
|
|
||||
|
function DOMException(code) { |
||||
|
Error.call(this); |
||||
|
Error.captureStackTrace(this, this.constructor); |
||||
|
this.code = code; |
||||
|
this.message = messages[code]; |
||||
|
this.name = names[code]; |
||||
|
} |
||||
|
DOMException.prototype.__proto__ = Error.prototype; |
||||
|
|
||||
|
// Initialize the constants on DOMException and DOMException.prototype
|
||||
|
for(var c in constants) { |
||||
|
var v = { value: constants[c] }; |
||||
|
Object.defineProperty(DOMException, c, v); |
||||
|
Object.defineProperty(DOMException.prototype, c, v); |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = DOMImplementation; |
||||
|
|
||||
|
var Document = require('./Document.js'); |
||||
|
var DocumentType = require('./DocumentType.js'); |
||||
|
var HTMLParser = require('./HTMLParser.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
var xml = require('./xmlnames.js'); |
||||
|
|
||||
|
// Each document must have its own instance of the domimplementation object
|
||||
|
function DOMImplementation(contextObject) { |
||||
|
this.contextObject = contextObject; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Feature/version pairs that DOMImplementation.hasFeature() returns
|
||||
|
// true for. It returns false for anything else.
|
||||
|
var supportedFeatures = { |
||||
|
'xml': { '': true, '1.0': true, '2.0': true }, // DOM Core
|
||||
|
'core': { '': true, '2.0': true }, // DOM Core
|
||||
|
'html': { '': true, '1.0': true, '2.0': true} , // HTML
|
||||
|
'xhtml': { '': true, '1.0': true, '2.0': true} , // HTML
|
||||
|
}; |
||||
|
|
||||
|
DOMImplementation.prototype = { |
||||
|
hasFeature: function hasFeature(feature, version) { |
||||
|
var f = supportedFeatures[(feature || '').toLowerCase()]; |
||||
|
return (f && f[version || '']) || false; |
||||
|
}, |
||||
|
|
||||
|
createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) { |
||||
|
if (!xml.isValidQName(qualifiedName)) utils.InvalidCharacterError(); |
||||
|
|
||||
|
return new DocumentType(this.contextObject, qualifiedName, publicId, systemId); |
||||
|
}, |
||||
|
|
||||
|
createDocument: function createDocument(namespace, qualifiedName, doctype) { |
||||
|
//
|
||||
|
// Note that the current DOMCore spec makes it impossible to
|
||||
|
// create an HTML document with this function, even if the
|
||||
|
// namespace and doctype are propertly set. See this thread:
|
||||
|
// http://lists.w3.org/Archives/Public/www-dom/2011AprJun/0132.html
|
||||
|
//
|
||||
|
var d = new Document(false, null); |
||||
|
var e; |
||||
|
|
||||
|
if (qualifiedName) |
||||
|
e = d.createElementNS(namespace, qualifiedName); |
||||
|
else |
||||
|
e = null; |
||||
|
|
||||
|
if (doctype) { |
||||
|
d.appendChild(doctype); |
||||
|
} |
||||
|
|
||||
|
if (e) d.appendChild(e); |
||||
|
if (namespace === utils.NAMESPACE.HTML) { |
||||
|
d._contentType = 'application/xhtml+xml'; |
||||
|
} else if (namespace === utils.NAMESPACE.SVG) { |
||||
|
d._contentType = 'image/svg+xml'; |
||||
|
} else { |
||||
|
d._contentType = 'application/xml'; |
||||
|
} |
||||
|
|
||||
|
return d; |
||||
|
}, |
||||
|
|
||||
|
createHTMLDocument: function createHTMLDocument(titleText) { |
||||
|
var d = new Document(true, null); |
||||
|
d.appendChild(new DocumentType(d, 'html')); |
||||
|
var html = d.createElement('html'); |
||||
|
d.appendChild(html); |
||||
|
var head = d.createElement('head'); |
||||
|
html.appendChild(head); |
||||
|
if (titleText !== undefined) { |
||||
|
var title = d.createElement('title'); |
||||
|
head.appendChild(title); |
||||
|
title.appendChild(d.createTextNode(titleText)); |
||||
|
} |
||||
|
html.appendChild(d.createElement('body')); |
||||
|
d.modclock = 1; // Start tracking modifications
|
||||
|
return d; |
||||
|
}, |
||||
|
|
||||
|
mozSetOutputMutationHandler: function(doc, handler) { |
||||
|
doc.mutationHandler = handler; |
||||
|
}, |
||||
|
|
||||
|
mozGetInputMutationHandler: function(doc) { |
||||
|
utils.nyi(); |
||||
|
}, |
||||
|
|
||||
|
mozHTMLParser: HTMLParser, |
||||
|
}; |
@ -0,0 +1,172 @@ |
|||||
|
"use strict"; |
||||
|
// DOMTokenList implementation based on https://github.com/Raynos/DOM-shim
|
||||
|
// XXX: should cache the getList(this) value more aggressively!
|
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
module.exports = DOMTokenList; |
||||
|
|
||||
|
function DOMTokenList(getter, setter) { |
||||
|
this._getString = getter; |
||||
|
this._setString = setter; |
||||
|
this._length = 0; |
||||
|
this._update(); |
||||
|
} |
||||
|
|
||||
|
Object.defineProperties(DOMTokenList.prototype, { |
||||
|
length: { get: function() { return this._length; } }, |
||||
|
item: { value: function(index) { |
||||
|
var list = getList(this); |
||||
|
if (index < 0 || index >= list.length) { |
||||
|
return null; |
||||
|
} |
||||
|
return list[index]; |
||||
|
}}, |
||||
|
|
||||
|
contains: { value: function(token) { |
||||
|
token = String(token); // no error checking for contains()
|
||||
|
var list = getList(this); |
||||
|
return list.indexOf(token) > -1; |
||||
|
}}, |
||||
|
|
||||
|
add: { value: function() { |
||||
|
var list = getList(this); |
||||
|
for (var i = 0, len = arguments.length; i < len; i++) { |
||||
|
var token = handleErrors(arguments[i]); |
||||
|
if (list.indexOf(token) < 0) { |
||||
|
list.push(token); |
||||
|
} |
||||
|
} |
||||
|
// Note: as per spec, if handleErrors() throws any errors, we never
|
||||
|
// make it here and none of the changes take effect.
|
||||
|
// Also per spec: we run the "update steps" even if no change was
|
||||
|
// made (ie, if the token already existed)
|
||||
|
this._update(list); |
||||
|
}}, |
||||
|
|
||||
|
remove: { value: function() { |
||||
|
var list = getList(this); |
||||
|
for (var i = 0, len = arguments.length; i < len; i++) { |
||||
|
var token = handleErrors(arguments[i]); |
||||
|
var index = list.indexOf(token); |
||||
|
if (index > -1) { |
||||
|
list.splice(index, 1); |
||||
|
} |
||||
|
} |
||||
|
// Note: as per spec, if handleErrors() throws any errors, we never
|
||||
|
// make it here and none of the changes take effect.
|
||||
|
// Also per spec: we run the "update steps" even if no change was
|
||||
|
// made (ie, if the token wasn't previously present)
|
||||
|
this._update(list); |
||||
|
}}, |
||||
|
|
||||
|
toggle: { value: function toggle(token, force) { |
||||
|
token = handleErrors(token); |
||||
|
if (this.contains(token)) { |
||||
|
if (force === undefined || force === false) { |
||||
|
this.remove(token); |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} else { |
||||
|
if (force === undefined || force === true) { |
||||
|
this.add(token); |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
replace: { value: function replace(token, newToken) { |
||||
|
// weird corner case of spec: if `token` contains whitespace, but
|
||||
|
// `newToken` is the empty string, we must throw SyntaxError not
|
||||
|
// InvalidCharacterError (sigh)
|
||||
|
if (String(newToken)==='') { utils.SyntaxError(); } |
||||
|
token = handleErrors(token); |
||||
|
newToken = handleErrors(newToken); |
||||
|
var list = getList(this); |
||||
|
var idx = list.indexOf(token); |
||||
|
if (idx < 0) { |
||||
|
// Note that, per spec, we do not run the update steps on this path.
|
||||
|
return false; |
||||
|
} |
||||
|
var idx2 = list.indexOf(newToken); |
||||
|
if (idx2 < 0) { |
||||
|
list[idx] = newToken; |
||||
|
} else { |
||||
|
// "replace the first instance of either `token` or `newToken` with
|
||||
|
// `newToken` and remove all other instances"
|
||||
|
if (idx < idx2) { |
||||
|
list[idx] = newToken; |
||||
|
list.splice(idx2, 1); |
||||
|
} else { |
||||
|
// idx2 is already `newToken`
|
||||
|
list.splice(idx, 1); |
||||
|
} |
||||
|
} |
||||
|
this._update(list); |
||||
|
return true; |
||||
|
}}, |
||||
|
|
||||
|
toString: { value: function() { |
||||
|
return this._getString(); |
||||
|
}}, |
||||
|
|
||||
|
value: { |
||||
|
get: function() { |
||||
|
return this._getString(); |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
this._setString(v); |
||||
|
this._update(); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// Called when the setter is called from outside this interface.
|
||||
|
_update: { value: function(list) { |
||||
|
if (list) { |
||||
|
fixIndex(this, list); |
||||
|
this._setString(list.join(" ").trim()); |
||||
|
} else { |
||||
|
fixIndex(this, getList(this)); |
||||
|
} |
||||
|
} }, |
||||
|
}); |
||||
|
|
||||
|
function fixIndex(clist, list) { |
||||
|
var oldLength = clist._length; |
||||
|
var i; |
||||
|
clist._length = list.length; |
||||
|
for (i = 0; i < list.length; i++) { |
||||
|
clist[i] = list[i]; |
||||
|
} |
||||
|
// Clear/free old entries.
|
||||
|
for (; i < oldLength; i++) { |
||||
|
clist[i] = undefined; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleErrors(token) { |
||||
|
token = String(token); |
||||
|
if (token === "") { |
||||
|
utils.SyntaxError(); |
||||
|
} |
||||
|
if (/[ \t\r\n\f]/.test(token)) { |
||||
|
utils.InvalidCharacterError(); |
||||
|
} |
||||
|
return token; |
||||
|
} |
||||
|
|
||||
|
function getList(clist) { |
||||
|
var str = clist._getString().replace(/(^[ \t\r\n\f]+)|([ \t\r\n\f]+$)/g, ''); |
||||
|
if (str === "") { |
||||
|
return []; |
||||
|
} else { |
||||
|
var seen = Object.create(null); |
||||
|
return str.split(/[ \t\r\n\f]+/g).filter(function(n) { |
||||
|
var key = '$' + n; |
||||
|
if (seen[key]) { return false; } |
||||
|
seen[key] = true; |
||||
|
return true; |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,884 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Document; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var NodeList = require('./NodeList.js'); |
||||
|
var ContainerNode = require('./ContainerNode.js'); |
||||
|
var Element = require('./Element.js'); |
||||
|
var Text = require('./Text.js'); |
||||
|
var Comment = require('./Comment.js'); |
||||
|
var Event = require('./Event.js'); |
||||
|
var DocumentFragment = require('./DocumentFragment.js'); |
||||
|
var ProcessingInstruction = require('./ProcessingInstruction.js'); |
||||
|
var DOMImplementation = require('./DOMImplementation.js'); |
||||
|
var TreeWalker = require('./TreeWalker.js'); |
||||
|
var NodeIterator = require('./NodeIterator.js'); |
||||
|
var NodeFilter = require('./NodeFilter.js'); |
||||
|
var URL = require('./URL.js'); |
||||
|
var select = require('./select.js'); |
||||
|
var events = require('./events.js'); |
||||
|
var xml = require('./xmlnames.js'); |
||||
|
var html = require('./htmlelts.js'); |
||||
|
var svg = require('./svg.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
var MUTATE = require('./MutationConstants.js'); |
||||
|
var NAMESPACE = utils.NAMESPACE; |
||||
|
var isApiWritable = require("./config.js").isApiWritable; |
||||
|
|
||||
|
function Document(isHTML, address) { |
||||
|
ContainerNode.call(this); |
||||
|
this.nodeType = Node.DOCUMENT_NODE; |
||||
|
this.isHTML = isHTML; |
||||
|
this._address = address || 'about:blank'; |
||||
|
this.readyState = 'loading'; |
||||
|
this.implementation = new DOMImplementation(this); |
||||
|
|
||||
|
// DOMCore says that documents are always associated with themselves
|
||||
|
this.ownerDocument = null; // ... but W3C tests expect null
|
||||
|
this._contentType = isHTML ? 'text/html' : 'application/xml'; |
||||
|
|
||||
|
// These will be initialized by our custom versions of
|
||||
|
// appendChild and insertBefore that override the inherited
|
||||
|
// Node methods.
|
||||
|
// XXX: override those methods!
|
||||
|
this.doctype = null; |
||||
|
this.documentElement = null; |
||||
|
|
||||
|
// "Associated inert template document"
|
||||
|
this._templateDocCache = null; |
||||
|
// List of active NodeIterators, see NodeIterator#_preremove()
|
||||
|
this._nodeIterators = null; |
||||
|
|
||||
|
// Documents are always rooted, by definition
|
||||
|
this._nid = 1; |
||||
|
this._nextnid = 2; // For numbering children of the document
|
||||
|
this._nodes = [null, this]; // nid to node map
|
||||
|
|
||||
|
// This maintains the mapping from element ids to element nodes.
|
||||
|
// We may need to update this mapping every time a node is rooted
|
||||
|
// or uprooted, and any time an attribute is added, removed or changed
|
||||
|
// on a rooted element.
|
||||
|
this.byId = Object.create(null); |
||||
|
|
||||
|
// This property holds a monotonically increasing value akin to
|
||||
|
// a timestamp used to record the last modification time of nodes
|
||||
|
// and their subtrees. See the lastModTime attribute and modify()
|
||||
|
// method of the Node class. And see FilteredElementList for an example
|
||||
|
// of the use of lastModTime
|
||||
|
this.modclock = 0; |
||||
|
} |
||||
|
|
||||
|
// Map from lowercase event category names (used as arguments to
|
||||
|
// createEvent()) to the property name in the impl object of the
|
||||
|
// event constructor.
|
||||
|
var supportedEvents = { |
||||
|
event: 'Event', |
||||
|
customevent: 'CustomEvent', |
||||
|
uievent: 'UIEvent', |
||||
|
mouseevent: 'MouseEvent' |
||||
|
}; |
||||
|
|
||||
|
// Certain arguments to document.createEvent() must be treated specially
|
||||
|
var replacementEvent = { |
||||
|
events: 'event', |
||||
|
htmlevents: 'event', |
||||
|
mouseevents: 'mouseevent', |
||||
|
mutationevents: 'mutationevent', |
||||
|
uievents: 'uievent' |
||||
|
}; |
||||
|
|
||||
|
var mirrorAttr = function(f, name, defaultValue) { |
||||
|
return { |
||||
|
get: function() { |
||||
|
var o = f.call(this); |
||||
|
if (o) { return o[name]; } |
||||
|
return defaultValue; |
||||
|
}, |
||||
|
set: function(value) { |
||||
|
var o = f.call(this); |
||||
|
if (o) { o[name] = value; } |
||||
|
}, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
/** @spec https://dom.spec.whatwg.org/#validate-and-extract */ |
||||
|
function validateAndExtract(namespace, qualifiedName) { |
||||
|
var prefix, localName, pos; |
||||
|
if (namespace==='') { namespace = null; } |
||||
|
// See https://github.com/whatwg/dom/issues/671
|
||||
|
// and https://github.com/whatwg/dom/issues/319
|
||||
|
if (!xml.isValidQName(qualifiedName)) { |
||||
|
utils.InvalidCharacterError(); |
||||
|
} |
||||
|
prefix = null; |
||||
|
localName = qualifiedName; |
||||
|
|
||||
|
pos = qualifiedName.indexOf(':'); |
||||
|
if (pos >= 0) { |
||||
|
prefix = qualifiedName.substring(0, pos); |
||||
|
localName = qualifiedName.substring(pos+1); |
||||
|
} |
||||
|
if (prefix !== null && namespace === null) { |
||||
|
utils.NamespaceError(); |
||||
|
} |
||||
|
if (prefix === 'xml' && namespace !== NAMESPACE.XML) { |
||||
|
utils.NamespaceError(); |
||||
|
} |
||||
|
if ((prefix === 'xmlns' || qualifiedName === 'xmlns') && |
||||
|
namespace !== NAMESPACE.XMLNS) { |
||||
|
utils.NamespaceError(); |
||||
|
} |
||||
|
if (namespace === NAMESPACE.XMLNS && !(prefix==='xmlns' || qualifiedName==='xmlns')) { |
||||
|
utils.NamespaceError(); |
||||
|
} |
||||
|
return { namespace: namespace, prefix: prefix, localName: localName }; |
||||
|
} |
||||
|
|
||||
|
Document.prototype = Object.create(ContainerNode.prototype, { |
||||
|
// This method allows dom.js to communicate with a renderer
|
||||
|
// that displays the document in some way
|
||||
|
// XXX: I should probably move this to the window object
|
||||
|
_setMutationHandler: { value: function(handler) { |
||||
|
this.mutationHandler = handler; |
||||
|
}}, |
||||
|
|
||||
|
// This method allows dom.js to receive event notifications
|
||||
|
// from the renderer.
|
||||
|
// XXX: I should probably move this to the window object
|
||||
|
_dispatchRendererEvent: { value: function(targetNid, type, details) { |
||||
|
var target = this._nodes[targetNid]; |
||||
|
if (!target) return; |
||||
|
target._dispatchEvent(new Event(type, details), true); |
||||
|
}}, |
||||
|
|
||||
|
nodeName: { value: '#document'}, |
||||
|
nodeValue: { |
||||
|
get: function() { |
||||
|
return null; |
||||
|
}, |
||||
|
set: function() {} |
||||
|
}, |
||||
|
|
||||
|
// XXX: DOMCore may remove documentURI, so it is NYI for now
|
||||
|
documentURI: { get: function() { return this._address; }, set: utils.nyi }, |
||||
|
compatMode: { get: function() { |
||||
|
// The _quirks property is set by the HTML parser
|
||||
|
return this._quirks ? 'BackCompat' : 'CSS1Compat'; |
||||
|
}}, |
||||
|
|
||||
|
createTextNode: { value: function(data) { |
||||
|
return new Text(this, String(data)); |
||||
|
}}, |
||||
|
createComment: { value: function(data) { |
||||
|
return new Comment(this, data); |
||||
|
}}, |
||||
|
createDocumentFragment: { value: function() { |
||||
|
return new DocumentFragment(this); |
||||
|
}}, |
||||
|
createProcessingInstruction: { value: function(target, data) { |
||||
|
if (!xml.isValidName(target) || data.indexOf('?>') !== -1) |
||||
|
utils.InvalidCharacterError(); |
||||
|
return new ProcessingInstruction(this, target, data); |
||||
|
}}, |
||||
|
|
||||
|
createAttribute: { value: function(localName) { |
||||
|
localName = String(localName); |
||||
|
if (!xml.isValidName(localName)) utils.InvalidCharacterError(); |
||||
|
if (this.isHTML) { |
||||
|
localName = utils.toASCIILowerCase(localName); |
||||
|
} |
||||
|
return new Element._Attr(null, localName, null, null, ''); |
||||
|
}}, |
||||
|
createAttributeNS: { value: function(namespace, qualifiedName) { |
||||
|
// Convert parameter types according to WebIDL
|
||||
|
namespace = |
||||
|
(namespace === null || namespace === undefined || namespace === '') ? null : |
||||
|
String(namespace); |
||||
|
qualifiedName = String(qualifiedName); |
||||
|
var ve = validateAndExtract(namespace, qualifiedName); |
||||
|
return new Element._Attr(null, ve.localName, ve.prefix, ve.namespace, ''); |
||||
|
}}, |
||||
|
|
||||
|
createElement: { value: function(localName) { |
||||
|
localName = String(localName); |
||||
|
if (!xml.isValidName(localName)) utils.InvalidCharacterError(); |
||||
|
// Per spec, namespace should be HTML namespace if "context object is
|
||||
|
// an HTML document or context object's content type is
|
||||
|
// "application/xhtml+xml", and null otherwise.
|
||||
|
if (this.isHTML) { |
||||
|
if (/[A-Z]/.test(localName)) |
||||
|
localName = utils.toASCIILowerCase(localName); |
||||
|
return html.createElement(this, localName, null); |
||||
|
} else if (this.contentType === 'application/xhtml+xml') { |
||||
|
return html.createElement(this, localName, null); |
||||
|
} else { |
||||
|
return new Element(this, localName, null, null); |
||||
|
} |
||||
|
}, writable: isApiWritable }, |
||||
|
|
||||
|
createElementNS: { value: function(namespace, qualifiedName) { |
||||
|
// Convert parameter types according to WebIDL
|
||||
|
namespace = |
||||
|
(namespace === null || namespace === undefined || namespace === '') ? null : |
||||
|
String(namespace); |
||||
|
qualifiedName = String(qualifiedName); |
||||
|
var ve = validateAndExtract(namespace, qualifiedName); |
||||
|
return this._createElementNS(ve.localName, ve.namespace, ve.prefix); |
||||
|
}, writable: isApiWritable }, |
||||
|
|
||||
|
// This is used directly by HTML parser, which allows it to create
|
||||
|
// elements with localNames containing ':' and non-default namespaces
|
||||
|
_createElementNS: { value: function(localName, namespace, prefix) { |
||||
|
if (namespace === NAMESPACE.HTML) { |
||||
|
return html.createElement(this, localName, prefix); |
||||
|
} |
||||
|
else if (namespace === NAMESPACE.SVG) { |
||||
|
return svg.createElement(this, localName, prefix); |
||||
|
} |
||||
|
|
||||
|
return new Element(this, localName, namespace, prefix); |
||||
|
}}, |
||||
|
|
||||
|
createEvent: { value: function createEvent(interfaceName) { |
||||
|
interfaceName = interfaceName.toLowerCase(); |
||||
|
var name = replacementEvent[interfaceName] || interfaceName; |
||||
|
var constructor = events[supportedEvents[name]]; |
||||
|
|
||||
|
if (constructor) { |
||||
|
var e = new constructor(); |
||||
|
e._initialized = false; |
||||
|
return e; |
||||
|
} |
||||
|
else { |
||||
|
utils.NotSupportedError(); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// See: http://www.w3.org/TR/dom/#dom-document-createtreewalker
|
||||
|
createTreeWalker: {value: function (root, whatToShow, filter) { |
||||
|
if (!root) { throw new TypeError("root argument is required"); } |
||||
|
if (!(root instanceof Node)) { throw new TypeError("root not a node"); } |
||||
|
whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (+whatToShow); |
||||
|
filter = filter === undefined ? null : filter; |
||||
|
|
||||
|
return new TreeWalker(root, whatToShow, filter); |
||||
|
}}, |
||||
|
|
||||
|
// See: http://www.w3.org/TR/dom/#dom-document-createnodeiterator
|
||||
|
createNodeIterator: {value: function (root, whatToShow, filter) { |
||||
|
if (!root) { throw new TypeError("root argument is required"); } |
||||
|
if (!(root instanceof Node)) { throw new TypeError("root not a node"); } |
||||
|
whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (+whatToShow); |
||||
|
filter = filter === undefined ? null : filter; |
||||
|
|
||||
|
return new NodeIterator(root, whatToShow, filter); |
||||
|
}}, |
||||
|
|
||||
|
_attachNodeIterator: { value: function(ni) { |
||||
|
// XXX ideally this should be a weak reference from Document to NodeIterator
|
||||
|
if (!this._nodeIterators) { this._nodeIterators = []; } |
||||
|
this._nodeIterators.push(ni); |
||||
|
}}, |
||||
|
|
||||
|
_detachNodeIterator: { value: function(ni) { |
||||
|
// ni should always be in list of node iterators
|
||||
|
var idx = this._nodeIterators.indexOf(ni); |
||||
|
this._nodeIterators.splice(idx, 1); |
||||
|
}}, |
||||
|
|
||||
|
_preremoveNodeIterators: { value: function(toBeRemoved) { |
||||
|
if (this._nodeIterators) { |
||||
|
this._nodeIterators.forEach(function(ni) { ni._preremove(toBeRemoved); }); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Maintain the documentElement and
|
||||
|
// doctype properties of the document. Each of the following
|
||||
|
// methods chains to the Node implementation of the method
|
||||
|
// to do the actual inserting, removal or replacement.
|
||||
|
|
||||
|
_updateDocTypeElement: { value: function _updateDocTypeElement() { |
||||
|
this.doctype = this.documentElement = null; |
||||
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === Node.DOCUMENT_TYPE_NODE) |
||||
|
this.doctype = kid; |
||||
|
else if (kid.nodeType === Node.ELEMENT_NODE) |
||||
|
this.documentElement = kid; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
insertBefore: { value: function insertBefore(child, refChild) { |
||||
|
Node.prototype.insertBefore.call(this, child, refChild); |
||||
|
this._updateDocTypeElement(); |
||||
|
return child; |
||||
|
}}, |
||||
|
|
||||
|
replaceChild: { value: function replaceChild(node, child) { |
||||
|
Node.prototype.replaceChild.call(this, node, child); |
||||
|
this._updateDocTypeElement(); |
||||
|
return child; |
||||
|
}}, |
||||
|
|
||||
|
removeChild: { value: function removeChild(child) { |
||||
|
Node.prototype.removeChild.call(this, child); |
||||
|
this._updateDocTypeElement(); |
||||
|
return child; |
||||
|
}}, |
||||
|
|
||||
|
getElementById: { value: function(id) { |
||||
|
var n = this.byId[id]; |
||||
|
if (!n) return null; |
||||
|
if (n instanceof MultiId) { // there was more than one element with this id
|
||||
|
return n.getFirst(); |
||||
|
} |
||||
|
return n; |
||||
|
}}, |
||||
|
|
||||
|
_hasMultipleElementsWithId: { value: function(id) { |
||||
|
// Used internally by querySelectorAll optimization
|
||||
|
return (this.byId[id] instanceof MultiId); |
||||
|
}}, |
||||
|
|
||||
|
// Just copy this method from the Element prototype
|
||||
|
getElementsByName: { value: Element.prototype.getElementsByName }, |
||||
|
getElementsByTagName: { value: Element.prototype.getElementsByTagName }, |
||||
|
getElementsByTagNameNS: { value: Element.prototype.getElementsByTagNameNS }, |
||||
|
getElementsByClassName: { value: Element.prototype.getElementsByClassName }, |
||||
|
|
||||
|
adoptNode: { value: function adoptNode(node) { |
||||
|
if (node.nodeType === Node.DOCUMENT_NODE) utils.NotSupportedError(); |
||||
|
if (node.nodeType === Node.ATTRIBUTE_NODE) { return node; } |
||||
|
|
||||
|
if (node.parentNode) node.parentNode.removeChild(node); |
||||
|
|
||||
|
if (node.ownerDocument !== this) |
||||
|
recursivelySetOwner(node, this); |
||||
|
|
||||
|
return node; |
||||
|
}}, |
||||
|
|
||||
|
importNode: { value: function importNode(node, deep) { |
||||
|
return this.adoptNode(node.cloneNode(deep)); |
||||
|
}, writable: isApiWritable }, |
||||
|
|
||||
|
// The following attributes and methods are from the HTML spec
|
||||
|
origin: { get: function origin() { return null; } }, |
||||
|
characterSet: { get: function characterSet() { return "UTF-8"; } }, |
||||
|
contentType: { get: function contentType() { return this._contentType; } }, |
||||
|
URL: { get: function URL() { return this._address; } }, |
||||
|
domain: { get: utils.nyi, set: utils.nyi }, |
||||
|
referrer: { get: utils.nyi }, |
||||
|
cookie: { get: utils.nyi, set: utils.nyi }, |
||||
|
lastModified: { get: utils.nyi }, |
||||
|
location: { |
||||
|
get: function() { |
||||
|
return this.defaultView ? this.defaultView.location : null; // gh #75
|
||||
|
}, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
_titleElement: { |
||||
|
get: function() { |
||||
|
// The title element of a document is the first title element in the
|
||||
|
// document in tree order, if there is one, or null otherwise.
|
||||
|
return this.getElementsByTagName('title').item(0) || null; |
||||
|
} |
||||
|
}, |
||||
|
title: { |
||||
|
get: function() { |
||||
|
var elt = this._titleElement; |
||||
|
// The child text content of the title element, or '' if null.
|
||||
|
var value = elt ? elt.textContent : ''; |
||||
|
// Strip and collapse whitespace in value
|
||||
|
return value.replace(/[ \t\n\r\f]+/g, ' ').replace(/(^ )|( $)/g, ''); |
||||
|
}, |
||||
|
set: function(value) { |
||||
|
var elt = this._titleElement; |
||||
|
var head = this.head; |
||||
|
if (!elt && !head) { return; /* according to spec */ } |
||||
|
if (!elt) { |
||||
|
elt = this.createElement('title'); |
||||
|
head.appendChild(elt); |
||||
|
} |
||||
|
elt.textContent = value; |
||||
|
} |
||||
|
}, |
||||
|
dir: mirrorAttr(function() { |
||||
|
var htmlElement = this.documentElement; |
||||
|
if (htmlElement && htmlElement.tagName === 'HTML') { return htmlElement; } |
||||
|
}, 'dir', ''), |
||||
|
fgColor: mirrorAttr(function() { return this.body; }, 'text', ''), |
||||
|
linkColor: mirrorAttr(function() { return this.body; }, 'link', ''), |
||||
|
vlinkColor: mirrorAttr(function() { return this.body; }, 'vLink', ''), |
||||
|
alinkColor: mirrorAttr(function() { return this.body; }, 'aLink', ''), |
||||
|
bgColor: mirrorAttr(function() { return this.body; }, 'bgColor', ''), |
||||
|
|
||||
|
// Historical aliases of Document#characterSet
|
||||
|
charset: { get: function() { return this.characterSet; } }, |
||||
|
inputEncoding: { get: function() { return this.characterSet; } }, |
||||
|
|
||||
|
scrollingElement: { |
||||
|
get: function() { |
||||
|
return this._quirks ? this.body : this.documentElement; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// Return the first <body> child of the document element.
|
||||
|
// XXX For now, setting this attribute is not implemented.
|
||||
|
body: { |
||||
|
get: function() { |
||||
|
return namedHTMLChild(this.documentElement, 'body'); |
||||
|
}, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
// Return the first <head> child of the document element.
|
||||
|
head: { get: function() { |
||||
|
return namedHTMLChild(this.documentElement, 'head'); |
||||
|
}}, |
||||
|
images: { get: utils.nyi }, |
||||
|
embeds: { get: utils.nyi }, |
||||
|
plugins: { get: utils.nyi }, |
||||
|
links: { get: utils.nyi }, |
||||
|
forms: { get: utils.nyi }, |
||||
|
scripts: { get: utils.nyi }, |
||||
|
applets: { get: function() { return []; } }, |
||||
|
activeElement: { get: function() { return null; } }, |
||||
|
innerHTML: { |
||||
|
get: function() { return this.serialize(); }, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
outerHTML: { |
||||
|
get: function() { return this.serialize(); }, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
|
||||
|
write: { value: function(args) { |
||||
|
if (!this.isHTML) utils.InvalidStateError(); |
||||
|
|
||||
|
// XXX: still have to implement the ignore part
|
||||
|
if (!this._parser /* && this._ignore_destructive_writes > 0 */ ) |
||||
|
return; |
||||
|
|
||||
|
if (!this._parser) { |
||||
|
// XXX call document.open, etc.
|
||||
|
} |
||||
|
|
||||
|
var s = arguments.join(''); |
||||
|
|
||||
|
// If the Document object's reload override flag is set, then
|
||||
|
// append the string consisting of the concatenation of all the
|
||||
|
// arguments to the method to the Document's reload override
|
||||
|
// buffer.
|
||||
|
// XXX: don't know what this is about. Still have to do it
|
||||
|
|
||||
|
// If there is no pending parsing-blocking script, have the
|
||||
|
// tokenizer process the characters that were inserted, one at a
|
||||
|
// time, processing resulting tokens as they are emitted, and
|
||||
|
// stopping when the tokenizer reaches the insertion point or when
|
||||
|
// the processing of the tokenizer is aborted by the tree
|
||||
|
// construction stage (this can happen if a script end tag token is
|
||||
|
// emitted by the tokenizer).
|
||||
|
|
||||
|
// XXX: still have to do the above. Sounds as if we don't
|
||||
|
// always call parse() here. If we're blocked, then we just
|
||||
|
// insert the text into the stream but don't parse it reentrantly...
|
||||
|
|
||||
|
// Invoke the parser reentrantly
|
||||
|
this._parser.parse(s); |
||||
|
}}, |
||||
|
|
||||
|
writeln: { value: function writeln(args) { |
||||
|
this.write(Array.prototype.join.call(arguments, '') + '\n'); |
||||
|
}}, |
||||
|
|
||||
|
open: { value: function() { |
||||
|
this.documentElement = null; |
||||
|
}}, |
||||
|
|
||||
|
close: { value: function() { |
||||
|
this.readyState = 'interactive'; |
||||
|
this._dispatchEvent(new Event('readystatechange'), true); |
||||
|
this._dispatchEvent(new Event('DOMContentLoaded'), true); |
||||
|
this.readyState = 'complete'; |
||||
|
this._dispatchEvent(new Event('readystatechange'), true); |
||||
|
if (this.defaultView) { |
||||
|
this.defaultView._dispatchEvent(new Event('load'), true); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
var d = new Document(this.isHTML, this._address); |
||||
|
d._quirks = this._quirks; |
||||
|
d._contentType = this._contentType; |
||||
|
return d; |
||||
|
}}, |
||||
|
|
||||
|
// We need to adopt the nodes if we do a deep clone
|
||||
|
cloneNode: { value: function cloneNode(deep) { |
||||
|
var clone = Node.prototype.cloneNode.call(this, false); |
||||
|
if (deep) { |
||||
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
clone._appendChild(clone.importNode(kid, true)); |
||||
|
} |
||||
|
} |
||||
|
clone._updateDocTypeElement(); |
||||
|
return clone; |
||||
|
}}, |
||||
|
|
||||
|
isEqual: { value: function isEqual(n) { |
||||
|
// Any two documents are shallowly equal.
|
||||
|
// Node.isEqualNode will also test the children
|
||||
|
return true; |
||||
|
}}, |
||||
|
|
||||
|
// Implementation-specific function. Called when a text, comment,
|
||||
|
// or pi value changes.
|
||||
|
mutateValue: { value: function(node) { |
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.VALUE, |
||||
|
target: node, |
||||
|
data: node.data |
||||
|
}); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Invoked when an attribute's value changes. Attr holds the new
|
||||
|
// value. oldval is the old value. Attribute mutations can also
|
||||
|
// involve changes to the prefix (and therefore the qualified name)
|
||||
|
mutateAttr: { value: function(attr, oldval) { |
||||
|
// Manage id->element mapping for getElementsById()
|
||||
|
// XXX: this special case id handling should not go here,
|
||||
|
// but in the attribute declaration for the id attribute
|
||||
|
/* |
||||
|
if (attr.localName === 'id' && attr.namespaceURI === null) { |
||||
|
if (oldval) delId(oldval, attr.ownerElement); |
||||
|
addId(attr.value, attr.ownerElement); |
||||
|
} |
||||
|
*/ |
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.ATTR, |
||||
|
target: attr.ownerElement, |
||||
|
attr: attr |
||||
|
}); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Used by removeAttribute and removeAttributeNS for attributes.
|
||||
|
mutateRemoveAttr: { value: function(attr) { |
||||
|
/* |
||||
|
* This is now handled in Attributes.js |
||||
|
// Manage id to element mapping
|
||||
|
if (attr.localName === 'id' && attr.namespaceURI === null) { |
||||
|
this.delId(attr.value, attr.ownerElement); |
||||
|
} |
||||
|
*/ |
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.REMOVE_ATTR, |
||||
|
target: attr.ownerElement, |
||||
|
attr: attr |
||||
|
}); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Called by Node.removeChild, etc. to remove a rooted element from
|
||||
|
// the tree. Only needs to generate a single mutation event when a
|
||||
|
// node is removed, but must recursively mark all descendants as not
|
||||
|
// rooted.
|
||||
|
mutateRemove: { value: function(node) { |
||||
|
// Send a single mutation event
|
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.REMOVE, |
||||
|
target: node.parentNode, |
||||
|
node: node |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// Mark this and all descendants as not rooted
|
||||
|
recursivelyUproot(node); |
||||
|
}}, |
||||
|
|
||||
|
// Called when a new element becomes rooted. It must recursively
|
||||
|
// generate mutation events for each of the children, and mark them all
|
||||
|
// as rooted.
|
||||
|
mutateInsert: { value: function(node) { |
||||
|
// Mark node and its descendants as rooted
|
||||
|
recursivelyRoot(node); |
||||
|
|
||||
|
// Send a single mutation event
|
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.INSERT, |
||||
|
target: node.parentNode, |
||||
|
node: node |
||||
|
}); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Called when a rooted element is moved within the document
|
||||
|
mutateMove: { value: function(node) { |
||||
|
if (this.mutationHandler) { |
||||
|
this.mutationHandler({ |
||||
|
type: MUTATE.MOVE, |
||||
|
target: node |
||||
|
}); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// Add a mapping from id to n for n.ownerDocument
|
||||
|
addId: { value: function addId(id, n) { |
||||
|
var val = this.byId[id]; |
||||
|
if (!val) { |
||||
|
this.byId[id] = n; |
||||
|
} |
||||
|
else { |
||||
|
// TODO: Add a way to opt-out console warnings
|
||||
|
//console.warn('Duplicate element id ' + id);
|
||||
|
if (!(val instanceof MultiId)) { |
||||
|
val = new MultiId(val); |
||||
|
this.byId[id] = val; |
||||
|
} |
||||
|
val.add(n); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Delete the mapping from id to n for n.ownerDocument
|
||||
|
delId: { value: function delId(id, n) { |
||||
|
var val = this.byId[id]; |
||||
|
utils.assert(val); |
||||
|
|
||||
|
if (val instanceof MultiId) { |
||||
|
val.del(n); |
||||
|
if (val.length === 1) { // convert back to a single node
|
||||
|
this.byId[id] = val.downgrade(); |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
this.byId[id] = undefined; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
_resolve: { value: function(href) { |
||||
|
//XXX: Cache the URL
|
||||
|
return new URL(this._documentBaseURL).resolve(href); |
||||
|
}}, |
||||
|
|
||||
|
_documentBaseURL: { get: function() { |
||||
|
// XXX: This is not implemented correctly yet
|
||||
|
var url = this._address; |
||||
|
if (url === 'about:blank') url = '/'; |
||||
|
|
||||
|
var base = this.querySelector('base[href]'); |
||||
|
if (base) { |
||||
|
return new URL(url).resolve(base.getAttribute('href')); |
||||
|
} |
||||
|
return url; |
||||
|
|
||||
|
// The document base URL of a Document object is the
|
||||
|
// absolute URL obtained by running these substeps:
|
||||
|
|
||||
|
// Let fallback base url be the document's address.
|
||||
|
|
||||
|
// If fallback base url is about:blank, and the
|
||||
|
// Document's browsing context has a creator browsing
|
||||
|
// context, then let fallback base url be the document
|
||||
|
// base URL of the creator Document instead.
|
||||
|
|
||||
|
// If the Document is an iframe srcdoc document, then
|
||||
|
// let fallback base url be the document base URL of
|
||||
|
// the Document's browsing context's browsing context
|
||||
|
// container's Document instead.
|
||||
|
|
||||
|
// If there is no base element that has an href
|
||||
|
// attribute, then the document base URL is fallback
|
||||
|
// base url; abort these steps. Otherwise, let url be
|
||||
|
// the value of the href attribute of the first such
|
||||
|
// element.
|
||||
|
|
||||
|
// Resolve url relative to fallback base url (thus,
|
||||
|
// the base href attribute isn't affected by xml:base
|
||||
|
// attributes).
|
||||
|
|
||||
|
// The document base URL is the result of the previous
|
||||
|
// step if it was successful; otherwise it is fallback
|
||||
|
// base url.
|
||||
|
}}, |
||||
|
|
||||
|
_templateDoc: { get: function() { |
||||
|
if (!this._templateDocCache) { |
||||
|
// "associated inert template document"
|
||||
|
var newDoc = new Document(this.isHTML, this._address); |
||||
|
this._templateDocCache = newDoc._templateDocCache = newDoc; |
||||
|
} |
||||
|
return this._templateDocCache; |
||||
|
}}, |
||||
|
|
||||
|
querySelector: { value: function(selector) { |
||||
|
return select(selector, this)[0]; |
||||
|
}}, |
||||
|
|
||||
|
querySelectorAll: { value: function(selector) { |
||||
|
var nodes = select(selector, this); |
||||
|
return nodes.item ? nodes : new NodeList(nodes); |
||||
|
}} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
|
||||
|
var eventHandlerTypes = [ |
||||
|
'abort', 'canplay', 'canplaythrough', 'change', 'click', 'contextmenu', |
||||
|
'cuechange', 'dblclick', 'drag', 'dragend', 'dragenter', 'dragleave', |
||||
|
'dragover', 'dragstart', 'drop', 'durationchange', 'emptied', 'ended', |
||||
|
'input', 'invalid', 'keydown', 'keypress', 'keyup', 'loadeddata', |
||||
|
'loadedmetadata', 'loadstart', 'mousedown', 'mousemove', 'mouseout', |
||||
|
'mouseover', 'mouseup', 'mousewheel', 'pause', 'play', 'playing', |
||||
|
'progress', 'ratechange', 'readystatechange', 'reset', 'seeked', |
||||
|
'seeking', 'select', 'show', 'stalled', 'submit', 'suspend', |
||||
|
'timeupdate', 'volumechange', 'waiting', |
||||
|
|
||||
|
'blur', 'error', 'focus', 'load', 'scroll' |
||||
|
]; |
||||
|
|
||||
|
// Add event handler idl attribute getters and setters to Document
|
||||
|
eventHandlerTypes.forEach(function(type) { |
||||
|
// Define the event handler registration IDL attribute for this type
|
||||
|
Object.defineProperty(Document.prototype, 'on' + type, { |
||||
|
get: function() { |
||||
|
return this._getEventHandler(type); |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
this._setEventHandler(type, v); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function namedHTMLChild(parent, name) { |
||||
|
if (parent && parent.isHTML) { |
||||
|
for (var kid = parent.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === Node.ELEMENT_NODE && |
||||
|
kid.localName === name && |
||||
|
kid.namespaceURI === NAMESPACE.HTML) { |
||||
|
return kid; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
function root(n) { |
||||
|
n._nid = n.ownerDocument._nextnid++; |
||||
|
n.ownerDocument._nodes[n._nid] = n; |
||||
|
// Manage id to element mapping
|
||||
|
if (n.nodeType === Node.ELEMENT_NODE) { |
||||
|
var id = n.getAttribute('id'); |
||||
|
if (id) n.ownerDocument.addId(id, n); |
||||
|
|
||||
|
// Script elements need to know when they're inserted
|
||||
|
// into the document
|
||||
|
if (n._roothook) n._roothook(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function uproot(n) { |
||||
|
// Manage id to element mapping
|
||||
|
if (n.nodeType === Node.ELEMENT_NODE) { |
||||
|
var id = n.getAttribute('id'); |
||||
|
if (id) n.ownerDocument.delId(id, n); |
||||
|
} |
||||
|
n.ownerDocument._nodes[n._nid] = undefined; |
||||
|
n._nid = undefined; |
||||
|
} |
||||
|
|
||||
|
function recursivelyRoot(node) { |
||||
|
root(node); |
||||
|
// XXX:
|
||||
|
// accessing childNodes on a leaf node creates a new array the
|
||||
|
// first time, so be careful to write this loop so that it
|
||||
|
// doesn't do that. node is polymorphic, so maybe this is hard to
|
||||
|
// optimize? Try switching on nodeType?
|
||||
|
/* |
||||
|
if (node.hasChildNodes()) { |
||||
|
var kids = node.childNodes; |
||||
|
for(var i = 0, n = kids.length; i < n; i++) |
||||
|
recursivelyRoot(kids[i]); |
||||
|
} |
||||
|
*/ |
||||
|
if (node.nodeType === Node.ELEMENT_NODE) { |
||||
|
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling) |
||||
|
recursivelyRoot(kid); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function recursivelyUproot(node) { |
||||
|
uproot(node); |
||||
|
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling) |
||||
|
recursivelyUproot(kid); |
||||
|
} |
||||
|
|
||||
|
function recursivelySetOwner(node, owner) { |
||||
|
node.ownerDocument = owner; |
||||
|
node._lastModTime = undefined; // mod times are document-based
|
||||
|
if (Object.prototype.hasOwnProperty.call(node, '_tagName')) { |
||||
|
node._tagName = undefined; // Element subclasses might need to change case
|
||||
|
} |
||||
|
for (var kid = node.firstChild; kid !== null; kid = kid.nextSibling) |
||||
|
recursivelySetOwner(kid, owner); |
||||
|
} |
||||
|
|
||||
|
// A class for storing multiple nodes with the same ID
|
||||
|
function MultiId(node) { |
||||
|
this.nodes = Object.create(null); |
||||
|
this.nodes[node._nid] = node; |
||||
|
this.length = 1; |
||||
|
this.firstNode = undefined; |
||||
|
} |
||||
|
|
||||
|
// Add a node to the list, with O(1) time
|
||||
|
MultiId.prototype.add = function(node) { |
||||
|
if (!this.nodes[node._nid]) { |
||||
|
this.nodes[node._nid] = node; |
||||
|
this.length++; |
||||
|
this.firstNode = undefined; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Remove a node from the list, with O(1) time
|
||||
|
MultiId.prototype.del = function(node) { |
||||
|
if (this.nodes[node._nid]) { |
||||
|
delete this.nodes[node._nid]; |
||||
|
this.length--; |
||||
|
this.firstNode = undefined; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Get the first node from the list, in the document order
|
||||
|
// Takes O(N) time in the size of the list, with a cache that is invalidated
|
||||
|
// when the list is modified.
|
||||
|
MultiId.prototype.getFirst = function() { |
||||
|
/* jshint bitwise: false */ |
||||
|
if (!this.firstNode) { |
||||
|
var nid; |
||||
|
for (nid in this.nodes) { |
||||
|
if (this.firstNode === undefined || |
||||
|
this.firstNode.compareDocumentPosition(this.nodes[nid]) & Node.DOCUMENT_POSITION_PRECEDING) { |
||||
|
this.firstNode = this.nodes[nid]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return this.firstNode; |
||||
|
}; |
||||
|
|
||||
|
// If there is only one node left, return it. Otherwise return "this".
|
||||
|
MultiId.prototype.downgrade = function() { |
||||
|
if (this.length === 1) { |
||||
|
var nid; |
||||
|
for (nid in this.nodes) { |
||||
|
return this.nodes[nid]; |
||||
|
} |
||||
|
} |
||||
|
return this; |
||||
|
}; |
@ -0,0 +1,68 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = DocumentFragment; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var NodeList = require('./NodeList.js'); |
||||
|
var ContainerNode = require('./ContainerNode.js'); |
||||
|
var Element = require('./Element.js'); |
||||
|
var select = require('./select.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
function DocumentFragment(doc) { |
||||
|
ContainerNode.call(this); |
||||
|
this.nodeType = Node.DOCUMENT_FRAGMENT_NODE; |
||||
|
this.ownerDocument = doc; |
||||
|
} |
||||
|
|
||||
|
DocumentFragment.prototype = Object.create(ContainerNode.prototype, { |
||||
|
nodeName: { value: '#document-fragment' }, |
||||
|
nodeValue: { |
||||
|
get: function() { |
||||
|
return null; |
||||
|
}, |
||||
|
set: function() {} |
||||
|
}, |
||||
|
// Copy the text content getter/setter from Element
|
||||
|
textContent: Object.getOwnPropertyDescriptor(Element.prototype, 'textContent'), |
||||
|
|
||||
|
querySelector: { value: function(selector) { |
||||
|
// implement in terms of querySelectorAll
|
||||
|
var nodes = this.querySelectorAll(selector); |
||||
|
return nodes.length ? nodes[0] : null; |
||||
|
}}, |
||||
|
querySelectorAll: { value: function(selector) { |
||||
|
// create a context
|
||||
|
var context = Object.create(this); |
||||
|
// add some methods to the context for zest implementation, without
|
||||
|
// adding them to the public DocumentFragment API
|
||||
|
context.isHTML = true; // in HTML namespace (case-insensitive match)
|
||||
|
context.getElementsByTagName = Element.prototype.getElementsByTagName; |
||||
|
context.nextElement = |
||||
|
Object.getOwnPropertyDescriptor(Element.prototype, 'firstElementChild'). |
||||
|
get; |
||||
|
// invoke zest
|
||||
|
var nodes = select(selector, context); |
||||
|
return nodes.item ? nodes : new NodeList(nodes); |
||||
|
}}, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
return new DocumentFragment(this.ownerDocument); |
||||
|
}}, |
||||
|
isEqual: { value: function isEqual(n) { |
||||
|
// Any two document fragments are shallowly equal.
|
||||
|
// Node.isEqualNode() will test their children for equality
|
||||
|
return true; |
||||
|
}}, |
||||
|
|
||||
|
// Non-standard, but useful (github issue #73)
|
||||
|
innerHTML: { |
||||
|
get: function() { return this.serialize(); }, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
outerHTML: { |
||||
|
get: function() { return this.serialize(); }, |
||||
|
set: utils.nyi |
||||
|
}, |
||||
|
|
||||
|
}); |
@ -0,0 +1,36 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = DocumentType; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var Leaf = require('./Leaf.js'); |
||||
|
var ChildNode = require('./ChildNode.js'); |
||||
|
|
||||
|
function DocumentType(ownerDocument, name, publicId, systemId) { |
||||
|
Leaf.call(this); |
||||
|
this.nodeType = Node.DOCUMENT_TYPE_NODE; |
||||
|
this.ownerDocument = ownerDocument || null; |
||||
|
this.name = name; |
||||
|
this.publicId = publicId || ""; |
||||
|
this.systemId = systemId || ""; |
||||
|
} |
||||
|
|
||||
|
DocumentType.prototype = Object.create(Leaf.prototype, { |
||||
|
nodeName: { get: function() { return this.name; }}, |
||||
|
nodeValue: { |
||||
|
get: function() { return null; }, |
||||
|
set: function() {} |
||||
|
}, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
return new DocumentType(this.ownerDocument, this.name, this.publicId, this.systemId); |
||||
|
}}, |
||||
|
|
||||
|
isEqual: { value: function isEqual(n) { |
||||
|
return this.name === n.name && |
||||
|
this.publicId === n.publicId && |
||||
|
this.systemId === n.systemId; |
||||
|
}} |
||||
|
}); |
||||
|
|
||||
|
Object.defineProperties(DocumentType.prototype, ChildNode); |
1202
domino/Element.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,66 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Event; |
||||
|
|
||||
|
Event.CAPTURING_PHASE = 1; |
||||
|
Event.AT_TARGET = 2; |
||||
|
Event.BUBBLING_PHASE = 3; |
||||
|
|
||||
|
function Event(type, dictionary) { |
||||
|
// Initialize basic event properties
|
||||
|
this.type = ''; |
||||
|
this.target = null; |
||||
|
this.currentTarget = null; |
||||
|
this.eventPhase = Event.AT_TARGET; |
||||
|
this.bubbles = false; |
||||
|
this.cancelable = false; |
||||
|
this.isTrusted = false; |
||||
|
this.defaultPrevented = false; |
||||
|
this.timeStamp = Date.now(); |
||||
|
|
||||
|
// Initialize internal flags
|
||||
|
// XXX: Would it be better to inherit these defaults from the prototype?
|
||||
|
this._propagationStopped = false; |
||||
|
this._immediatePropagationStopped = false; |
||||
|
this._initialized = true; |
||||
|
this._dispatching = false; |
||||
|
|
||||
|
// Now initialize based on the constructor arguments (if any)
|
||||
|
if (type) this.type = type; |
||||
|
if (dictionary) { |
||||
|
for(var p in dictionary) { |
||||
|
this[p] = dictionary[p]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Event.prototype = Object.create(Object.prototype, { |
||||
|
constructor: { value: Event }, |
||||
|
stopPropagation: { value: function stopPropagation() { |
||||
|
this._propagationStopped = true; |
||||
|
}}, |
||||
|
|
||||
|
stopImmediatePropagation: { value: function stopImmediatePropagation() { |
||||
|
this._propagationStopped = true; |
||||
|
this._immediatePropagationStopped = true; |
||||
|
}}, |
||||
|
|
||||
|
preventDefault: { value: function preventDefault() { |
||||
|
if (this.cancelable) this.defaultPrevented = true; |
||||
|
}}, |
||||
|
|
||||
|
initEvent: { value: function initEvent(type, bubbles, cancelable) { |
||||
|
this._initialized = true; |
||||
|
if (this._dispatching) return; |
||||
|
|
||||
|
this._propagationStopped = false; |
||||
|
this._immediatePropagationStopped = false; |
||||
|
this.defaultPrevented = false; |
||||
|
this.isTrusted = false; |
||||
|
|
||||
|
this.target = null; |
||||
|
this.type = type; |
||||
|
this.bubbles = bubbles; |
||||
|
this.cancelable = cancelable; |
||||
|
}}, |
||||
|
|
||||
|
}); |
@ -0,0 +1,298 @@ |
|||||
|
"use strict"; |
||||
|
var Event = require('./Event.js'); |
||||
|
var MouseEvent = require('./MouseEvent.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
module.exports = EventTarget; |
||||
|
|
||||
|
function EventTarget() {} |
||||
|
|
||||
|
EventTarget.prototype = { |
||||
|
// XXX
|
||||
|
// See WebIDL §4.8 for details on object event handlers
|
||||
|
// and how they should behave. We actually have to accept
|
||||
|
// any object to addEventListener... Can't type check it.
|
||||
|
// on registration.
|
||||
|
|
||||
|
// XXX:
|
||||
|
// Capturing event listeners are sort of rare. I think I can optimize
|
||||
|
// them so that dispatchEvent can skip the capturing phase (or much of
|
||||
|
// it). Each time a capturing listener is added, increment a flag on
|
||||
|
// the target node and each of its ancestors. Decrement when removed.
|
||||
|
// And update the counter when nodes are added and removed from the
|
||||
|
// tree as well. Then, in dispatch event, the capturing phase can
|
||||
|
// abort if it sees any node with a zero count.
|
||||
|
addEventListener: function addEventListener(type, listener, capture) { |
||||
|
if (!listener) return; |
||||
|
if (capture === undefined) capture = false; |
||||
|
if (!this._listeners) this._listeners = Object.create(null); |
||||
|
if (!this._listeners[type]) this._listeners[type] = []; |
||||
|
var list = this._listeners[type]; |
||||
|
|
||||
|
// If this listener has already been registered, just return
|
||||
|
for(var i = 0, n = list.length; i < n; i++) { |
||||
|
var l = list[i]; |
||||
|
if (l.listener === listener && l.capture === capture) |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Add an object to the list of listeners
|
||||
|
var obj = { listener: listener, capture: capture }; |
||||
|
if (typeof listener === 'function') obj.f = listener; |
||||
|
list.push(obj); |
||||
|
}, |
||||
|
|
||||
|
removeEventListener: function removeEventListener(type, |
||||
|
listener, |
||||
|
capture) { |
||||
|
if (capture === undefined) capture = false; |
||||
|
if (this._listeners) { |
||||
|
var list = this._listeners[type]; |
||||
|
if (list) { |
||||
|
// Find the listener in the list and remove it
|
||||
|
for(var i = 0, n = list.length; i < n; i++) { |
||||
|
var l = list[i]; |
||||
|
if (l.listener === listener && l.capture === capture) { |
||||
|
if (list.length === 1) { |
||||
|
this._listeners[type] = undefined; |
||||
|
} |
||||
|
else { |
||||
|
list.splice(i, 1); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// This is the public API for dispatching untrusted public events.
|
||||
|
// See _dispatchEvent for the implementation
|
||||
|
dispatchEvent: function dispatchEvent(event) { |
||||
|
// Dispatch an untrusted event
|
||||
|
return this._dispatchEvent(event, false); |
||||
|
}, |
||||
|
|
||||
|
//
|
||||
|
// See DOMCore §4.4
|
||||
|
// XXX: I'll probably need another version of this method for
|
||||
|
// internal use, one that does not set isTrusted to false.
|
||||
|
// XXX: see Document._dispatchEvent: perhaps that and this could
|
||||
|
// call a common internal function with different settings of
|
||||
|
// a trusted boolean argument
|
||||
|
//
|
||||
|
// XXX:
|
||||
|
// The spec has changed in how to deal with handlers registered
|
||||
|
// on idl or content attributes rather than with addEventListener.
|
||||
|
// Used to say that they always ran first. That's how webkit does it
|
||||
|
// Spec now says that they run in a position determined by
|
||||
|
// when they were first set. FF does it that way. See:
|
||||
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#event-handlers
|
||||
|
//
|
||||
|
_dispatchEvent: function _dispatchEvent(event, trusted) { |
||||
|
if (typeof trusted !== 'boolean') trusted = false; |
||||
|
function invoke(target, event) { |
||||
|
var type = event.type, phase = event.eventPhase; |
||||
|
event.currentTarget = target; |
||||
|
|
||||
|
// If there was an individual handler defined, invoke it first
|
||||
|
// XXX: see comment above: this shouldn't always be first.
|
||||
|
if (phase !== Event.CAPTURING_PHASE && |
||||
|
target._handlers && target._handlers[type]) |
||||
|
{ |
||||
|
var handler = target._handlers[type]; |
||||
|
var rv; |
||||
|
if (typeof handler === 'function') { |
||||
|
rv=handler.call(event.currentTarget, event); |
||||
|
} |
||||
|
else { |
||||
|
var f = handler.handleEvent; |
||||
|
if (typeof f !== 'function') |
||||
|
throw new TypeError('handleEvent property of ' + |
||||
|
'event handler object is' + |
||||
|
'not a function.'); |
||||
|
rv=f.call(handler, event); |
||||
|
} |
||||
|
|
||||
|
switch(event.type) { |
||||
|
case 'mouseover': |
||||
|
if (rv === true) // Historical baggage
|
||||
|
event.preventDefault(); |
||||
|
break; |
||||
|
case 'beforeunload': |
||||
|
// XXX: eventually we need a special case here
|
||||
|
/* falls through */ |
||||
|
default: |
||||
|
if (rv === false) |
||||
|
event.preventDefault(); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Now invoke list list of listeners for this target and type
|
||||
|
var list = target._listeners && target._listeners[type]; |
||||
|
if (!list) return; |
||||
|
list = list.slice(); |
||||
|
for(var i = 0, n = list.length; i < n; i++) { |
||||
|
if (event._immediatePropagationStopped) return; |
||||
|
var l = list[i]; |
||||
|
if ((phase === Event.CAPTURING_PHASE && !l.capture) || |
||||
|
(phase === Event.BUBBLING_PHASE && l.capture)) |
||||
|
continue; |
||||
|
if (l.f) { |
||||
|
l.f.call(event.currentTarget, event); |
||||
|
} |
||||
|
else { |
||||
|
var fn = l.listener.handleEvent; |
||||
|
if (typeof fn !== 'function') |
||||
|
throw new TypeError('handleEvent property of event listener object is not a function.'); |
||||
|
fn.call(l.listener, event); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!event._initialized || event._dispatching) utils.InvalidStateError(); |
||||
|
event.isTrusted = trusted; |
||||
|
|
||||
|
// Begin dispatching the event now
|
||||
|
event._dispatching = true; |
||||
|
event.target = this; |
||||
|
|
||||
|
// Build the list of targets for the capturing and bubbling phases
|
||||
|
// XXX: we'll eventually have to add Window to this list.
|
||||
|
var ancestors = []; |
||||
|
for(var n = this.parentNode; n; n = n.parentNode) |
||||
|
ancestors.push(n); |
||||
|
|
||||
|
// Capturing phase
|
||||
|
event.eventPhase = Event.CAPTURING_PHASE; |
||||
|
for(var i = ancestors.length-1; i >= 0; i--) { |
||||
|
invoke(ancestors[i], event); |
||||
|
if (event._propagationStopped) break; |
||||
|
} |
||||
|
|
||||
|
// At target phase
|
||||
|
if (!event._propagationStopped) { |
||||
|
event.eventPhase = Event.AT_TARGET; |
||||
|
invoke(this, event); |
||||
|
} |
||||
|
|
||||
|
// Bubbling phase
|
||||
|
if (event.bubbles && !event._propagationStopped) { |
||||
|
event.eventPhase = Event.BUBBLING_PHASE; |
||||
|
for(var ii = 0, nn = ancestors.length; ii < nn; ii++) { |
||||
|
invoke(ancestors[ii], event); |
||||
|
if (event._propagationStopped) break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
event._dispatching = false; |
||||
|
event.eventPhase = Event.AT_TARGET; |
||||
|
event.currentTarget = null; |
||||
|
|
||||
|
// Deal with mouse events and figure out when
|
||||
|
// a click has happened
|
||||
|
if (trusted && !event.defaultPrevented && event instanceof MouseEvent) { |
||||
|
switch(event.type) { |
||||
|
case 'mousedown': |
||||
|
this._armed = { |
||||
|
x: event.clientX, |
||||
|
y: event.clientY, |
||||
|
t: event.timeStamp |
||||
|
}; |
||||
|
break; |
||||
|
case 'mouseout': |
||||
|
case 'mouseover': |
||||
|
this._armed = null; |
||||
|
break; |
||||
|
case 'mouseup': |
||||
|
if (this._isClick(event)) this._doClick(event); |
||||
|
this._armed = null; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
return !event.defaultPrevented; |
||||
|
}, |
||||
|
|
||||
|
// Determine whether a click occurred
|
||||
|
// XXX We don't support double clicks for now
|
||||
|
_isClick: function(event) { |
||||
|
return (this._armed !== null && |
||||
|
event.type === 'mouseup' && |
||||
|
event.isTrusted && |
||||
|
event.button === 0 && |
||||
|
event.timeStamp - this._armed.t < 1000 && |
||||
|
Math.abs(event.clientX - this._armed.x) < 10 && |
||||
|
Math.abs(event.clientY - this._armed.Y) < 10); |
||||
|
}, |
||||
|
|
||||
|
// Clicks are handled like this:
|
||||
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#interactive-content-0
|
||||
|
//
|
||||
|
// Note that this method is similar to the HTMLElement.click() method
|
||||
|
// The event argument must be the trusted mouseup event
|
||||
|
_doClick: function(event) { |
||||
|
if (this._click_in_progress) return; |
||||
|
this._click_in_progress = true; |
||||
|
|
||||
|
// Find the nearest enclosing element that is activatable
|
||||
|
// An element is activatable if it has a
|
||||
|
// _post_click_activation_steps hook
|
||||
|
var activated = this; |
||||
|
while(activated && !activated._post_click_activation_steps) |
||||
|
activated = activated.parentNode; |
||||
|
|
||||
|
if (activated && activated._pre_click_activation_steps) { |
||||
|
activated._pre_click_activation_steps(); |
||||
|
} |
||||
|
|
||||
|
var click = this.ownerDocument.createEvent('MouseEvent'); |
||||
|
click.initMouseEvent('click', true, true, |
||||
|
this.ownerDocument.defaultView, 1, |
||||
|
event.screenX, event.screenY, |
||||
|
event.clientX, event.clientY, |
||||
|
event.ctrlKey, event.altKey, |
||||
|
event.shiftKey, event.metaKey, |
||||
|
event.button, null); |
||||
|
|
||||
|
var result = this._dispatchEvent(click, true); |
||||
|
|
||||
|
if (activated) { |
||||
|
if (result) { |
||||
|
// This is where hyperlinks get followed, for example.
|
||||
|
if (activated._post_click_activation_steps) |
||||
|
activated._post_click_activation_steps(click); |
||||
|
} |
||||
|
else { |
||||
|
if (activated._cancelled_activation_steps) |
||||
|
activated._cancelled_activation_steps(); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//
|
||||
|
// An event handler is like an event listener, but it registered
|
||||
|
// by setting an IDL or content attribute like onload or onclick.
|
||||
|
// There can only be one of these at a time for any event type.
|
||||
|
// This is an internal method for the attribute accessors and
|
||||
|
// content attribute handlers that need to register events handlers.
|
||||
|
// The type argument is the same as in addEventListener().
|
||||
|
// The handler argument is the same as listeners in addEventListener:
|
||||
|
// it can be a function or an object. Pass null to remove any existing
|
||||
|
// handler. Handlers are always invoked before any listeners of
|
||||
|
// the same type. They are not invoked during the capturing phase
|
||||
|
// of event dispatch.
|
||||
|
//
|
||||
|
_setEventHandler: function _setEventHandler(type, handler) { |
||||
|
if (!this._handlers) this._handlers = Object.create(null); |
||||
|
this._handlers[type] = handler; |
||||
|
}, |
||||
|
|
||||
|
_getEventHandler: function _getEventHandler(type) { |
||||
|
return (this._handlers && this._handlers[type]) || null; |
||||
|
} |
||||
|
|
||||
|
}; |
@ -0,0 +1,92 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = FilteredElementList; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
|
||||
|
//
|
||||
|
// This file defines node list implementation that lazily traverses
|
||||
|
// the document tree (or a subtree rooted at any element) and includes
|
||||
|
// only those elements for which a specified filter function returns true.
|
||||
|
// It is used to implement the
|
||||
|
// {Document,Element}.getElementsBy{TagName,ClassName}{,NS} methods.
|
||||
|
//
|
||||
|
// XXX this should inherit from NodeList
|
||||
|
|
||||
|
function FilteredElementList(root, filter) { |
||||
|
this.root = root; |
||||
|
this.filter = filter; |
||||
|
this.lastModTime = root.lastModTime; |
||||
|
this.done = false; |
||||
|
this.cache = []; |
||||
|
this.traverse(); |
||||
|
} |
||||
|
|
||||
|
FilteredElementList.prototype = Object.create(Object.prototype, { |
||||
|
length: { get: function() { |
||||
|
this.checkcache(); |
||||
|
if (!this.done) this.traverse(); |
||||
|
return this.cache.length; |
||||
|
} }, |
||||
|
|
||||
|
item: { value: function(n) { |
||||
|
this.checkcache(); |
||||
|
if (!this.done && n >= this.cache.length) { |
||||
|
// This can lead to O(N^2) behavior if we stop when we get to n
|
||||
|
// and the caller is iterating through the items in order; so
|
||||
|
// be sure to do the full traverse here.
|
||||
|
this.traverse(/*n*/); |
||||
|
} |
||||
|
return this.cache[n]; |
||||
|
} }, |
||||
|
|
||||
|
checkcache: { value: function() { |
||||
|
if (this.lastModTime !== this.root.lastModTime) { |
||||
|
// subtree has changed, so invalidate cache
|
||||
|
for (var i = this.cache.length-1; i>=0; i--) { |
||||
|
this[i] = undefined; |
||||
|
} |
||||
|
this.cache.length = 0; |
||||
|
this.done = false; |
||||
|
this.lastModTime = this.root.lastModTime; |
||||
|
} |
||||
|
} }, |
||||
|
|
||||
|
// If n is specified, then traverse the tree until we've found the nth
|
||||
|
// item (or until we've found all items). If n is not specified,
|
||||
|
// traverse until we've found all items.
|
||||
|
traverse: { value: function(n) { |
||||
|
// increment n so we can compare to length, and so it is never falsy
|
||||
|
if (n !== undefined) n++; |
||||
|
|
||||
|
var elt; |
||||
|
while ((elt = this.next()) !== null) { |
||||
|
this[this.cache.length] = elt; //XXX Use proxy instead
|
||||
|
this.cache.push(elt); |
||||
|
if (n && this.cache.length === n) return; |
||||
|
} |
||||
|
|
||||
|
// no next element, so we've found everything
|
||||
|
this.done = true; |
||||
|
} }, |
||||
|
|
||||
|
// Return the next element under root that matches filter
|
||||
|
next: { value: function() { |
||||
|
var start = (this.cache.length === 0) ? this.root // Start at the root or at
|
||||
|
: this.cache[this.cache.length-1]; // the last element we found
|
||||
|
|
||||
|
var elt; |
||||
|
if (start.nodeType === Node.DOCUMENT_NODE) |
||||
|
elt = start.documentElement; |
||||
|
else |
||||
|
elt = start.nextElement(this.root); |
||||
|
|
||||
|
while(elt) { |
||||
|
if (this.filter(elt)) { |
||||
|
return elt; |
||||
|
} |
||||
|
|
||||
|
elt = elt.nextElement(this.root); |
||||
|
} |
||||
|
return null; |
||||
|
} }, |
||||
|
}); |
7264
domino/HTMLParser.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,37 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Leaf; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var NodeList = require('./NodeList.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
var HierarchyRequestError = utils.HierarchyRequestError; |
||||
|
var NotFoundError = utils.NotFoundError; |
||||
|
|
||||
|
// This class defines common functionality for node subtypes that
|
||||
|
// can never have children
|
||||
|
function Leaf() { |
||||
|
Node.call(this); |
||||
|
} |
||||
|
|
||||
|
Leaf.prototype = Object.create(Node.prototype, { |
||||
|
hasChildNodes: { value: function() { return false; }}, |
||||
|
firstChild: { value: null }, |
||||
|
lastChild: { value: null }, |
||||
|
insertBefore: { value: function(node, child) { |
||||
|
if (!node.nodeType) throw new TypeError('not a node'); |
||||
|
HierarchyRequestError(); |
||||
|
}}, |
||||
|
replaceChild: { value: function(node, child) { |
||||
|
if (!node.nodeType) throw new TypeError('not a node'); |
||||
|
HierarchyRequestError(); |
||||
|
}}, |
||||
|
removeChild: { value: function(node) { |
||||
|
if (!node.nodeType) throw new TypeError('not a node'); |
||||
|
NotFoundError(); |
||||
|
}}, |
||||
|
removeChildren: { value: function() { /* no op */ }}, |
||||
|
childNodes: { get: function() { |
||||
|
if (!this._childNodes) this._childNodes = new NodeList(); |
||||
|
return this._childNodes; |
||||
|
}} |
||||
|
}); |
@ -0,0 +1,44 @@ |
|||||
|
"use strict"; |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
var LinkedList = module.exports = { |
||||
|
// basic validity tests on a circular linked list a
|
||||
|
valid: function(a) { |
||||
|
utils.assert(a, "list falsy"); |
||||
|
utils.assert(a._previousSibling, "previous falsy"); |
||||
|
utils.assert(a._nextSibling, "next falsy"); |
||||
|
// xxx check that list is actually circular
|
||||
|
return true; |
||||
|
}, |
||||
|
// insert a before b
|
||||
|
insertBefore: function(a, b) { |
||||
|
utils.assert(LinkedList.valid(a) && LinkedList.valid(b)); |
||||
|
var a_first = a, a_last = a._previousSibling; |
||||
|
var b_first = b, b_last = b._previousSibling; |
||||
|
a_first._previousSibling = b_last; |
||||
|
a_last._nextSibling = b_first; |
||||
|
b_last._nextSibling = a_first; |
||||
|
b_first._previousSibling = a_last; |
||||
|
utils.assert(LinkedList.valid(a) && LinkedList.valid(b)); |
||||
|
}, |
||||
|
// replace a single node a with a list b (which could be null)
|
||||
|
replace: function(a, b) { |
||||
|
utils.assert(LinkedList.valid(a) && (b===null || LinkedList.valid(b))); |
||||
|
if (b!==null) { |
||||
|
LinkedList.insertBefore(b, a); |
||||
|
} |
||||
|
LinkedList.remove(a); |
||||
|
utils.assert(LinkedList.valid(a) && (b===null || LinkedList.valid(b))); |
||||
|
}, |
||||
|
// remove single node a from its list
|
||||
|
remove: function(a) { |
||||
|
utils.assert(LinkedList.valid(a)); |
||||
|
var prev = a._previousSibling; |
||||
|
if (prev === a) { return; } |
||||
|
var next = a._nextSibling; |
||||
|
prev._nextSibling = next; |
||||
|
next._previousSibling = prev; |
||||
|
a._previousSibling = a._nextSibling = a; |
||||
|
utils.assert(LinkedList.valid(a)); |
||||
|
} |
||||
|
}; |
@ -0,0 +1,56 @@ |
|||||
|
"use strict"; |
||||
|
var URL = require('./URL.js'); |
||||
|
var URLUtils = require('./URLUtils.js'); |
||||
|
|
||||
|
module.exports = Location; |
||||
|
|
||||
|
function Location(window, href) { |
||||
|
this._window = window; |
||||
|
this._href = href; |
||||
|
} |
||||
|
|
||||
|
Location.prototype = Object.create(URLUtils.prototype, { |
||||
|
constructor: { value: Location }, |
||||
|
|
||||
|
// Special behavior when href is set
|
||||
|
href: { |
||||
|
get: function() { return this._href; }, |
||||
|
set: function(v) { this.assign(v); } |
||||
|
}, |
||||
|
|
||||
|
assign: { value: function(url) { |
||||
|
// Resolve the new url against the current one
|
||||
|
// XXX:
|
||||
|
// This is not actually correct. It should be resolved against
|
||||
|
// the URL of the document of the script. For now, though, I only
|
||||
|
// support a single window and there is only one base url.
|
||||
|
// So this is good enough for now.
|
||||
|
var current = new URL(this._href); |
||||
|
var newurl = current.resolve(url); |
||||
|
|
||||
|
// Save the new url
|
||||
|
this._href = newurl; |
||||
|
|
||||
|
// Start loading the new document!
|
||||
|
// XXX
|
||||
|
// This is just something hacked together.
|
||||
|
// The real algorithm is: http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#navigate
|
||||
|
}}, |
||||
|
|
||||
|
replace: { value: function(url) { |
||||
|
// XXX
|
||||
|
// Since we aren't tracking history yet, replace is the same as assign
|
||||
|
this.assign(url); |
||||
|
}}, |
||||
|
|
||||
|
reload: { value: function() { |
||||
|
// XXX:
|
||||
|
// Actually, the spec is a lot more complicated than this
|
||||
|
this.assign(this.href); |
||||
|
}}, |
||||
|
|
||||
|
toString: { value: function() { |
||||
|
return this.href; |
||||
|
}} |
||||
|
|
||||
|
}); |
@ -0,0 +1,52 @@ |
|||||
|
"use strict"; |
||||
|
var UIEvent = require('./UIEvent.js'); |
||||
|
|
||||
|
module.exports = MouseEvent; |
||||
|
|
||||
|
function MouseEvent() { |
||||
|
// Just use the superclass constructor to initialize
|
||||
|
UIEvent.call(this); |
||||
|
|
||||
|
this.screenX = this.screenY = this.clientX = this.clientY = 0; |
||||
|
this.ctrlKey = this.altKey = this.shiftKey = this.metaKey = false; |
||||
|
this.button = 0; |
||||
|
this.buttons = 1; |
||||
|
this.relatedTarget = null; |
||||
|
} |
||||
|
MouseEvent.prototype = Object.create(UIEvent.prototype, { |
||||
|
constructor: { value: MouseEvent }, |
||||
|
initMouseEvent: { value: function(type, bubbles, cancelable, |
||||
|
view, detail, |
||||
|
screenX, screenY, clientX, clientY, |
||||
|
ctrlKey, altKey, shiftKey, metaKey, |
||||
|
button, relatedTarget) { |
||||
|
|
||||
|
this.initEvent(type, bubbles, cancelable, view, detail); |
||||
|
this.screenX = screenX; |
||||
|
this.screenY = screenY; |
||||
|
this.clientX = clientX; |
||||
|
this.clientY = clientY; |
||||
|
this.ctrlKey = ctrlKey; |
||||
|
this.altKey = altKey; |
||||
|
this.shiftKey = shiftKey; |
||||
|
this.metaKey = metaKey; |
||||
|
this.button = button; |
||||
|
switch(button) { |
||||
|
case 0: this.buttons = 1; break; |
||||
|
case 1: this.buttons = 4; break; |
||||
|
case 2: this.buttons = 2; break; |
||||
|
default: this.buttons = 0; break; |
||||
|
} |
||||
|
this.relatedTarget = relatedTarget; |
||||
|
}}, |
||||
|
|
||||
|
getModifierState: { value: function(key) { |
||||
|
switch(key) { |
||||
|
case "Alt": return this.altKey; |
||||
|
case "Control": return this.ctrlKey; |
||||
|
case "Shift": return this.shiftKey; |
||||
|
case "Meta": return this.metaKey; |
||||
|
default: return false; |
||||
|
} |
||||
|
}} |
||||
|
}); |
@ -0,0 +1,9 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = { |
||||
|
VALUE: 1, // The value of a Text, Comment or PI node changed
|
||||
|
ATTR: 2, // A new attribute was added or an attribute value and/or prefix changed
|
||||
|
REMOVE_ATTR: 3, // An attribute was removed
|
||||
|
REMOVE: 4, // A node was removed
|
||||
|
MOVE: 5, // A node was moved
|
||||
|
INSERT: 6 // A node (or a subtree of nodes) was inserted
|
||||
|
}; |
@ -0,0 +1,41 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = NamedNodeMap; |
||||
|
|
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
/* This is a hacky implementation of NamedNodeMap, intended primarily to |
||||
|
* satisfy clients (like dompurify and the web-platform-tests) which check |
||||
|
* to ensure that Node#attributes instanceof NamedNodeMap. */ |
||||
|
|
||||
|
function NamedNodeMap(element) { |
||||
|
this.element = element; |
||||
|
} |
||||
|
Object.defineProperties(NamedNodeMap.prototype, { |
||||
|
length: { get: utils.shouldOverride }, |
||||
|
item: { value: utils.shouldOverride }, |
||||
|
|
||||
|
getNamedItem: { value: function getNamedItem(qualifiedName) { |
||||
|
return this.element.getAttributeNode(qualifiedName); |
||||
|
} }, |
||||
|
getNamedItemNS: { value: function getNamedItemNS(namespace, localName) { |
||||
|
return this.element.getAttributeNodeNS(namespace, localName); |
||||
|
} }, |
||||
|
setNamedItem: { value: utils.nyi }, |
||||
|
setNamedItemNS: { value: utils.nyi }, |
||||
|
removeNamedItem: { value: function removeNamedItem(qualifiedName) { |
||||
|
var attr = this.element.getAttributeNode(qualifiedName); |
||||
|
if (attr) { |
||||
|
this.element.removeAttribute(qualifiedName); |
||||
|
return attr; |
||||
|
} |
||||
|
utils.NotFoundError(); |
||||
|
} }, |
||||
|
removeNamedItemNS: { value: function removeNamedItemNS(ns, lname) { |
||||
|
var attr = this.element.getAttributeNodeNS(ns, lname); |
||||
|
if (attr) { |
||||
|
this.element.removeAttributeNS(ns, lname); |
||||
|
return attr; |
||||
|
} |
||||
|
utils.NotFoundError(); |
||||
|
} }, |
||||
|
}); |
@ -0,0 +1,17 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
// https://html.spec.whatwg.org/multipage/webappapis.html#navigatorid
|
||||
|
var NavigatorID = Object.create(null, { |
||||
|
appCodeName: { value: "Mozilla" }, |
||||
|
appName: { value: "Netscape" }, |
||||
|
appVersion: { value: "4.0" }, |
||||
|
platform: { value: "" }, |
||||
|
product: { value: "Gecko" }, |
||||
|
productSub: { value: "20100101" }, |
||||
|
userAgent: { value: "" }, |
||||
|
vendor: { value: "" }, |
||||
|
vendorSub: { value: "" }, |
||||
|
taintEnabled: { value: function() { return false; } } |
||||
|
}); |
||||
|
|
||||
|
module.exports = NavigatorID; |
@ -0,0 +1,738 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Node; |
||||
|
|
||||
|
var EventTarget = require('./EventTarget.js'); |
||||
|
var LinkedList = require('./LinkedList.js'); |
||||
|
var NodeUtils = require('./NodeUtils.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
// All nodes have a nodeType and an ownerDocument.
|
||||
|
// Once inserted, they also have a parentNode.
|
||||
|
// This is an abstract class; all nodes in a document are instances
|
||||
|
// of a subtype, so all the properties are defined by more specific
|
||||
|
// constructors.
|
||||
|
function Node() { |
||||
|
EventTarget.call(this); |
||||
|
this.parentNode = null; |
||||
|
this._nextSibling = this._previousSibling = this; |
||||
|
this._index = undefined; |
||||
|
} |
||||
|
|
||||
|
var ELEMENT_NODE = Node.ELEMENT_NODE = 1; |
||||
|
var ATTRIBUTE_NODE = Node.ATTRIBUTE_NODE = 2; |
||||
|
var TEXT_NODE = Node.TEXT_NODE = 3; |
||||
|
var CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE = 4; |
||||
|
var ENTITY_REFERENCE_NODE = Node.ENTITY_REFERENCE_NODE = 5; |
||||
|
var ENTITY_NODE = Node.ENTITY_NODE = 6; |
||||
|
var PROCESSING_INSTRUCTION_NODE = Node.PROCESSING_INSTRUCTION_NODE = 7; |
||||
|
var COMMENT_NODE = Node.COMMENT_NODE = 8; |
||||
|
var DOCUMENT_NODE = Node.DOCUMENT_NODE = 9; |
||||
|
var DOCUMENT_TYPE_NODE = Node.DOCUMENT_TYPE_NODE = 10; |
||||
|
var DOCUMENT_FRAGMENT_NODE = Node.DOCUMENT_FRAGMENT_NODE = 11; |
||||
|
var NOTATION_NODE = Node.NOTATION_NODE = 12; |
||||
|
|
||||
|
var DOCUMENT_POSITION_DISCONNECTED = Node.DOCUMENT_POSITION_DISCONNECTED = 0x01; |
||||
|
var DOCUMENT_POSITION_PRECEDING = Node.DOCUMENT_POSITION_PRECEDING = 0x02; |
||||
|
var DOCUMENT_POSITION_FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING = 0x04; |
||||
|
var DOCUMENT_POSITION_CONTAINS = Node.DOCUMENT_POSITION_CONTAINS = 0x08; |
||||
|
var DOCUMENT_POSITION_CONTAINED_BY = Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10; |
||||
|
var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; |
||||
|
|
||||
|
Node.prototype = Object.create(EventTarget.prototype, { |
||||
|
|
||||
|
// Node that are not inserted into the tree inherit a null parent
|
||||
|
|
||||
|
// XXX: the baseURI attribute is defined by dom core, but
|
||||
|
// a correct implementation of it requires HTML features, so
|
||||
|
// we'll come back to this later.
|
||||
|
baseURI: { get: utils.nyi }, |
||||
|
|
||||
|
parentElement: { get: function() { |
||||
|
return (this.parentNode && this.parentNode.nodeType===ELEMENT_NODE) ? this.parentNode : null; |
||||
|
}}, |
||||
|
|
||||
|
hasChildNodes: { value: utils.shouldOverride }, |
||||
|
|
||||
|
firstChild: { get: utils.shouldOverride }, |
||||
|
|
||||
|
lastChild: { get: utils.shouldOverride }, |
||||
|
|
||||
|
previousSibling: { get: function() { |
||||
|
var parent = this.parentNode; |
||||
|
if (!parent) return null; |
||||
|
if (this === parent.firstChild) return null; |
||||
|
return this._previousSibling; |
||||
|
}}, |
||||
|
|
||||
|
nextSibling: { get: function() { |
||||
|
var parent = this.parentNode, next = this._nextSibling; |
||||
|
if (!parent) return null; |
||||
|
if (next === parent.firstChild) return null; |
||||
|
return next; |
||||
|
}}, |
||||
|
|
||||
|
textContent: { |
||||
|
// Should override for DocumentFragment/Element/Attr/Text/PI/Comment
|
||||
|
get: function() { return null; }, |
||||
|
set: function(v) { /* do nothing */ }, |
||||
|
}, |
||||
|
|
||||
|
_countChildrenOfType: { value: function(type) { |
||||
|
var sum = 0; |
||||
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === type) sum++; |
||||
|
} |
||||
|
return sum; |
||||
|
}}, |
||||
|
|
||||
|
_ensureInsertValid: { value: function _ensureInsertValid(node, child, isPreinsert) { |
||||
|
var parent = this, i, kid; |
||||
|
if (!node.nodeType) throw new TypeError('not a node'); |
||||
|
// 1. If parent is not a Document, DocumentFragment, or Element
|
||||
|
// node, throw a HierarchyRequestError.
|
||||
|
switch (parent.nodeType) { |
||||
|
case DOCUMENT_NODE: |
||||
|
case DOCUMENT_FRAGMENT_NODE: |
||||
|
case ELEMENT_NODE: |
||||
|
break; |
||||
|
default: utils.HierarchyRequestError(); |
||||
|
} |
||||
|
// 2. If node is a host-including inclusive ancestor of parent,
|
||||
|
// throw a HierarchyRequestError.
|
||||
|
if (node.isAncestor(parent)) utils.HierarchyRequestError(); |
||||
|
// 3. If child is not null and its parent is not parent, then
|
||||
|
// throw a NotFoundError. (replaceChild omits the 'child is not null'
|
||||
|
// and throws a TypeError here if child is null.)
|
||||
|
if (child !== null || !isPreinsert) { |
||||
|
if (child.parentNode !== parent) utils.NotFoundError(); |
||||
|
} |
||||
|
// 4. If node is not a DocumentFragment, DocumentType, Element,
|
||||
|
// Text, ProcessingInstruction, or Comment node, throw a
|
||||
|
// HierarchyRequestError.
|
||||
|
switch (node.nodeType) { |
||||
|
case DOCUMENT_FRAGMENT_NODE: |
||||
|
case DOCUMENT_TYPE_NODE: |
||||
|
case ELEMENT_NODE: |
||||
|
case TEXT_NODE: |
||||
|
case PROCESSING_INSTRUCTION_NODE: |
||||
|
case COMMENT_NODE: |
||||
|
break; |
||||
|
default: utils.HierarchyRequestError(); |
||||
|
} |
||||
|
// 5. If either node is a Text node and parent is a document, or
|
||||
|
// node is a doctype and parent is not a document, throw a
|
||||
|
// HierarchyRequestError.
|
||||
|
// 6. If parent is a document, and any of the statements below, switched
|
||||
|
// on node, are true, throw a HierarchyRequestError.
|
||||
|
if (parent.nodeType === DOCUMENT_NODE) { |
||||
|
switch (node.nodeType) { |
||||
|
case TEXT_NODE: |
||||
|
utils.HierarchyRequestError(); |
||||
|
break; |
||||
|
case DOCUMENT_FRAGMENT_NODE: |
||||
|
// 6a1. If node has more than one element child or has a Text
|
||||
|
// node child.
|
||||
|
if (node._countChildrenOfType(TEXT_NODE) > 0) |
||||
|
utils.HierarchyRequestError(); |
||||
|
switch (node._countChildrenOfType(ELEMENT_NODE)) { |
||||
|
case 0: |
||||
|
break; |
||||
|
case 1: |
||||
|
// 6a2. Otherwise, if node has one element child and either
|
||||
|
// parent has an element child, child is a doctype, or child
|
||||
|
// is not null and a doctype is following child. [preinsert]
|
||||
|
// 6a2. Otherwise, if node has one element child and either
|
||||
|
// parent has an element child that is not child or a
|
||||
|
// doctype is following child. [replaceWith]
|
||||
|
if (child !== null /* always true here for replaceWith */) { |
||||
|
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE) |
||||
|
utils.HierarchyRequestError(); |
||||
|
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === DOCUMENT_TYPE_NODE) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
} |
||||
|
i = parent._countChildrenOfType(ELEMENT_NODE); |
||||
|
if (isPreinsert) { |
||||
|
// "parent has an element child"
|
||||
|
if (i > 0) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} else { |
||||
|
// "parent has an element child that is not child"
|
||||
|
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE)) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
break; |
||||
|
default: // 6a1, continued. (more than one Element child)
|
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
break; |
||||
|
case ELEMENT_NODE: |
||||
|
// 6b. parent has an element child, child is a doctype, or
|
||||
|
// child is not null and a doctype is following child. [preinsert]
|
||||
|
// 6b. parent has an element child that is not child or a
|
||||
|
// doctype is following child. [replaceWith]
|
||||
|
if (child !== null /* always true here for replaceWith */) { |
||||
|
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE) |
||||
|
utils.HierarchyRequestError(); |
||||
|
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === DOCUMENT_TYPE_NODE) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
} |
||||
|
i = parent._countChildrenOfType(ELEMENT_NODE); |
||||
|
if (isPreinsert) { |
||||
|
// "parent has an element child"
|
||||
|
if (i > 0) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} else { |
||||
|
// "parent has an element child that is not child"
|
||||
|
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE)) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
break; |
||||
|
case DOCUMENT_TYPE_NODE: |
||||
|
// 6c. parent has a doctype child, child is non-null and an
|
||||
|
// element is preceding child, or child is null and parent has
|
||||
|
// an element child. [preinsert]
|
||||
|
// 6c. parent has a doctype child that is not child, or an
|
||||
|
// element is preceding child. [replaceWith]
|
||||
|
if (child === null) { |
||||
|
if (parent._countChildrenOfType(ELEMENT_NODE)) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} else { |
||||
|
// child is always non-null for [replaceWith] case
|
||||
|
for (kid = parent.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid === child) break; |
||||
|
if (kid.nodeType === ELEMENT_NODE) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
} |
||||
|
i = parent._countChildrenOfType(DOCUMENT_TYPE_NODE); |
||||
|
if (isPreinsert) { |
||||
|
// "parent has an doctype child"
|
||||
|
if (i > 0) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} else { |
||||
|
// "parent has an doctype child that is not child"
|
||||
|
if (i > 1 || (i === 1 && child.nodeType !== DOCUMENT_TYPE_NODE)) |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} else { |
||||
|
// 5, continued: (parent is not a document)
|
||||
|
if (node.nodeType === DOCUMENT_TYPE_NODE) utils.HierarchyRequestError(); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
insertBefore: { value: function insertBefore(node, child) { |
||||
|
var parent = this; |
||||
|
// 1. Ensure pre-insertion validity
|
||||
|
parent._ensureInsertValid(node, child, true); |
||||
|
// 2. Let reference child be child.
|
||||
|
var refChild = child; |
||||
|
// 3. If reference child is node, set it to node's next sibling
|
||||
|
if (refChild === node) { refChild = node.nextSibling; } |
||||
|
// 4. Adopt node into parent's node document.
|
||||
|
parent.doc.adoptNode(node); |
||||
|
// 5. Insert node into parent before reference child.
|
||||
|
node._insertOrReplace(parent, refChild, false); |
||||
|
// 6. Return node
|
||||
|
return node; |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
appendChild: { value: function(child) { |
||||
|
// This invokes _appendChild after doing validity checks.
|
||||
|
return this.insertBefore(child, null); |
||||
|
}}, |
||||
|
|
||||
|
_appendChild: { value: function(child) { |
||||
|
child._insertOrReplace(this, null, false); |
||||
|
}}, |
||||
|
|
||||
|
removeChild: { value: function removeChild(child) { |
||||
|
var parent = this; |
||||
|
if (!child.nodeType) throw new TypeError('not a node'); |
||||
|
if (child.parentNode !== parent) utils.NotFoundError(); |
||||
|
child.remove(); |
||||
|
return child; |
||||
|
}}, |
||||
|
|
||||
|
// To replace a `child` with `node` within a `parent` (this)
|
||||
|
replaceChild: { value: function replaceChild(node, child) { |
||||
|
var parent = this; |
||||
|
// Ensure validity (slight differences from pre-insertion check)
|
||||
|
parent._ensureInsertValid(node, child, false); |
||||
|
// Adopt node into parent's node document.
|
||||
|
if (node.doc !== parent.doc) { |
||||
|
// XXX adoptNode has side-effect of removing node from its parent
|
||||
|
// and generating a mutation event, thus causing the _insertOrReplace
|
||||
|
// to generate two deletes and an insert instead of a 'move'
|
||||
|
// event. It looks like the new MutationObserver stuff avoids
|
||||
|
// this problem, but for now let's only adopt (ie, remove `node`
|
||||
|
// from its parent) here if we need to.
|
||||
|
parent.doc.adoptNode(node); |
||||
|
} |
||||
|
// Do the replace.
|
||||
|
node._insertOrReplace(parent, child, true); |
||||
|
return child; |
||||
|
}}, |
||||
|
|
||||
|
// See: http://ejohn.org/blog/comparing-document-position/
|
||||
|
contains: { value: function contains(node) { |
||||
|
if (node === null) { return false; } |
||||
|
if (this === node) { return true; /* inclusive descendant */ } |
||||
|
/* jshint bitwise: false */ |
||||
|
return (this.compareDocumentPosition(node) & |
||||
|
DOCUMENT_POSITION_CONTAINED_BY) !== 0; |
||||
|
}}, |
||||
|
|
||||
|
compareDocumentPosition: { value: function compareDocumentPosition(that){ |
||||
|
// Basic algorithm for finding the relative position of two nodes.
|
||||
|
// Make a list the ancestors of each node, starting with the
|
||||
|
// document element and proceeding down to the nodes themselves.
|
||||
|
// Then, loop through the lists, looking for the first element
|
||||
|
// that differs. The order of those two elements give the
|
||||
|
// order of their descendant nodes. Or, if one list is a prefix
|
||||
|
// of the other one, then that node contains the other.
|
||||
|
|
||||
|
if (this === that) return 0; |
||||
|
|
||||
|
// If they're not owned by the same document or if one is rooted
|
||||
|
// and one is not, then they're disconnected.
|
||||
|
if (this.doc !== that.doc || |
||||
|
this.rooted !== that.rooted) |
||||
|
return (DOCUMENT_POSITION_DISCONNECTED + |
||||
|
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); |
||||
|
|
||||
|
// Get arrays of ancestors for this and that
|
||||
|
var these = [], those = []; |
||||
|
for(var n = this; n !== null; n = n.parentNode) these.push(n); |
||||
|
for(n = that; n !== null; n = n.parentNode) those.push(n); |
||||
|
these.reverse(); // So we start with the outermost
|
||||
|
those.reverse(); |
||||
|
|
||||
|
if (these[0] !== those[0]) // No common ancestor
|
||||
|
return (DOCUMENT_POSITION_DISCONNECTED + |
||||
|
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); |
||||
|
|
||||
|
n = Math.min(these.length, those.length); |
||||
|
for(var i = 1; i < n; i++) { |
||||
|
if (these[i] !== those[i]) { |
||||
|
// We found two different ancestors, so compare
|
||||
|
// their positions
|
||||
|
if (these[i].index < those[i].index) |
||||
|
return DOCUMENT_POSITION_FOLLOWING; |
||||
|
else |
||||
|
return DOCUMENT_POSITION_PRECEDING; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// If we get to here, then one of the nodes (the one with the
|
||||
|
// shorter list of ancestors) contains the other one.
|
||||
|
if (these.length < those.length) |
||||
|
return (DOCUMENT_POSITION_FOLLOWING + |
||||
|
DOCUMENT_POSITION_CONTAINED_BY); |
||||
|
else |
||||
|
return (DOCUMENT_POSITION_PRECEDING + |
||||
|
DOCUMENT_POSITION_CONTAINS); |
||||
|
}}, |
||||
|
|
||||
|
isSameNode: {value : function isSameNode(node) { |
||||
|
return this === node; |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// This method implements the generic parts of node equality testing
|
||||
|
// and defers to the (non-recursive) type-specific isEqual() method
|
||||
|
// defined by subclasses
|
||||
|
isEqualNode: { value: function isEqualNode(node) { |
||||
|
if (!node) return false; |
||||
|
if (node.nodeType !== this.nodeType) return false; |
||||
|
|
||||
|
// Check type-specific properties for equality
|
||||
|
if (!this.isEqual(node)) return false; |
||||
|
|
||||
|
// Now check children for number and equality
|
||||
|
for (var c1 = this.firstChild, c2 = node.firstChild; |
||||
|
c1 && c2; |
||||
|
c1 = c1.nextSibling, c2 = c2.nextSibling) { |
||||
|
if (!c1.isEqualNode(c2)) return false; |
||||
|
} |
||||
|
return c1 === null && c2 === null; |
||||
|
}}, |
||||
|
|
||||
|
// This method delegates shallow cloning to a clone() method
|
||||
|
// that each concrete subclass must implement
|
||||
|
cloneNode: { value: function(deep) { |
||||
|
// Clone this node
|
||||
|
var clone = this.clone(); |
||||
|
|
||||
|
// Handle the recursive case if necessary
|
||||
|
if (deep) { |
||||
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
clone._appendChild(kid.cloneNode(true)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return clone; |
||||
|
}}, |
||||
|
|
||||
|
lookupPrefix: { value: function lookupPrefix(ns) { |
||||
|
var e; |
||||
|
if (ns === '' || ns === null || ns === undefined) return null; |
||||
|
switch(this.nodeType) { |
||||
|
case ELEMENT_NODE: |
||||
|
return this._lookupNamespacePrefix(ns, this); |
||||
|
case DOCUMENT_NODE: |
||||
|
e = this.documentElement; |
||||
|
return e ? e.lookupPrefix(ns) : null; |
||||
|
case ENTITY_NODE: |
||||
|
case NOTATION_NODE: |
||||
|
case DOCUMENT_FRAGMENT_NODE: |
||||
|
case DOCUMENT_TYPE_NODE: |
||||
|
return null; |
||||
|
case ATTRIBUTE_NODE: |
||||
|
e = this.ownerElement; |
||||
|
return e ? e.lookupPrefix(ns) : null; |
||||
|
default: |
||||
|
e = this.parentElement; |
||||
|
return e ? e.lookupPrefix(ns) : null; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
lookupNamespaceURI: {value: function lookupNamespaceURI(prefix) { |
||||
|
if (prefix === '' || prefix === undefined) { prefix = null; } |
||||
|
var e; |
||||
|
switch(this.nodeType) { |
||||
|
case ELEMENT_NODE: |
||||
|
return utils.shouldOverride(); |
||||
|
case DOCUMENT_NODE: |
||||
|
e = this.documentElement; |
||||
|
return e ? e.lookupNamespaceURI(prefix) : null; |
||||
|
case ENTITY_NODE: |
||||
|
case NOTATION_NODE: |
||||
|
case DOCUMENT_TYPE_NODE: |
||||
|
case DOCUMENT_FRAGMENT_NODE: |
||||
|
return null; |
||||
|
case ATTRIBUTE_NODE: |
||||
|
e = this.ownerElement; |
||||
|
return e ? e.lookupNamespaceURI(prefix) : null; |
||||
|
default: |
||||
|
e = this.parentElement; |
||||
|
return e ? e.lookupNamespaceURI(prefix) : null; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
isDefaultNamespace: { value: function isDefaultNamespace(ns) { |
||||
|
if (ns === '' || ns === undefined) { ns = null; } |
||||
|
var defaultNamespace = this.lookupNamespaceURI(null); |
||||
|
return (defaultNamespace === ns); |
||||
|
}}, |
||||
|
|
||||
|
// Utility methods for nodes. Not part of the DOM
|
||||
|
|
||||
|
// Return the index of this node in its parent.
|
||||
|
// Throw if no parent, or if this node is not a child of its parent
|
||||
|
index: { get: function() { |
||||
|
var parent = this.parentNode; |
||||
|
if (this === parent.firstChild) return 0; // fast case
|
||||
|
var kids = parent.childNodes; |
||||
|
if (this._index === undefined || kids[this._index] !== this) { |
||||
|
// Ensure that we don't have an O(N^2) blowup if none of the
|
||||
|
// kids have defined indices yet and we're traversing via
|
||||
|
// nextSibling or previousSibling
|
||||
|
for (var i=0; i<kids.length; i++) { |
||||
|
kids[i]._index = i; |
||||
|
} |
||||
|
utils.assert(kids[this._index] === this); |
||||
|
} |
||||
|
return this._index; |
||||
|
}}, |
||||
|
|
||||
|
// Return true if this node is equal to or is an ancestor of that node
|
||||
|
// Note that nodes are considered to be ancestors of themselves
|
||||
|
isAncestor: { value: function(that) { |
||||
|
// If they belong to different documents, then they're unrelated.
|
||||
|
if (this.doc !== that.doc) return false; |
||||
|
// If one is rooted and one isn't then they're not related
|
||||
|
if (this.rooted !== that.rooted) return false; |
||||
|
|
||||
|
// Otherwise check by traversing the parentNode chain
|
||||
|
for(var e = that; e; e = e.parentNode) { |
||||
|
if (e === this) return true; |
||||
|
} |
||||
|
return false; |
||||
|
}}, |
||||
|
|
||||
|
// DOMINO Changed the behavior to conform with the specs. See:
|
||||
|
// https://groups.google.com/d/topic/mozilla.dev.platform/77sIYcpdDmc/discussion
|
||||
|
ensureSameDoc: { value: function(that) { |
||||
|
if (that.ownerDocument === null) { |
||||
|
that.ownerDocument = this.doc; |
||||
|
} |
||||
|
else if(that.ownerDocument !== this.doc) { |
||||
|
utils.WrongDocumentError(); |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
removeChildren: { value: utils.shouldOverride }, |
||||
|
|
||||
|
// Insert this node as a child of parent before the specified child,
|
||||
|
// or insert as the last child of parent if specified child is null,
|
||||
|
// or replace the specified child with this node, firing mutation events as
|
||||
|
// necessary
|
||||
|
_insertOrReplace: { value: function _insertOrReplace(parent, before, isReplace) { |
||||
|
var child = this, before_index, i; |
||||
|
|
||||
|
if (child.nodeType === DOCUMENT_FRAGMENT_NODE && child.rooted) { |
||||
|
utils.HierarchyRequestError(); |
||||
|
} |
||||
|
|
||||
|
/* Ensure index of `before` is cached before we (possibly) remove it. */ |
||||
|
if (parent._childNodes) { |
||||
|
before_index = (before === null) ? parent._childNodes.length : |
||||
|
before.index; /* ensure _index is cached */ |
||||
|
|
||||
|
// If we are already a child of the specified parent, then
|
||||
|
// the index may have to be adjusted.
|
||||
|
if (child.parentNode === parent) { |
||||
|
var child_index = child.index; |
||||
|
// If the child is before the spot it is to be inserted at,
|
||||
|
// then when it is removed, the index of that spot will be
|
||||
|
// reduced.
|
||||
|
if (child_index < before_index) { |
||||
|
before_index--; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Delete the old child
|
||||
|
if (isReplace) { |
||||
|
if (before.rooted) before.doc.mutateRemove(before); |
||||
|
before.parentNode = null; |
||||
|
} |
||||
|
|
||||
|
var n = before; |
||||
|
if (n === null) { n = parent.firstChild; } |
||||
|
|
||||
|
// If both the child and the parent are rooted, then we want to
|
||||
|
// transplant the child without uprooting and rerooting it.
|
||||
|
var bothRooted = child.rooted && parent.rooted; |
||||
|
if (child.nodeType === DOCUMENT_FRAGMENT_NODE) { |
||||
|
var spliceArgs = [0, isReplace ? 1 : 0], next; |
||||
|
for (var kid = child.firstChild; kid !== null; kid = next) { |
||||
|
next = kid.nextSibling; |
||||
|
spliceArgs.push(kid); |
||||
|
kid.parentNode = parent; |
||||
|
} |
||||
|
var len = spliceArgs.length; |
||||
|
// Add all nodes to the new parent, overwriting the old child
|
||||
|
if (isReplace) { |
||||
|
LinkedList.replace(n, len > 2 ? spliceArgs[2] : null); |
||||
|
} else if (len > 2 && n !== null) { |
||||
|
LinkedList.insertBefore(spliceArgs[2], n); |
||||
|
} |
||||
|
if (parent._childNodes) { |
||||
|
spliceArgs[0] = (before === null) ? |
||||
|
parent._childNodes.length : before._index; |
||||
|
parent._childNodes.splice.apply(parent._childNodes, spliceArgs); |
||||
|
for (i=2; i<len; i++) { |
||||
|
spliceArgs[i]._index = spliceArgs[0] + (i - 2); |
||||
|
} |
||||
|
} else if (parent._firstChild === before) { |
||||
|
if (len > 2) { |
||||
|
parent._firstChild = spliceArgs[2]; |
||||
|
} else if (isReplace) { |
||||
|
parent._firstChild = null; |
||||
|
} |
||||
|
} |
||||
|
// Remove all nodes from the document fragment
|
||||
|
if (child._childNodes) { |
||||
|
child._childNodes.length = 0; |
||||
|
} else { |
||||
|
child._firstChild = null; |
||||
|
} |
||||
|
// Call the mutation handlers
|
||||
|
// Use spliceArgs since the original array has been destroyed. The
|
||||
|
// liveness guarantee requires us to clone the array so that
|
||||
|
// references to the childNodes of the DocumentFragment will be empty
|
||||
|
// when the insertion handlers are called.
|
||||
|
if (parent.rooted) { |
||||
|
parent.modify(); |
||||
|
for (i = 2; i < len; i++) { |
||||
|
parent.doc.mutateInsert(spliceArgs[i]); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
if (before === child) { return; } |
||||
|
if (bothRooted) { |
||||
|
// Remove the child from its current position in the tree
|
||||
|
// without calling remove(), since we don't want to uproot it.
|
||||
|
child._remove(); |
||||
|
} else if (child.parentNode) { |
||||
|
child.remove(); |
||||
|
} |
||||
|
|
||||
|
// Insert it as a child of its new parent
|
||||
|
child.parentNode = parent; |
||||
|
if (isReplace) { |
||||
|
LinkedList.replace(n, child); |
||||
|
if (parent._childNodes) { |
||||
|
child._index = before_index; |
||||
|
parent._childNodes[before_index] = child; |
||||
|
} else if (parent._firstChild === before) { |
||||
|
parent._firstChild = child; |
||||
|
} |
||||
|
} else { |
||||
|
if (n !== null) { |
||||
|
LinkedList.insertBefore(child, n); |
||||
|
} |
||||
|
if (parent._childNodes) { |
||||
|
child._index = before_index; |
||||
|
parent._childNodes.splice(before_index, 0, child); |
||||
|
} else if (parent._firstChild === before) { |
||||
|
parent._firstChild = child; |
||||
|
} |
||||
|
} |
||||
|
if (bothRooted) { |
||||
|
parent.modify(); |
||||
|
// Generate a move mutation event
|
||||
|
parent.doc.mutateMove(child); |
||||
|
} else if (parent.rooted) { |
||||
|
parent.modify(); |
||||
|
parent.doc.mutateInsert(child); |
||||
|
} |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// Return the lastModTime value for this node. (For use as a
|
||||
|
// cache invalidation mechanism. If the node does not already
|
||||
|
// have one, initialize it from the owner document's modclock
|
||||
|
// property. (Note that modclock does not return the actual
|
||||
|
// time; it is simply a counter incremented on each document
|
||||
|
// modification)
|
||||
|
lastModTime: { get: function() { |
||||
|
if (!this._lastModTime) { |
||||
|
this._lastModTime = this.doc.modclock; |
||||
|
} |
||||
|
return this._lastModTime; |
||||
|
}}, |
||||
|
|
||||
|
// Increment the owner document's modclock and use the new
|
||||
|
// value to update the lastModTime value for this node and
|
||||
|
// all of its ancestors. Nodes that have never had their
|
||||
|
// lastModTime value queried do not need to have a
|
||||
|
// lastModTime property set on them since there is no
|
||||
|
// previously queried value to ever compare the new value
|
||||
|
// against, so only update nodes that already have a
|
||||
|
// _lastModTime property.
|
||||
|
modify: { value: function() { |
||||
|
if (this.doc.modclock) { // Skip while doc.modclock == 0
|
||||
|
var time = ++this.doc.modclock; |
||||
|
for(var n = this; n; n = n.parentElement) { |
||||
|
if (n._lastModTime) { |
||||
|
n._lastModTime = time; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// This attribute is not part of the DOM but is quite helpful.
|
||||
|
// It returns the document with which a node is associated. Usually
|
||||
|
// this is the ownerDocument. But ownerDocument is null for the
|
||||
|
// document object itself, so this is a handy way to get the document
|
||||
|
// regardless of the node type
|
||||
|
doc: { get: function() { |
||||
|
return this.ownerDocument || this; |
||||
|
}}, |
||||
|
|
||||
|
|
||||
|
// If the node has a nid (node id), then it is rooted in a document
|
||||
|
rooted: { get: function() { |
||||
|
return !!this._nid; |
||||
|
}}, |
||||
|
|
||||
|
normalize: { value: function() { |
||||
|
var next; |
||||
|
for (var child=this.firstChild; child !== null; child=next) { |
||||
|
next = child.nextSibling; |
||||
|
|
||||
|
if (child.normalize) { |
||||
|
child.normalize(); |
||||
|
} |
||||
|
|
||||
|
if (child.nodeType !== Node.TEXT_NODE) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (child.nodeValue === "") { |
||||
|
this.removeChild(child); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var prevChild = child.previousSibling; |
||||
|
if (prevChild === null) { |
||||
|
continue; |
||||
|
} else if (prevChild.nodeType === Node.TEXT_NODE) { |
||||
|
// merge this with previous and remove the child
|
||||
|
prevChild.appendData(child.nodeValue); |
||||
|
this.removeChild(child); |
||||
|
} |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
// Convert the children of a node to an HTML string.
|
||||
|
// This is used by the innerHTML getter
|
||||
|
// The serialization spec is at:
|
||||
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
|
||||
|
//
|
||||
|
// The serialization logic is intentionally implemented in a separate
|
||||
|
// `NodeUtils` helper instead of the more obvious choice of a private
|
||||
|
// `_serializeOne()` method on the `Node.prototype` in order to avoid
|
||||
|
// the megamorphic `this._serializeOne` property access, which reduces
|
||||
|
// performance unnecessarily. If you need specialized behavior for a
|
||||
|
// certain subclass, you'll need to implement that in `NodeUtils`.
|
||||
|
// See https://github.com/fgnass/domino/pull/142 for more information.
|
||||
|
serialize: { value: function() { |
||||
|
var s = ''; |
||||
|
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) { |
||||
|
s += NodeUtils.serializeOne(kid, this); |
||||
|
} |
||||
|
return s; |
||||
|
}}, |
||||
|
|
||||
|
// Non-standard, but often useful for debugging.
|
||||
|
outerHTML: { |
||||
|
get: function() { |
||||
|
return NodeUtils.serializeOne(this, { nodeType: 0 }); |
||||
|
}, |
||||
|
set: utils.nyi, |
||||
|
}, |
||||
|
|
||||
|
// mirror node type properties in the prototype, so they are present
|
||||
|
// in instances of Node (and subclasses)
|
||||
|
ELEMENT_NODE: { value: ELEMENT_NODE }, |
||||
|
ATTRIBUTE_NODE: { value: ATTRIBUTE_NODE }, |
||||
|
TEXT_NODE: { value: TEXT_NODE }, |
||||
|
CDATA_SECTION_NODE: { value: CDATA_SECTION_NODE }, |
||||
|
ENTITY_REFERENCE_NODE: { value: ENTITY_REFERENCE_NODE }, |
||||
|
ENTITY_NODE: { value: ENTITY_NODE }, |
||||
|
PROCESSING_INSTRUCTION_NODE: { value: PROCESSING_INSTRUCTION_NODE }, |
||||
|
COMMENT_NODE: { value: COMMENT_NODE }, |
||||
|
DOCUMENT_NODE: { value: DOCUMENT_NODE }, |
||||
|
DOCUMENT_TYPE_NODE: { value: DOCUMENT_TYPE_NODE }, |
||||
|
DOCUMENT_FRAGMENT_NODE: { value: DOCUMENT_FRAGMENT_NODE }, |
||||
|
NOTATION_NODE: { value: NOTATION_NODE }, |
||||
|
|
||||
|
DOCUMENT_POSITION_DISCONNECTED: { value: DOCUMENT_POSITION_DISCONNECTED }, |
||||
|
DOCUMENT_POSITION_PRECEDING: { value: DOCUMENT_POSITION_PRECEDING }, |
||||
|
DOCUMENT_POSITION_FOLLOWING: { value: DOCUMENT_POSITION_FOLLOWING }, |
||||
|
DOCUMENT_POSITION_CONTAINS: { value: DOCUMENT_POSITION_CONTAINS }, |
||||
|
DOCUMENT_POSITION_CONTAINED_BY: { value: DOCUMENT_POSITION_CONTAINED_BY }, |
||||
|
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: { value: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC }, |
||||
|
}); |
@ -0,0 +1,24 @@ |
|||||
|
"use strict"; |
||||
|
var NodeFilter = { |
||||
|
// Constants for acceptNode()
|
||||
|
FILTER_ACCEPT: 1, |
||||
|
FILTER_REJECT: 2, |
||||
|
FILTER_SKIP: 3, |
||||
|
|
||||
|
// Constants for whatToShow
|
||||
|
SHOW_ALL: 0xFFFFFFFF, |
||||
|
SHOW_ELEMENT: 0x1, |
||||
|
SHOW_ATTRIBUTE: 0x2, // historical
|
||||
|
SHOW_TEXT: 0x4, |
||||
|
SHOW_CDATA_SECTION: 0x8, // historical
|
||||
|
SHOW_ENTITY_REFERENCE: 0x10, // historical
|
||||
|
SHOW_ENTITY: 0x20, // historical
|
||||
|
SHOW_PROCESSING_INSTRUCTION: 0x40, |
||||
|
SHOW_COMMENT: 0x80, |
||||
|
SHOW_DOCUMENT: 0x100, |
||||
|
SHOW_DOCUMENT_TYPE: 0x200, |
||||
|
SHOW_DOCUMENT_FRAGMENT: 0x400, |
||||
|
SHOW_NOTATION: 0x800 // historical
|
||||
|
}; |
||||
|
|
||||
|
module.exports = (NodeFilter.constructor = NodeFilter.prototype = NodeFilter); |
@ -0,0 +1,217 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = NodeIterator; |
||||
|
|
||||
|
var NodeFilter = require('./NodeFilter.js'); |
||||
|
var NodeTraversal = require('./NodeTraversal.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
/* Private methods and helpers */ |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeIterator::moveToNext and NodeIterator::moveToPrevious |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeIterator.cpp?rev=186279#L51
|
||||
|
*/ |
||||
|
function move(node, stayWithin, directionIsNext) { |
||||
|
if (directionIsNext) { |
||||
|
return NodeTraversal.next(node, stayWithin); |
||||
|
} else { |
||||
|
if (node === stayWithin) { |
||||
|
return null; |
||||
|
} |
||||
|
return NodeTraversal.previous(node, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function isInclusiveAncestor(node, possibleChild) { |
||||
|
for ( ; possibleChild; possibleChild = possibleChild.parentNode) { |
||||
|
if (node === possibleChild) { return true; } |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#concept-nodeiterator-traverse
|
||||
|
* @method |
||||
|
* @access private |
||||
|
* @param {NodeIterator} ni |
||||
|
* @param {string} direction One of 'next' or 'previous'. |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
function traverse(ni, directionIsNext) { |
||||
|
var node, beforeNode; |
||||
|
node = ni._referenceNode; |
||||
|
beforeNode = ni._pointerBeforeReferenceNode; |
||||
|
while (true) { |
||||
|
if (beforeNode === directionIsNext) { |
||||
|
beforeNode = !beforeNode; |
||||
|
} else { |
||||
|
node = move(node, ni._root, directionIsNext); |
||||
|
if (node === null) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
var result = ni._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
ni._referenceNode = node; |
||||
|
ni._pointerBeforeReferenceNode = beforeNode; |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
/* Public API */ |
||||
|
|
||||
|
/** |
||||
|
* Implemented version: http://www.w3.org/TR/2015/WD-dom-20150618/#nodeiterator
|
||||
|
* Latest version: http://www.w3.org/TR/dom/#nodeiterator
|
||||
|
* |
||||
|
* @constructor |
||||
|
* @param {Node} root |
||||
|
* @param {number} whatToShow [optional] |
||||
|
* @param {Function|NodeFilter} filter [optional] |
||||
|
* @throws Error |
||||
|
*/ |
||||
|
function NodeIterator(root, whatToShow, filter) { |
||||
|
if (!root || !root.nodeType) { |
||||
|
utils.NotSupportedError(); |
||||
|
} |
||||
|
|
||||
|
// Read-only properties
|
||||
|
this._root = root; |
||||
|
this._referenceNode = root; |
||||
|
this._pointerBeforeReferenceNode = true; |
||||
|
this._whatToShow = Number(whatToShow) || 0; |
||||
|
this._filter = filter || null; |
||||
|
this._active = false; |
||||
|
// Record active node iterators in the document, in order to perform
|
||||
|
// "node iterator pre-removal steps".
|
||||
|
root.doc._attachNodeIterator(this); |
||||
|
} |
||||
|
|
||||
|
Object.defineProperties(NodeIterator.prototype, { |
||||
|
root: { get: function root() { |
||||
|
return this._root; |
||||
|
} }, |
||||
|
referenceNode: { get: function referenceNode() { |
||||
|
return this._referenceNode; |
||||
|
} }, |
||||
|
pointerBeforeReferenceNode: { get: function pointerBeforeReferenceNode() { |
||||
|
return this._pointerBeforeReferenceNode; |
||||
|
} }, |
||||
|
whatToShow: { get: function whatToShow() { |
||||
|
return this._whatToShow; |
||||
|
} }, |
||||
|
filter: { get: function filter() { |
||||
|
return this._filter; |
||||
|
} }, |
||||
|
|
||||
|
/** |
||||
|
* @method |
||||
|
* @param {Node} node |
||||
|
* @return {Number} Constant NodeFilter.FILTER_ACCEPT, |
||||
|
* NodeFilter.FILTER_REJECT or NodeFilter.FILTER_SKIP. |
||||
|
*/ |
||||
|
_internalFilter: { value: function _internalFilter(node) { |
||||
|
/* jshint bitwise: false */ |
||||
|
var result, filter; |
||||
|
if (this._active) { |
||||
|
utils.InvalidStateError(); |
||||
|
} |
||||
|
|
||||
|
// Maps nodeType to whatToShow
|
||||
|
if (!(((1 << (node.nodeType - 1)) & this._whatToShow))) { |
||||
|
return NodeFilter.FILTER_SKIP; |
||||
|
} |
||||
|
|
||||
|
filter = this._filter; |
||||
|
if (filter === null) { |
||||
|
result = NodeFilter.FILTER_ACCEPT; |
||||
|
} else { |
||||
|
this._active = true; |
||||
|
try { |
||||
|
if (typeof filter === 'function') { |
||||
|
result = filter(node); |
||||
|
} else { |
||||
|
result = filter.acceptNode(node); |
||||
|
} |
||||
|
} finally { |
||||
|
this._active = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Note that coercing to a number means that
|
||||
|
// `true` becomes `1` (which is NodeFilter.FILTER_ACCEPT)
|
||||
|
// `false` becomes `0` (neither accept, reject, or skip)
|
||||
|
return (+result); |
||||
|
} }, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps
|
||||
|
* @method |
||||
|
* @return void |
||||
|
*/ |
||||
|
_preremove: { value: function _preremove(toBeRemovedNode) { |
||||
|
if (isInclusiveAncestor(toBeRemovedNode, this._root)) { return; } |
||||
|
if (!isInclusiveAncestor(toBeRemovedNode, this._referenceNode)) { return; } |
||||
|
if (this._pointerBeforeReferenceNode) { |
||||
|
var next = toBeRemovedNode; |
||||
|
while (next.lastChild) { |
||||
|
next = next.lastChild; |
||||
|
} |
||||
|
next = NodeTraversal.next(next, this.root); |
||||
|
if (next) { |
||||
|
this._referenceNode = next; |
||||
|
return; |
||||
|
} |
||||
|
this._pointerBeforeReferenceNode = false; |
||||
|
// fall through
|
||||
|
} |
||||
|
if (toBeRemovedNode.previousSibling === null) { |
||||
|
this._referenceNode = toBeRemovedNode.parentNode; |
||||
|
} else { |
||||
|
this._referenceNode = toBeRemovedNode.previousSibling; |
||||
|
var lastChild; |
||||
|
for (lastChild = this._referenceNode.lastChild; |
||||
|
lastChild; |
||||
|
lastChild = this._referenceNode.lastChild) { |
||||
|
this._referenceNode = lastChild; |
||||
|
} |
||||
|
} |
||||
|
} }, |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-nextnode
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
nextNode: { value: function nextNode() { |
||||
|
return traverse(this, true); |
||||
|
} }, |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-previousnode
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
previousNode: { value: function previousNode() { |
||||
|
return traverse(this, false); |
||||
|
} }, |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#dom-nodeiterator-detach
|
||||
|
* @method |
||||
|
* @return void |
||||
|
*/ |
||||
|
detach: { value: function detach() { |
||||
|
/* "The detach() method must do nothing. |
||||
|
* Its functionality (disabling a NodeIterator object) was removed, |
||||
|
* but the method itself is preserved for compatibility. |
||||
|
*/ |
||||
|
} }, |
||||
|
|
||||
|
/** For compatibility with web-platform-tests. */ |
||||
|
toString: { value: function toString() { |
||||
|
return "[object NodeIterator]"; |
||||
|
} }, |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
// No support for subclassing array, return an actual Array object.
|
||||
|
function item(i) { |
||||
|
/* jshint validthis: true */ |
||||
|
return this[i] || null; |
||||
|
} |
||||
|
|
||||
|
function NodeList(a) { |
||||
|
if (!a) a = []; |
||||
|
a.item = item; |
||||
|
return a; |
||||
|
} |
||||
|
|
||||
|
module.exports = NodeList; |
@ -0,0 +1,12 @@ |
|||||
|
/* jshint esversion: 6 */ |
||||
|
"use strict"; |
||||
|
|
||||
|
module.exports = class NodeList extends Array { |
||||
|
constructor(a) { |
||||
|
super((a && a.length) || 0); |
||||
|
if (a) { |
||||
|
for (var idx in a) { this[idx] = a[idx]; } |
||||
|
} |
||||
|
} |
||||
|
item(i) { return this[i] || null; } |
||||
|
}; |
@ -0,0 +1,13 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
var NodeList; |
||||
|
|
||||
|
try { |
||||
|
// Attempt to use ES6-style Array subclass if possible.
|
||||
|
NodeList = require('./NodeList.es6.js'); |
||||
|
} catch (e) { |
||||
|
// No support for subclassing array, return an actual Array object.
|
||||
|
NodeList = require('./NodeList.es5.js'); |
||||
|
} |
||||
|
|
||||
|
module.exports = NodeList; |
@ -0,0 +1,87 @@ |
|||||
|
"use strict"; |
||||
|
/* exported NodeTraversal */ |
||||
|
var NodeTraversal = module.exports = { |
||||
|
nextSkippingChildren: nextSkippingChildren, |
||||
|
nextAncestorSibling: nextAncestorSibling, |
||||
|
next: next, |
||||
|
previous: previous, |
||||
|
deepLastChild: deepLastChild |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeTraversal::nextSkippingChildren |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L109
|
||||
|
*/ |
||||
|
function nextSkippingChildren(node, stayWithin) { |
||||
|
if (node === stayWithin) { |
||||
|
return null; |
||||
|
} |
||||
|
if (node.nextSibling !== null) { |
||||
|
return node.nextSibling; |
||||
|
} |
||||
|
return nextAncestorSibling(node, stayWithin); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeTraversal::nextAncestorSibling |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.cpp?rev=179143#L93
|
||||
|
*/ |
||||
|
function nextAncestorSibling(node, stayWithin) { |
||||
|
for (node = node.parentNode; node !== null; node = node.parentNode) { |
||||
|
if (node === stayWithin) { |
||||
|
return null; |
||||
|
} |
||||
|
if (node.nextSibling !== null) { |
||||
|
return node.nextSibling; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeTraversal::next |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L99
|
||||
|
*/ |
||||
|
function next(node, stayWithin) { |
||||
|
var n; |
||||
|
n = node.firstChild; |
||||
|
if (n !== null) { |
||||
|
return n; |
||||
|
} |
||||
|
if (node === stayWithin) { |
||||
|
return null; |
||||
|
} |
||||
|
n = node.nextSibling; |
||||
|
if (n !== null) { |
||||
|
return n; |
||||
|
} |
||||
|
return nextAncestorSibling(node, stayWithin); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeTraversal::deepLastChild |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.cpp?rev=179143#L116
|
||||
|
*/ |
||||
|
function deepLastChild(node) { |
||||
|
while (node.lastChild) { |
||||
|
node = node.lastChild; |
||||
|
} |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @based on WebKit's NodeTraversal::previous |
||||
|
* https://trac.webkit.org/browser/trunk/Source/WebCore/dom/NodeTraversal.h?rev=179143#L121
|
||||
|
*/ |
||||
|
function previous(node, stayWithin) { |
||||
|
var p; |
||||
|
p = node.previousSibling; |
||||
|
if (p !== null) { |
||||
|
return deepLastChild(p); |
||||
|
} |
||||
|
p = node.parentNode; |
||||
|
if (p === stayWithin) { |
||||
|
return null; |
||||
|
} |
||||
|
return p; |
||||
|
} |
@ -0,0 +1,168 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = { |
||||
|
// NOTE: The `serializeOne()` function used to live on the `Node.prototype`
|
||||
|
// as a private method `Node#_serializeOne(child)`, however that requires
|
||||
|
// a megamorphic property access `this._serializeOne` just to get to the
|
||||
|
// method, and this is being done on lots of different `Node` subclasses,
|
||||
|
// which puts a lot of pressure on V8's megamorphic stub cache. So by
|
||||
|
// moving the helper off of the `Node.prototype` and into a separate
|
||||
|
// function in this helper module, we get a monomorphic property access
|
||||
|
// `NodeUtils.serializeOne` to get to the function and reduce pressure
|
||||
|
// on the megamorphic stub cache.
|
||||
|
// See https://github.com/fgnass/domino/pull/142 for more information.
|
||||
|
serializeOne: serializeOne |
||||
|
}; |
||||
|
|
||||
|
var utils = require('./utils.js'); |
||||
|
var NAMESPACE = utils.NAMESPACE; |
||||
|
|
||||
|
var hasRawContent = { |
||||
|
STYLE: true, |
||||
|
SCRIPT: true, |
||||
|
XMP: true, |
||||
|
IFRAME: true, |
||||
|
NOEMBED: true, |
||||
|
NOFRAMES: true, |
||||
|
PLAINTEXT: true |
||||
|
}; |
||||
|
|
||||
|
var emptyElements = { |
||||
|
area: true, |
||||
|
base: true, |
||||
|
basefont: true, |
||||
|
bgsound: true, |
||||
|
br: true, |
||||
|
col: true, |
||||
|
embed: true, |
||||
|
frame: true, |
||||
|
hr: true, |
||||
|
img: true, |
||||
|
input: true, |
||||
|
keygen: true, |
||||
|
link: true, |
||||
|
meta: true, |
||||
|
param: true, |
||||
|
source: true, |
||||
|
track: true, |
||||
|
wbr: true |
||||
|
}; |
||||
|
|
||||
|
var extraNewLine = { |
||||
|
/* Removed in https://github.com/whatwg/html/issues/944 |
||||
|
pre: true, |
||||
|
textarea: true, |
||||
|
listing: true |
||||
|
*/ |
||||
|
}; |
||||
|
|
||||
|
function escape(s) { |
||||
|
return s.replace(/[&<>\u00A0]/g, function(c) { |
||||
|
switch(c) { |
||||
|
case '&': return '&'; |
||||
|
case '<': return '<'; |
||||
|
case '>': return '>'; |
||||
|
case '\u00A0': return ' '; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function escapeAttr(s) { |
||||
|
var toEscape = /[&"\u00A0]/g; |
||||
|
if (!toEscape.test(s)) { |
||||
|
// nothing to do, fast path
|
||||
|
return s; |
||||
|
} else { |
||||
|
return s.replace(toEscape, function(c) { |
||||
|
switch(c) { |
||||
|
case '&': return '&'; |
||||
|
case '"': return '"'; |
||||
|
case '\u00A0': return ' '; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function attrname(a) { |
||||
|
var ns = a.namespaceURI; |
||||
|
if (!ns) |
||||
|
return a.localName; |
||||
|
if (ns === NAMESPACE.XML) |
||||
|
return 'xml:' + a.localName; |
||||
|
if (ns === NAMESPACE.XLINK) |
||||
|
return 'xlink:' + a.localName; |
||||
|
|
||||
|
if (ns === NAMESPACE.XMLNS) { |
||||
|
if (a.localName === 'xmlns') return 'xmlns'; |
||||
|
else return 'xmlns:' + a.localName; |
||||
|
} |
||||
|
return a.name; |
||||
|
} |
||||
|
|
||||
|
function serializeOne(kid, parent) { |
||||
|
var s = ''; |
||||
|
switch(kid.nodeType) { |
||||
|
case 1: //ELEMENT_NODE
|
||||
|
var ns = kid.namespaceURI; |
||||
|
var html = ns === NAMESPACE.HTML; |
||||
|
var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName; |
||||
|
|
||||
|
s += '<' + tagname; |
||||
|
|
||||
|
for(var j = 0, k = kid._numattrs; j < k; j++) { |
||||
|
var a = kid._attr(j); |
||||
|
s += ' ' + attrname(a); |
||||
|
if (a.value !== undefined) s += '="' + escapeAttr(a.value) + '"'; |
||||
|
} |
||||
|
s += '>'; |
||||
|
|
||||
|
if (!(html && emptyElements[tagname])) { |
||||
|
var ss = kid.serialize(); |
||||
|
if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n'; |
||||
|
// Serialize children and add end tag for all others
|
||||
|
s += ss; |
||||
|
s += '</' + tagname + '>'; |
||||
|
} |
||||
|
break; |
||||
|
case 3: //TEXT_NODE
|
||||
|
case 4: //CDATA_SECTION_NODE
|
||||
|
var parenttag; |
||||
|
if (parent.nodeType === 1 /*ELEMENT_NODE*/ && |
||||
|
parent.namespaceURI === NAMESPACE.HTML) |
||||
|
parenttag = parent.tagName; |
||||
|
else |
||||
|
parenttag = ''; |
||||
|
|
||||
|
if (hasRawContent[parenttag] || |
||||
|
(parenttag==='NOSCRIPT' && parent.ownerDocument._scripting_enabled)) { |
||||
|
s += kid.data; |
||||
|
} else { |
||||
|
s += escape(kid.data); |
||||
|
} |
||||
|
break; |
||||
|
case 8: //COMMENT_NODE
|
||||
|
s += '<!--' + kid.data + '-->'; |
||||
|
break; |
||||
|
case 7: //PROCESSING_INSTRUCTION_NODE
|
||||
|
s += '<?' + kid.target + ' ' + kid.data + '?>'; |
||||
|
break; |
||||
|
case 10: //DOCUMENT_TYPE_NODE
|
||||
|
s += '<!DOCTYPE ' + kid.name; |
||||
|
|
||||
|
if (false) { |
||||
|
// Latest HTML serialization spec omits the public/system ID
|
||||
|
if (kid.publicID) { |
||||
|
s += ' PUBLIC "' + kid.publicId + '"'; |
||||
|
} |
||||
|
|
||||
|
if (kid.systemId) { |
||||
|
s += ' "' + kid.systemId + '"'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
s += '>'; |
||||
|
break; |
||||
|
default: |
||||
|
utils.InvalidStateError(); |
||||
|
} |
||||
|
return s; |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
"use strict"; |
||||
|
var Node = require('./Node.js'); |
||||
|
|
||||
|
var NonDocumentTypeChildNode = { |
||||
|
|
||||
|
nextElementSibling: { get: function() { |
||||
|
if (this.parentNode) { |
||||
|
for (var kid = this.nextSibling; kid !== null; kid = kid.nextSibling) { |
||||
|
if (kid.nodeType === Node.ELEMENT_NODE) return kid; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
}}, |
||||
|
|
||||
|
previousElementSibling: { get: function() { |
||||
|
if (this.parentNode) { |
||||
|
for (var kid = this.previousSibling; kid !== null; kid = kid.previousSibling) { |
||||
|
if (kid.nodeType === Node.ELEMENT_NODE) return kid; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
}} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
module.exports = NonDocumentTypeChildNode; |
@ -0,0 +1,43 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = ProcessingInstruction; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var CharacterData = require('./CharacterData.js'); |
||||
|
|
||||
|
function ProcessingInstruction(doc, target, data) { |
||||
|
CharacterData.call(this); |
||||
|
this.nodeType = Node.PROCESSING_INSTRUCTION_NODE; |
||||
|
this.ownerDocument = doc; |
||||
|
this.target = target; |
||||
|
this._data = data; |
||||
|
} |
||||
|
|
||||
|
var nodeValue = { |
||||
|
get: function() { return this._data; }, |
||||
|
set: function(v) { |
||||
|
if (v === null || v === undefined) { v = ''; } else { v = String(v); } |
||||
|
this._data = v; |
||||
|
if (this.rooted) this.ownerDocument.mutateValue(this); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
ProcessingInstruction.prototype = Object.create(CharacterData.prototype, { |
||||
|
nodeName: { get: function() { return this.target; }}, |
||||
|
nodeValue: nodeValue, |
||||
|
textContent: nodeValue, |
||||
|
data: { |
||||
|
get: nodeValue.get, |
||||
|
set: function(v) { |
||||
|
nodeValue.set.call(this, v===null ? '' : String(v)); |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
return new ProcessingInstruction(this.ownerDocument, this.target, this._data); |
||||
|
}}, |
||||
|
isEqual: { value: function isEqual(n) { |
||||
|
return this.target === n.target && this._data === n._data; |
||||
|
}} |
||||
|
|
||||
|
}); |
@ -0,0 +1,74 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = Text; |
||||
|
|
||||
|
var utils = require('./utils.js'); |
||||
|
var Node = require('./Node.js'); |
||||
|
var CharacterData = require('./CharacterData.js'); |
||||
|
|
||||
|
function Text(doc, data) { |
||||
|
CharacterData.call(this); |
||||
|
this.nodeType = Node.TEXT_NODE; |
||||
|
this.ownerDocument = doc; |
||||
|
this._data = data; |
||||
|
this._index = undefined; |
||||
|
} |
||||
|
|
||||
|
var nodeValue = { |
||||
|
get: function() { return this._data; }, |
||||
|
set: function(v) { |
||||
|
if (v === null || v === undefined) { v = ''; } else { v = String(v); } |
||||
|
if (v === this._data) return; |
||||
|
this._data = v; |
||||
|
if (this.rooted) |
||||
|
this.ownerDocument.mutateValue(this); |
||||
|
if (this.parentNode && |
||||
|
this.parentNode._textchangehook) |
||||
|
this.parentNode._textchangehook(this); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Text.prototype = Object.create(CharacterData.prototype, { |
||||
|
nodeName: { value: "#text" }, |
||||
|
// These three attributes are all the same.
|
||||
|
// The data attribute has a [TreatNullAs=EmptyString] but we'll
|
||||
|
// implement that at the interface level
|
||||
|
nodeValue: nodeValue, |
||||
|
textContent: nodeValue, |
||||
|
data: { |
||||
|
get: nodeValue.get, |
||||
|
set: function(v) { |
||||
|
nodeValue.set.call(this, v===null ? '' : String(v)); |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
splitText: { value: function splitText(offset) { |
||||
|
if (offset > this._data.length || offset < 0) utils.IndexSizeError(); |
||||
|
|
||||
|
var newdata = this._data.substring(offset), |
||||
|
newnode = this.ownerDocument.createTextNode(newdata); |
||||
|
this.data = this.data.substring(0, offset); |
||||
|
|
||||
|
var parent = this.parentNode; |
||||
|
if (parent !== null) |
||||
|
parent.insertBefore(newnode, this.nextSibling); |
||||
|
|
||||
|
return newnode; |
||||
|
}}, |
||||
|
|
||||
|
wholeText: { get: function wholeText() { |
||||
|
var result = this.textContent; |
||||
|
for (var next = this.nextSibling; next; next = next.nextSibling) { |
||||
|
if (next.nodeType !== Node.TEXT_NODE) { break; } |
||||
|
result += next.textContent; |
||||
|
} |
||||
|
return result; |
||||
|
}}, |
||||
|
// Obsolete, removed from spec.
|
||||
|
replaceWholeText: { value: utils.nyi }, |
||||
|
|
||||
|
// Utility methods
|
||||
|
clone: { value: function clone() { |
||||
|
return new Text(this.ownerDocument, this._data); |
||||
|
}}, |
||||
|
|
||||
|
}); |
@ -0,0 +1,336 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = TreeWalker; |
||||
|
|
||||
|
var Node = require('./Node.js'); |
||||
|
var NodeFilter = require('./NodeFilter.js'); |
||||
|
var NodeTraversal = require('./NodeTraversal.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
var mapChild = { |
||||
|
first: 'firstChild', |
||||
|
last: 'lastChild', |
||||
|
next: 'firstChild', |
||||
|
previous: 'lastChild' |
||||
|
}; |
||||
|
|
||||
|
var mapSibling = { |
||||
|
first: 'nextSibling', |
||||
|
last: 'previousSibling', |
||||
|
next: 'nextSibling', |
||||
|
previous: 'previousSibling' |
||||
|
}; |
||||
|
|
||||
|
/* Private methods and helpers */ |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#concept-traverse-children
|
||||
|
* @method |
||||
|
* @access private |
||||
|
* @param {TreeWalker} tw |
||||
|
* @param {string} type One of 'first' or 'last'. |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
function traverseChildren(tw, type) { |
||||
|
var child, node, parent, result, sibling; |
||||
|
node = tw._currentNode[mapChild[type]]; |
||||
|
while (node !== null) { |
||||
|
result = tw._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
tw._currentNode = node; |
||||
|
return node; |
||||
|
} |
||||
|
if (result === NodeFilter.FILTER_SKIP) { |
||||
|
child = node[mapChild[type]]; |
||||
|
if (child !== null) { |
||||
|
node = child; |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
while (node !== null) { |
||||
|
sibling = node[mapSibling[type]]; |
||||
|
if (sibling !== null) { |
||||
|
node = sibling; |
||||
|
break; |
||||
|
} |
||||
|
parent = node.parentNode; |
||||
|
if (parent === null || parent === tw.root || parent === tw._currentNode) { |
||||
|
return null; |
||||
|
} else { |
||||
|
node = parent; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#concept-traverse-siblings
|
||||
|
* @method |
||||
|
* @access private |
||||
|
* @param {TreeWalker} tw |
||||
|
* @param {TreeWalker} type One of 'next' or 'previous'. |
||||
|
* @return {Node|nul} |
||||
|
*/ |
||||
|
function traverseSiblings(tw, type) { |
||||
|
var node, result, sibling; |
||||
|
node = tw._currentNode; |
||||
|
if (node === tw.root) { |
||||
|
return null; |
||||
|
} |
||||
|
while (true) { |
||||
|
sibling = node[mapSibling[type]]; |
||||
|
while (sibling !== null) { |
||||
|
node = sibling; |
||||
|
result = tw._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
tw._currentNode = node; |
||||
|
return node; |
||||
|
} |
||||
|
sibling = node[mapChild[type]]; |
||||
|
if (result === NodeFilter.FILTER_REJECT || sibling === null) { |
||||
|
sibling = node[mapSibling[type]]; |
||||
|
} |
||||
|
} |
||||
|
node = node.parentNode; |
||||
|
if (node === null || node === tw.root) { |
||||
|
return null; |
||||
|
} |
||||
|
if (tw._internalFilter(node) === NodeFilter.FILTER_ACCEPT) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/* Public API */ |
||||
|
|
||||
|
/** |
||||
|
* Latest version: https://dom.spec.whatwg.org/#treewalker
|
||||
|
* |
||||
|
* @constructor |
||||
|
* @param {Node} root |
||||
|
* @param {number} whatToShow [optional] |
||||
|
* @param {Function|NodeFilter} filter [optional] |
||||
|
* @throws Error |
||||
|
*/ |
||||
|
function TreeWalker(root, whatToShow, filter) { |
||||
|
if (!root || !root.nodeType) { |
||||
|
utils.NotSupportedError(); |
||||
|
} |
||||
|
|
||||
|
// Read-only properties
|
||||
|
this._root = root; |
||||
|
this._whatToShow = Number(whatToShow) || 0; |
||||
|
this._filter = filter || null; |
||||
|
this._active = false; |
||||
|
// Read-write property
|
||||
|
this._currentNode = root; |
||||
|
} |
||||
|
|
||||
|
Object.defineProperties(TreeWalker.prototype, { |
||||
|
root: { get: function() { return this._root; } }, |
||||
|
whatToShow: { get: function() { return this._whatToShow; } }, |
||||
|
filter: { get: function() { return this._filter; } }, |
||||
|
|
||||
|
currentNode: { |
||||
|
get: function currentNode() { |
||||
|
return this._currentNode; |
||||
|
}, |
||||
|
set: function setCurrentNode(v) { |
||||
|
if (!(v instanceof Node)) { |
||||
|
throw new TypeError("Not a Node"); // `null` is also not a node
|
||||
|
} |
||||
|
this._currentNode = v; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* @method |
||||
|
* @param {Node} node |
||||
|
* @return {Number} Constant NodeFilter.FILTER_ACCEPT, |
||||
|
* NodeFilter.FILTER_REJECT or NodeFilter.FILTER_SKIP. |
||||
|
*/ |
||||
|
_internalFilter: { value: function _internalFilter(node) { |
||||
|
/* jshint bitwise: false */ |
||||
|
var result, filter; |
||||
|
if (this._active) { |
||||
|
utils.InvalidStateError(); |
||||
|
} |
||||
|
|
||||
|
// Maps nodeType to whatToShow
|
||||
|
if (!(((1 << (node.nodeType - 1)) & this._whatToShow))) { |
||||
|
return NodeFilter.FILTER_SKIP; |
||||
|
} |
||||
|
|
||||
|
filter = this._filter; |
||||
|
if (filter === null) { |
||||
|
result = NodeFilter.FILTER_ACCEPT; |
||||
|
} else { |
||||
|
this._active = true; |
||||
|
try { |
||||
|
if (typeof filter === 'function') { |
||||
|
result = filter(node); |
||||
|
} else { |
||||
|
result = filter.acceptNode(node); |
||||
|
} |
||||
|
} finally { |
||||
|
this._active = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Note that coercing to a number means that
|
||||
|
// `true` becomes `1` (which is NodeFilter.FILTER_ACCEPT)
|
||||
|
// `false` becomes `0` (neither accept, reject, or skip)
|
||||
|
return (+result); |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#dom-treewalker-parentnode
|
||||
|
* @based on WebKit's TreeWalker::parentNode |
||||
|
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L50
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
parentNode: { value: function parentNode() { |
||||
|
var node = this._currentNode; |
||||
|
while (node !== this.root) { |
||||
|
node = node.parentNode; |
||||
|
if (node === null) { |
||||
|
return null; |
||||
|
} |
||||
|
if (this._internalFilter(node) === NodeFilter.FILTER_ACCEPT) { |
||||
|
this._currentNode = node; |
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#dom-treewalker-firstchild
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
firstChild: { value: function firstChild() { |
||||
|
return traverseChildren(this, 'first'); |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#dom-treewalker-lastchild
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
lastChild: { value: function lastChild() { |
||||
|
return traverseChildren(this, 'last'); |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#dom-treewalker-previoussibling
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
previousSibling: { value: function previousSibling() { |
||||
|
return traverseSiblings(this, 'previous'); |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec http://www.w3.org/TR/dom/#dom-treewalker-nextsibling
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
nextSibling: { value: function nextSibling() { |
||||
|
return traverseSiblings(this, 'next'); |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#dom-treewalker-previousnode
|
||||
|
* @based on WebKit's TreeWalker::previousNode |
||||
|
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L181
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
previousNode: { value: function previousNode() { |
||||
|
var node, result, previousSibling, lastChild; |
||||
|
node = this._currentNode; |
||||
|
while (node !== this._root) { |
||||
|
for (previousSibling = node.previousSibling; |
||||
|
previousSibling; |
||||
|
previousSibling = node.previousSibling) { |
||||
|
node = previousSibling; |
||||
|
result = this._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_REJECT) { |
||||
|
continue; |
||||
|
} |
||||
|
for (lastChild = node.lastChild; |
||||
|
lastChild; |
||||
|
lastChild = node.lastChild) { |
||||
|
node = lastChild; |
||||
|
result = this._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_REJECT) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
this._currentNode = node; |
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
|
if (node === this.root || node.parentNode === null) { |
||||
|
return null; |
||||
|
} |
||||
|
node = node.parentNode; |
||||
|
if (this._internalFilter(node) === NodeFilter.FILTER_ACCEPT) { |
||||
|
this._currentNode = node; |
||||
|
return node; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
}}, |
||||
|
|
||||
|
/** |
||||
|
* @spec https://dom.spec.whatwg.org/#dom-treewalker-nextnode
|
||||
|
* @based on WebKit's TreeWalker::nextNode |
||||
|
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/dom/TreeWalker.cpp?rev=220453#L228
|
||||
|
* @method |
||||
|
* @return {Node|null} |
||||
|
*/ |
||||
|
nextNode: { value: function nextNode() { |
||||
|
var node, result, firstChild, nextSibling; |
||||
|
node = this._currentNode; |
||||
|
result = NodeFilter.FILTER_ACCEPT; |
||||
|
|
||||
|
CHILDREN: |
||||
|
while (true) { |
||||
|
for (firstChild = node.firstChild; |
||||
|
firstChild; |
||||
|
firstChild = node.firstChild) { |
||||
|
node = firstChild; |
||||
|
result = this._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
this._currentNode = node; |
||||
|
return node; |
||||
|
} else if (result === NodeFilter.FILTER_REJECT) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
for (nextSibling = NodeTraversal.nextSkippingChildren(node, this.root); |
||||
|
nextSibling; |
||||
|
nextSibling = NodeTraversal.nextSkippingChildren(node, this.root)) { |
||||
|
node = nextSibling; |
||||
|
result = this._internalFilter(node); |
||||
|
if (result === NodeFilter.FILTER_ACCEPT) { |
||||
|
this._currentNode = node; |
||||
|
return node; |
||||
|
} else if (result === NodeFilter.FILTER_SKIP) { |
||||
|
continue CHILDREN; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
}}, |
||||
|
|
||||
|
/** For compatibility with web-platform-tests. */ |
||||
|
toString: { value: function toString() { |
||||
|
return "[object TreeWalker]"; |
||||
|
}}, |
||||
|
}); |
@ -0,0 +1,19 @@ |
|||||
|
"use strict"; |
||||
|
var Event = require('./Event.js'); |
||||
|
|
||||
|
module.exports = UIEvent; |
||||
|
|
||||
|
function UIEvent() { |
||||
|
// Just use the superclass constructor to initialize
|
||||
|
Event.call(this); |
||||
|
this.view = null; // FF uses the current window
|
||||
|
this.detail = 0; |
||||
|
} |
||||
|
UIEvent.prototype = Object.create(Event.prototype, { |
||||
|
constructor: { value: UIEvent }, |
||||
|
initUIEvent: { value: function(type, bubbles, cancelable, view, detail) { |
||||
|
this.initEvent(type, bubbles, cancelable); |
||||
|
this.view = view; |
||||
|
this.detail = detail; |
||||
|
}} |
||||
|
}); |
@ -0,0 +1,194 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = URL; |
||||
|
|
||||
|
function URL(url) { |
||||
|
if (!url) return Object.create(URL.prototype); |
||||
|
// Can't use String.trim() since it defines whitespace differently than HTML
|
||||
|
this.url = url.replace(/^[ \t\n\r\f]+|[ \t\n\r\f]+$/g, ""); |
||||
|
|
||||
|
// See http://tools.ietf.org/html/rfc3986#appendix-B
|
||||
|
// and https://url.spec.whatwg.org/#parsing
|
||||
|
var match = URL.pattern.exec(this.url); |
||||
|
if (match) { |
||||
|
if (match[2]) this.scheme = match[2]; |
||||
|
if (match[4]) { |
||||
|
// parse username/password
|
||||
|
var userinfo = match[4].match(URL.userinfoPattern); |
||||
|
if (userinfo) { |
||||
|
this.username = userinfo[1]; |
||||
|
this.password = userinfo[3]; |
||||
|
match[4] = match[4].substring(userinfo[0].length); |
||||
|
} |
||||
|
if (match[4].match(URL.portPattern)) { |
||||
|
var pos = match[4].lastIndexOf(':'); |
||||
|
this.host = match[4].substring(0, pos); |
||||
|
this.port = match[4].substring(pos+1); |
||||
|
} |
||||
|
else { |
||||
|
this.host = match[4]; |
||||
|
} |
||||
|
} |
||||
|
if (match[5]) this.path = match[5]; |
||||
|
if (match[6]) this.query = match[7]; |
||||
|
if (match[8]) this.fragment = match[9]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
URL.pattern = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; |
||||
|
URL.userinfoPattern = /^([^@:]*)(:([^@]*))?@/; |
||||
|
URL.portPattern = /:\d+$/; |
||||
|
URL.authorityPattern = /^[^:\/?#]+:\/\//; |
||||
|
URL.hierarchyPattern = /^[^:\/?#]+:\//; |
||||
|
|
||||
|
// Return a percentEncoded version of s.
|
||||
|
// S should be a single-character string
|
||||
|
// XXX: needs to do utf-8 encoding?
|
||||
|
URL.percentEncode = function percentEncode(s) { |
||||
|
var c = s.charCodeAt(0); |
||||
|
if (c < 256) return "%" + c.toString(16); |
||||
|
else throw Error("can't percent-encode codepoints > 255 yet"); |
||||
|
}; |
||||
|
|
||||
|
URL.prototype = { |
||||
|
constructor: URL, |
||||
|
|
||||
|
// XXX: not sure if this is the precise definition of absolute
|
||||
|
isAbsolute: function() { return !!this.scheme; }, |
||||
|
isAuthorityBased: function() { |
||||
|
return URL.authorityPattern.test(this.url); |
||||
|
}, |
||||
|
isHierarchical: function() { |
||||
|
return URL.hierarchyPattern.test(this.url); |
||||
|
}, |
||||
|
|
||||
|
toString: function() { |
||||
|
var s = ""; |
||||
|
if (this.scheme !== undefined) s += this.scheme + ":"; |
||||
|
if (this.isAbsolute()) { |
||||
|
s += '//'; |
||||
|
if (this.username || this.password) { |
||||
|
s += this.username || ''; |
||||
|
if (this.password) { |
||||
|
s += ':' + this.password; |
||||
|
} |
||||
|
s += '@'; |
||||
|
} |
||||
|
if (this.host) { |
||||
|
s += this.host; |
||||
|
} |
||||
|
} |
||||
|
if (this.port !== undefined) s += ":" + this.port; |
||||
|
if (this.path !== undefined) s += this.path; |
||||
|
if (this.query !== undefined) s += "?" + this.query; |
||||
|
if (this.fragment !== undefined) s += "#" + this.fragment; |
||||
|
return s; |
||||
|
}, |
||||
|
|
||||
|
// See: http://tools.ietf.org/html/rfc3986#section-5.2
|
||||
|
// and https://url.spec.whatwg.org/#constructors
|
||||
|
resolve: function(relative) { |
||||
|
var base = this; // The base url we're resolving against
|
||||
|
var r = new URL(relative); // The relative reference url to resolve
|
||||
|
var t = new URL(); // The absolute target url we will return
|
||||
|
|
||||
|
if (r.scheme !== undefined) { |
||||
|
t.scheme = r.scheme; |
||||
|
t.username = r.username; |
||||
|
t.password = r.password; |
||||
|
t.host = r.host; |
||||
|
t.port = r.port; |
||||
|
t.path = remove_dot_segments(r.path); |
||||
|
t.query = r.query; |
||||
|
} |
||||
|
else { |
||||
|
t.scheme = base.scheme; |
||||
|
if (r.host !== undefined) { |
||||
|
t.username = r.username; |
||||
|
t.password = r.password; |
||||
|
t.host = r.host; |
||||
|
t.port = r.port; |
||||
|
t.path = remove_dot_segments(r.path); |
||||
|
t.query = r.query; |
||||
|
} |
||||
|
else { |
||||
|
t.username = base.username; |
||||
|
t.password = base.password; |
||||
|
t.host = base.host; |
||||
|
t.port = base.port; |
||||
|
if (!r.path) { // undefined or empty
|
||||
|
t.path = base.path; |
||||
|
if (r.query !== undefined) |
||||
|
t.query = r.query; |
||||
|
else |
||||
|
t.query = base.query; |
||||
|
} |
||||
|
else { |
||||
|
if (r.path.charAt(0) === "/") { |
||||
|
t.path = remove_dot_segments(r.path); |
||||
|
} |
||||
|
else { |
||||
|
t.path = merge(base.path, r.path); |
||||
|
t.path = remove_dot_segments(t.path); |
||||
|
} |
||||
|
t.query = r.query; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
t.fragment = r.fragment; |
||||
|
|
||||
|
return t.toString(); |
||||
|
|
||||
|
|
||||
|
function merge(basepath, refpath) { |
||||
|
if (base.host !== undefined && !base.path) |
||||
|
return "/" + refpath; |
||||
|
|
||||
|
var lastslash = basepath.lastIndexOf("/"); |
||||
|
if (lastslash === -1) |
||||
|
return refpath; |
||||
|
else |
||||
|
return basepath.substring(0, lastslash+1) + refpath; |
||||
|
} |
||||
|
|
||||
|
function remove_dot_segments(path) { |
||||
|
if (!path) return path; // For "" or undefined
|
||||
|
|
||||
|
var output = ""; |
||||
|
while(path.length > 0) { |
||||
|
if (path === "." || path === "..") { |
||||
|
path = ""; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
var twochars = path.substring(0,2); |
||||
|
var threechars = path.substring(0,3); |
||||
|
var fourchars = path.substring(0,4); |
||||
|
if (threechars === "../") { |
||||
|
path = path.substring(3); |
||||
|
} |
||||
|
else if (twochars === "./") { |
||||
|
path = path.substring(2); |
||||
|
} |
||||
|
else if (threechars === "/./") { |
||||
|
path = "/" + path.substring(3); |
||||
|
} |
||||
|
else if (twochars === "/." && path.length === 2) { |
||||
|
path = "/"; |
||||
|
} |
||||
|
else if (fourchars === "/../" || |
||||
|
(threechars === "/.." && path.length === 3)) { |
||||
|
path = "/" + path.substring(4); |
||||
|
|
||||
|
output = output.replace(/\/?[^\/]*$/, ""); |
||||
|
} |
||||
|
else { |
||||
|
var segment = path.match(/(\/?([^\/]*))/)[0]; |
||||
|
output += segment; |
||||
|
path = path.substring(segment.length); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return output; |
||||
|
} |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,270 @@ |
|||||
|
"use strict"; |
||||
|
var URL = require('./URL.js'); |
||||
|
|
||||
|
module.exports = URLUtils; |
||||
|
|
||||
|
// Allow the `x == null` pattern. This is eslint's "null: 'ignore'" option,
|
||||
|
// but jshint doesn't support this.
|
||||
|
/* jshint eqeqeq: false */ |
||||
|
|
||||
|
// This is an abstract superclass for Location, HTMLAnchorElement and
|
||||
|
// other types that have the standard complement of "URL decomposition
|
||||
|
// IDL attributes". This is now standardized as URLUtils, see:
|
||||
|
// https://url.spec.whatwg.org/#urlutils
|
||||
|
// Subclasses must define a getter/setter on href.
|
||||
|
// The getter and setter methods parse and rebuild the URL on each
|
||||
|
// invocation; there is no attempt to cache the value and be more efficient
|
||||
|
function URLUtils() {} |
||||
|
URLUtils.prototype = Object.create(Object.prototype, { |
||||
|
|
||||
|
_url: { get: function() { |
||||
|
// XXX: this should do the "Reinitialize url" steps, and "null" should
|
||||
|
// be a valid return value.
|
||||
|
return new URL(this.href); |
||||
|
} }, |
||||
|
|
||||
|
protocol: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url && url.scheme) return url.scheme + ":"; |
||||
|
else return ":"; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute()) { |
||||
|
v = v.replace(/:+$/, ""); |
||||
|
v = v.replace(/[^-+\.a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
if (v.length > 0) { |
||||
|
url.scheme = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
host: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url.isAbsolute() && url.isAuthorityBased()) |
||||
|
return url.host + (url.port ? (":" + url.port) : ""); |
||||
|
else |
||||
|
return ""; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute() && url.isAuthorityBased()) { |
||||
|
v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
if (v.length > 0) { |
||||
|
url.host = v; |
||||
|
delete url.port; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
hostname: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url.isAbsolute() && url.isAuthorityBased()) |
||||
|
return url.host; |
||||
|
else |
||||
|
return ""; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute() && url.isAuthorityBased()) { |
||||
|
v = v.replace(/^\/+/, ""); |
||||
|
v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
if (v.length > 0) { |
||||
|
url.host = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
port: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url.isAbsolute() && url.isAuthorityBased() && url.port!==undefined) |
||||
|
return url.port; |
||||
|
else |
||||
|
return ""; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute() && url.isAuthorityBased()) { |
||||
|
v = '' + v; |
||||
|
v = v.replace(/[^0-9].*$/, ""); |
||||
|
v = v.replace(/^0+/, ""); |
||||
|
if (v.length === 0) v = "0"; |
||||
|
if (parseInt(v, 10) <= 65535) { |
||||
|
url.port = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
pathname: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url.isAbsolute() && url.isHierarchical()) |
||||
|
return url.path; |
||||
|
else |
||||
|
return ""; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute() && url.isHierarchical()) { |
||||
|
if (v.charAt(0) !== "/") |
||||
|
v = "/" + v; |
||||
|
v = v.replace(/[^-+\._~!$&'()*,;:=@\/a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
url.path = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
search: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url.isAbsolute() && url.isHierarchical() && url.query!==undefined) |
||||
|
return "?" + url.query; |
||||
|
else |
||||
|
return ""; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute() && url.isHierarchical()) { |
||||
|
if (v.charAt(0) === "?") v = v.substring(1); |
||||
|
v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
url.query = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
hash: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
if (url == null || url.fragment == null || url.fragment === '') { |
||||
|
return ""; |
||||
|
} else { |
||||
|
return "#" + url.fragment; |
||||
|
} |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
|
||||
|
if (v.charAt(0) === "#") v = v.substring(1); |
||||
|
v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode); |
||||
|
url.fragment = v; |
||||
|
output = url.toString(); |
||||
|
|
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
username: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
return url.username || ''; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute()) { |
||||
|
v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\:]/g, URL.percentEncode); |
||||
|
url.username = v; |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
password: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
return url.password || ''; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
if (url.isAbsolute()) { |
||||
|
if (v==='') { |
||||
|
url.password = null; |
||||
|
} else { |
||||
|
v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\]/g, URL.percentEncode); |
||||
|
url.password = v; |
||||
|
} |
||||
|
output = url.toString(); |
||||
|
} |
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
origin: { get: function() { |
||||
|
var url = this._url; |
||||
|
if (url == null) { return ''; } |
||||
|
var originForPort = function(defaultPort) { |
||||
|
var origin = [url.scheme, url.host, +url.port || defaultPort]; |
||||
|
// XXX should be "unicode serialization"
|
||||
|
return origin[0] + '://' + origin[1] + |
||||
|
(origin[2] === defaultPort ? '' : (':' + origin[2])); |
||||
|
}; |
||||
|
switch (url.scheme) { |
||||
|
case 'ftp': |
||||
|
return originForPort(21); |
||||
|
case 'gopher': |
||||
|
return originForPort(70); |
||||
|
case 'http': |
||||
|
case 'ws': |
||||
|
return originForPort(80); |
||||
|
case 'https': |
||||
|
case 'wss': |
||||
|
return originForPort(443); |
||||
|
default: |
||||
|
// this is what chrome does
|
||||
|
return url.scheme + '://'; |
||||
|
} |
||||
|
} }, |
||||
|
|
||||
|
/* |
||||
|
searchParams: { |
||||
|
get: function() { |
||||
|
var url = this._url; |
||||
|
// XXX
|
||||
|
}, |
||||
|
set: function(v) { |
||||
|
var output = this.href; |
||||
|
var url = new URL(output); |
||||
|
// XXX
|
||||
|
this.href = output; |
||||
|
}, |
||||
|
}, |
||||
|
*/ |
||||
|
}); |
||||
|
|
||||
|
URLUtils._inherit = function(proto) { |
||||
|
// copy getters/setters from URLUtils to o.
|
||||
|
Object.getOwnPropertyNames(URLUtils.prototype).forEach(function(p) { |
||||
|
if (p==='constructor' || p==='href') { return; } |
||||
|
var desc = Object.getOwnPropertyDescriptor(URLUtils.prototype, p); |
||||
|
Object.defineProperty(proto, p, desc); |
||||
|
}); |
||||
|
}; |
@ -0,0 +1,62 @@ |
|||||
|
"use strict"; |
||||
|
var DOMImplementation = require('./DOMImplementation.js'); |
||||
|
var EventTarget = require('./EventTarget.js'); |
||||
|
var Location = require('./Location.js'); |
||||
|
var sloppy = require('./sloppy.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
module.exports = Window; |
||||
|
|
||||
|
function Window(document) { |
||||
|
this.document = document || new DOMImplementation(null).createHTMLDocument(""); |
||||
|
this.document._scripting_enabled = true; |
||||
|
this.document.defaultView = this; |
||||
|
this.location = new Location(this, this.document._address || 'about:blank'); |
||||
|
} |
||||
|
|
||||
|
Window.prototype = Object.create(EventTarget.prototype, { |
||||
|
_run: { value: sloppy.Window_run }, |
||||
|
console: { value: console }, |
||||
|
history: { value: { |
||||
|
back: utils.nyi, |
||||
|
forward: utils.nyi, |
||||
|
go: utils.nyi |
||||
|
}}, |
||||
|
navigator: { value: require("./NavigatorID.js") }, |
||||
|
|
||||
|
// Self-referential properties
|
||||
|
window: { get: function() { return this; }}, |
||||
|
self: { get: function() { return this; }}, |
||||
|
frames: { get: function() { return this; }}, |
||||
|
|
||||
|
// Self-referential properties for a top-level window
|
||||
|
parent: { get: function() { return this; }}, |
||||
|
top: { get: function() { return this; }}, |
||||
|
|
||||
|
// We don't support any other windows for now
|
||||
|
length: { value: 0 }, // no frames
|
||||
|
frameElement: { value: null }, // not part of a frame
|
||||
|
opener: { value: null }, // not opened by another window
|
||||
|
|
||||
|
// The onload event handler.
|
||||
|
// XXX: need to support a bunch of other event types, too,
|
||||
|
// and have them interoperate with document.body.
|
||||
|
|
||||
|
onload: { |
||||
|
get: function() { |
||||
|
return this._getEventHandler("load"); |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
this._setEventHandler("load", v); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// XXX This is a completely broken implementation
|
||||
|
getComputedStyle: { value: function getComputedStyle(elt) { |
||||
|
return elt.style; |
||||
|
}} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
utils.expose(require('./WindowTimers.js'), Window); |
||||
|
utils.expose(require('./impl.js'), Window); |
@ -0,0 +1,11 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
// https://html.spec.whatwg.org/multipage/webappapis.html#windowtimers
|
||||
|
var WindowTimers = { |
||||
|
setTimeout: setTimeout, |
||||
|
clearTimeout: clearTimeout, |
||||
|
setInterval: setInterval, |
||||
|
clearInterval: clearInterval |
||||
|
}; |
||||
|
|
||||
|
module.exports = WindowTimers; |
@ -0,0 +1,152 @@ |
|||||
|
"use strict"; |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
exports.property = function(attr) { |
||||
|
if (Array.isArray(attr.type)) { |
||||
|
var valid = Object.create(null); |
||||
|
attr.type.forEach(function(val) { |
||||
|
valid[val.value || val] = val.alias || val; |
||||
|
}); |
||||
|
var missingValueDefault = attr.missing; |
||||
|
if (missingValueDefault===undefined) { missingValueDefault = null; } |
||||
|
var invalidValueDefault = attr.invalid; |
||||
|
if (invalidValueDefault===undefined) { invalidValueDefault = missingValueDefault; } |
||||
|
return { |
||||
|
get: function() { |
||||
|
var v = this._getattr(attr.name); |
||||
|
if (v === null) return missingValueDefault; |
||||
|
|
||||
|
v = valid[v.toLowerCase()]; |
||||
|
if (v !== undefined) return v; |
||||
|
if (invalidValueDefault !== null) return invalidValueDefault; |
||||
|
return v; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
this._setattr(attr.name, v); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
else if (attr.type === Boolean) { |
||||
|
return { |
||||
|
get: function() { |
||||
|
return this.hasAttribute(attr.name); |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
if (v) { |
||||
|
this._setattr(attr.name, ''); |
||||
|
} |
||||
|
else { |
||||
|
this.removeAttribute(attr.name); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
else if (attr.type === Number || |
||||
|
attr.type === "long" || |
||||
|
attr.type === "unsigned long" || |
||||
|
attr.type === "limited unsigned long with fallback") { |
||||
|
return numberPropDesc(attr); |
||||
|
} |
||||
|
else if (!attr.type || attr.type === String) { |
||||
|
return { |
||||
|
get: function() { return this._getattr(attr.name) || ''; }, |
||||
|
set: function(v) { |
||||
|
if (attr.treatNullAsEmptyString && v === null) { v = ''; } |
||||
|
this._setattr(attr.name, v); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
else if (typeof attr.type === 'function') { |
||||
|
return attr.type(attr.name, attr); |
||||
|
} |
||||
|
throw new Error('Invalid attribute definition'); |
||||
|
}; |
||||
|
|
||||
|
// See http://www.whatwg.org/specs/web-apps/current-work/#reflect
|
||||
|
//
|
||||
|
// defval is the default value. If it is a function, then that function
|
||||
|
// will be invoked as a method of the element to obtain the default.
|
||||
|
// If no default is specified for a given attribute, then the default
|
||||
|
// depends on the type of the attribute, but since this function handles
|
||||
|
// 4 integer cases, you must specify the default value in each call
|
||||
|
//
|
||||
|
// min and max define a valid range for getting the attribute.
|
||||
|
//
|
||||
|
// setmin defines a minimum value when setting. If the value is less
|
||||
|
// than that, then throw INDEX_SIZE_ERR.
|
||||
|
//
|
||||
|
// Conveniently, JavaScript's parseInt function appears to be
|
||||
|
// compatible with HTML's 'rules for parsing integers'
|
||||
|
function numberPropDesc(a) { |
||||
|
var def; |
||||
|
if(typeof a.default === 'function') { |
||||
|
def = a.default; |
||||
|
} |
||||
|
else if(typeof a.default === 'number') { |
||||
|
def = function() { return a.default; }; |
||||
|
} |
||||
|
else { |
||||
|
def = function() { utils.assert(false, typeof a.default); }; |
||||
|
} |
||||
|
var unsigned_long = (a.type === 'unsigned long'); |
||||
|
var signed_long = (a.type === 'long'); |
||||
|
var unsigned_fallback = (a.type === 'limited unsigned long with fallback'); |
||||
|
var min = a.min, max = a.max, setmin = a.setmin; |
||||
|
if (min === undefined) { |
||||
|
if (unsigned_long) min = 0; |
||||
|
if (signed_long) min = -0x80000000; |
||||
|
if (unsigned_fallback) min = 1; |
||||
|
} |
||||
|
if (max === undefined) { |
||||
|
if (unsigned_long || signed_long || unsigned_fallback) max = 0x7FFFFFFF; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
get: function() { |
||||
|
var v = this._getattr(a.name); |
||||
|
var n = a.float ? parseFloat(v) : parseInt(v, 10); |
||||
|
if (v === null || !isFinite(n) || (min !== undefined && n < min) || (max !== undefined && n > max)) { |
||||
|
return def.call(this); |
||||
|
} |
||||
|
if (unsigned_long || signed_long || unsigned_fallback) { |
||||
|
if (!/^[ \t\n\f\r]*[-+]?[0-9]/.test(v)) { return def.call(this); } |
||||
|
n = n|0; // jshint ignore:line
|
||||
|
} |
||||
|
return n; |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
if (!a.float) { v = Math.floor(v); } |
||||
|
if (setmin !== undefined && v < setmin) { |
||||
|
utils.IndexSizeError(a.name + ' set to ' + v); |
||||
|
} |
||||
|
if (unsigned_long) { |
||||
|
v = (v < 0 || v > 0x7FFFFFFF) ? def.call(this) : |
||||
|
(v|0); // jshint ignore:line
|
||||
|
} else if (unsigned_fallback) { |
||||
|
v = (v < 1 || v > 0x7FFFFFFF) ? def.call(this) : |
||||
|
(v|0); // jshint ignore:line
|
||||
|
} else if (signed_long) { |
||||
|
v = (v < -0x80000000 || v > 0x7FFFFFFF) ? def.call(this) : |
||||
|
(v|0); // jshint ignore:line
|
||||
|
} |
||||
|
this._setattr(a.name, String(v)); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// This is a utility function for setting up change handler functions
|
||||
|
// for attributes like 'id' that require special handling when they change.
|
||||
|
exports.registerChangeHandler = function(c, name, handler) { |
||||
|
var p = c.prototype; |
||||
|
|
||||
|
// If p does not already have its own _attributeChangeHandlers
|
||||
|
// then create one for it, inheriting from the inherited
|
||||
|
// _attributeChangeHandlers. At the top (for the Element class) the
|
||||
|
// _attributeChangeHandlers object will be created with a null prototype.
|
||||
|
if (!Object.prototype.hasOwnProperty.call(p, '_attributeChangeHandlers')) { |
||||
|
p._attributeChangeHandlers = |
||||
|
Object.create(p._attributeChangeHandlers || null); |
||||
|
} |
||||
|
|
||||
|
p._attributeChangeHandlers[name] = handler; |
||||
|
}; |
@ -0,0 +1,7 @@ |
|||||
|
/* |
||||
|
* This file defines Domino behaviour that can be externally configured. |
||||
|
* To change these settings, set the relevant global property *before* |
||||
|
* you call `require("domino")`. |
||||
|
*/ |
||||
|
|
||||
|
exports.isApiWritable = false; |
6654
domino/cssparser.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,70 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
var attributes = require('./attributes.js'); |
||||
|
var sloppy = require('./sloppy.js'); |
||||
|
var isApiWritable = require("./config.js").isApiWritable; |
||||
|
|
||||
|
module.exports = function(spec, defaultConstructor, tagList, tagNameToImpl) { |
||||
|
var c = spec.ctor; |
||||
|
if (c) { |
||||
|
var props = spec.props || {}; |
||||
|
|
||||
|
if (spec.attributes) { |
||||
|
for (var n in spec.attributes) { |
||||
|
var attr = spec.attributes[n]; |
||||
|
if (typeof attr !== 'object' || Array.isArray(attr)) attr = {type: attr}; |
||||
|
if (!attr.name) attr.name = n.toLowerCase(); |
||||
|
props[n] = attributes.property(attr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
props.constructor = { value : c, writable: isApiWritable }; |
||||
|
c.prototype = Object.create((spec.superclass || defaultConstructor).prototype, props); |
||||
|
if (spec.events) { |
||||
|
addEventHandlers(c, spec.events); |
||||
|
} |
||||
|
tagList[c.name] = c; |
||||
|
} |
||||
|
else { |
||||
|
c = defaultConstructor; |
||||
|
} |
||||
|
|
||||
|
(spec.tags || spec.tag && [spec.tag] || []).forEach(function(tag) { |
||||
|
tagNameToImpl[tag] = c; |
||||
|
}); |
||||
|
|
||||
|
return c; |
||||
|
}; |
||||
|
|
||||
|
function EventHandlerBuilder(body, document, form, element) { |
||||
|
this.body = body; |
||||
|
this.document = document; |
||||
|
this.form = form; |
||||
|
this.element = element; |
||||
|
} |
||||
|
|
||||
|
EventHandlerBuilder.prototype.build = sloppy.EventHandlerBuilder_build; |
||||
|
|
||||
|
function EventHandlerChangeHandler(elt, name, oldval, newval) { |
||||
|
var doc = elt.ownerDocument || Object.create(null); |
||||
|
var form = elt.form || Object.create(null); |
||||
|
elt[name] = new EventHandlerBuilder(newval, doc, form, elt).build(); |
||||
|
} |
||||
|
|
||||
|
function addEventHandlers(c, eventHandlerTypes) { |
||||
|
var p = c.prototype; |
||||
|
eventHandlerTypes.forEach(function(type) { |
||||
|
// Define the event handler registration IDL attribute for this type
|
||||
|
Object.defineProperty(p, "on" + type, { |
||||
|
get: function() { |
||||
|
return this._getEventHandler(type); |
||||
|
}, |
||||
|
set: function(v) { |
||||
|
this._setEventHandler(type, v); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
// Define special behavior for the content attribute as well
|
||||
|
attributes.registerChangeHandler(c, "on" + type, EventHandlerChangeHandler); |
||||
|
}); |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
"use strict"; |
||||
|
module.exports = { |
||||
|
Event: require('./Event.js'), |
||||
|
UIEvent: require('./UIEvent.js'), |
||||
|
MouseEvent: require('./MouseEvent.js'), |
||||
|
CustomEvent: require('./CustomEvent.js') |
||||
|
}; |
1426
domino/htmlelts.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,27 @@ |
|||||
|
"use strict"; |
||||
|
var utils = require('./utils.js'); |
||||
|
|
||||
|
exports = module.exports = { |
||||
|
CSSStyleDeclaration: require('./CSSStyleDeclaration.js'), |
||||
|
CharacterData: require('./CharacterData.js'), |
||||
|
Comment: require('./Comment.js'), |
||||
|
DOMException: require('./DOMException.js'), |
||||
|
DOMImplementation: require('./DOMImplementation.js'), |
||||
|
DOMTokenList: require('./DOMTokenList.js'), |
||||
|
Document: require('./Document.js'), |
||||
|
DocumentFragment: require('./DocumentFragment.js'), |
||||
|
DocumentType: require('./DocumentType.js'), |
||||
|
Element: require('./Element.js'), |
||||
|
HTMLParser: require('./HTMLParser.js'), |
||||
|
NamedNodeMap: require('./NamedNodeMap.js'), |
||||
|
Node: require('./Node.js'), |
||||
|
NodeList: require('./NodeList.js'), |
||||
|
NodeFilter: require('./NodeFilter.js'), |
||||
|
ProcessingInstruction: require('./ProcessingInstruction.js'), |
||||
|
Text: require('./Text.js'), |
||||
|
Window: require('./Window.js') |
||||
|
}; |
||||
|
|
||||
|
utils.merge(exports, require('./events.js')); |
||||
|
utils.merge(exports, require('./htmlelts.js').elements); |
||||
|
utils.merge(exports, require('./svg.js').elements); |
@ -0,0 +1,5 @@ |
|||||
|
declare module "domino" { |
||||
|
function createDOMImplementation(): DOMImplementation; |
||||
|
function createDocument(html?: string, force?: boolean): Document; |
||||
|
function createWindow(html?: string, address?: string): Window; |
||||
|
} |
@ -0,0 +1,79 @@ |
|||||
|
"use strict"; |
||||
|
var DOMImplementation = require('./DOMImplementation.js'); |
||||
|
var HTMLParser = require('./HTMLParser.js'); |
||||
|
var Window = require('./Window.js'); |
||||
|
|
||||
|
exports.createDOMImplementation = function() { |
||||
|
return new DOMImplementation(null); |
||||
|
}; |
||||
|
|
||||
|
exports.createDocument = function(html, force) { |
||||
|
// Previous API couldn't let you pass '' as a document, and that
|
||||
|
// yields a slightly different document than createHTMLDocument('')
|
||||
|
// does. The new `force` parameter lets you pass '' if you want to.
|
||||
|
if (html || force) { |
||||
|
var parser = new HTMLParser(); |
||||
|
parser.parse(html || '', true); |
||||
|
return parser.document(); |
||||
|
} |
||||
|
return new DOMImplementation(null).createHTMLDocument(""); |
||||
|
}; |
||||
|
|
||||
|
exports.createIncrementalHTMLParser = function() { |
||||
|
var parser = new HTMLParser(); |
||||
|
/** API for incremental parser. */ |
||||
|
return { |
||||
|
/** Provide an additional chunk of text to be parsed. */ |
||||
|
write: function(s) { |
||||
|
if (s.length > 0) { |
||||
|
parser.parse(s, false, function() { return true; }); |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* Signal that we are done providing input text, optionally |
||||
|
* providing one last chunk as a parameter. |
||||
|
*/ |
||||
|
end: function(s) { |
||||
|
parser.parse(s || '', true, function() { return true; }); |
||||
|
}, |
||||
|
/** |
||||
|
* Performs a chunk of parsing work, returning at the end of |
||||
|
* the next token as soon as shouldPauseFunc() returns true. |
||||
|
* Returns true iff there is more work to do. |
||||
|
* |
||||
|
* For example: |
||||
|
* ```
|
||||
|
* var incrParser = domino.createIncrementalHTMLParser(); |
||||
|
* incrParser.end('...long html document...'); |
||||
|
* while (true) { |
||||
|
* // Pause every 10ms
|
||||
|
* var start = Date.now(); |
||||
|
* var pauseIn10 = function() { return (Date.now() - start) >= 10; }; |
||||
|
* if (!incrParser.process(pauseIn10)) { |
||||
|
* break; |
||||
|
* } |
||||
|
* ...yield to other tasks, do other housekeeping, etc... |
||||
|
* } |
||||
|
* ```
|
||||
|
*/ |
||||
|
process: function(shouldPauseFunc) { |
||||
|
return parser.parse('', false, shouldPauseFunc); |
||||
|
}, |
||||
|
/** |
||||
|
* Returns the result of the incremental parse. Valid after |
||||
|
* `this.end()` has been called and `this.process()` has returned |
||||
|
* false. |
||||
|
*/ |
||||
|
document: function() { |
||||
|
return parser.document(); |
||||
|
}, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
exports.createWindow = function(html, address) { |
||||
|
var document = exports.createDocument(html); |
||||
|
if (address !== undefined) { document._address = address; } |
||||
|
return new Window(document); |
||||
|
}; |
||||
|
|
||||
|
exports.impl = require('./impl.js'); |
2938
domino/jquery.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,933 @@ |
|||||
|
"use strict"; |
||||
|
/* jshint eqnull: true */ |
||||
|
/** |
||||
|
* Zest (https://github.com/chjj/zest)
|
||||
|
* A css selector engine. |
||||
|
* Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed) |
||||
|
* Domino version based on Zest v0.1.3 with bugfixes applied. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Helpers |
||||
|
*/ |
||||
|
|
||||
|
var window = Object.create(null, { |
||||
|
location: { get: function() { |
||||
|
throw new Error('window.location is not supported.'); |
||||
|
} } |
||||
|
}); |
||||
|
|
||||
|
var compareDocumentPosition = function(a, b) { |
||||
|
return a.compareDocumentPosition(b); |
||||
|
}; |
||||
|
|
||||
|
var order = function(a, b) { |
||||
|
/* jshint bitwise: false */ |
||||
|
return compareDocumentPosition(a, b) & 2 ? 1 : -1; |
||||
|
}; |
||||
|
|
||||
|
var next = function(el) { |
||||
|
while ((el = el.nextSibling) |
||||
|
&& el.nodeType !== 1); |
||||
|
return el; |
||||
|
}; |
||||
|
|
||||
|
var prev = function(el) { |
||||
|
while ((el = el.previousSibling) |
||||
|
&& el.nodeType !== 1); |
||||
|
return el; |
||||
|
}; |
||||
|
|
||||
|
var child = function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
if (el = el.firstChild) { |
||||
|
while (el.nodeType !== 1 |
||||
|
&& (el = el.nextSibling)); |
||||
|
} |
||||
|
return el; |
||||
|
}; |
||||
|
|
||||
|
var lastChild = function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
if (el = el.lastChild) { |
||||
|
while (el.nodeType !== 1 |
||||
|
&& (el = el.previousSibling)); |
||||
|
} |
||||
|
return el; |
||||
|
}; |
||||
|
|
||||
|
var parentIsElement = function(n) { |
||||
|
if (!n.parentNode) { return false; } |
||||
|
var nodeType = n.parentNode.nodeType; |
||||
|
// The root `html` element can be a first- or last-child, too.
|
||||
|
return nodeType === 1 || nodeType === 9; |
||||
|
}; |
||||
|
|
||||
|
var unquote = function(str) { |
||||
|
if (!str) return str; |
||||
|
var ch = str[0]; |
||||
|
if (ch === '"' || ch === '\'') { |
||||
|
if (str[str.length-1] === ch) { |
||||
|
str = str.slice(1, -1); |
||||
|
} else { |
||||
|
// bad string.
|
||||
|
str = str.slice(1); |
||||
|
} |
||||
|
return str.replace(rules.str_escape, function(s) { |
||||
|
var m = /^\\(?:([0-9A-Fa-f]+)|([\r\n\f]+))/.exec(s); |
||||
|
if (!m) { return s.slice(1); } |
||||
|
if (m[2]) { return ''; /* escaped newlines are ignored in strings. */ } |
||||
|
var cp = parseInt(m[1], 16); |
||||
|
return String.fromCodePoint ? String.fromCodePoint(cp) : |
||||
|
// Not all JavaScript implementations have String.fromCodePoint yet.
|
||||
|
String.fromCharCode(cp); |
||||
|
}); |
||||
|
} else if (rules.ident.test(str)) { |
||||
|
return decodeid(str); |
||||
|
} else { |
||||
|
// NUMBER, PERCENTAGE, DIMENSION, etc
|
||||
|
return str; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var decodeid = function(str) { |
||||
|
return str.replace(rules.escape, function(s) { |
||||
|
var m = /^\\([0-9A-Fa-f]+)/.exec(s); |
||||
|
if (!m) { return s[1]; } |
||||
|
var cp = parseInt(m[1], 16); |
||||
|
return String.fromCodePoint ? String.fromCodePoint(cp) : |
||||
|
// Not all JavaScript implementations have String.fromCodePoint yet.
|
||||
|
String.fromCharCode(cp); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
var indexOf = (function() { |
||||
|
if (Array.prototype.indexOf) { |
||||
|
return Array.prototype.indexOf; |
||||
|
} |
||||
|
return function(obj, item) { |
||||
|
var i = this.length; |
||||
|
while (i--) { |
||||
|
if (this[i] === item) return i; |
||||
|
} |
||||
|
return -1; |
||||
|
}; |
||||
|
})(); |
||||
|
|
||||
|
var makeInside = function(start, end) { |
||||
|
var regex = rules.inside.source |
||||
|
.replace(/</g, start) |
||||
|
.replace(/>/g, end); |
||||
|
|
||||
|
return new RegExp(regex); |
||||
|
}; |
||||
|
|
||||
|
var replace = function(regex, name, val) { |
||||
|
regex = regex.source; |
||||
|
regex = regex.replace(name, val.source || val); |
||||
|
return new RegExp(regex); |
||||
|
}; |
||||
|
|
||||
|
var truncateUrl = function(url, num) { |
||||
|
return url |
||||
|
.replace(/^(?:\w+:\/\/|\/+)/, '') |
||||
|
.replace(/(?:\/+|\/*#.*?)$/, '') |
||||
|
.split('/', num) |
||||
|
.join('/'); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Handle `nth` Selectors |
||||
|
*/ |
||||
|
|
||||
|
var parseNth = function(param_, test) { |
||||
|
var param = param_.replace(/\s+/g, '') |
||||
|
, cap; |
||||
|
|
||||
|
if (param === 'even') { |
||||
|
param = '2n+0'; |
||||
|
} else if (param === 'odd') { |
||||
|
param = '2n+1'; |
||||
|
} else if (param.indexOf('n') === -1) { |
||||
|
param = '0n' + param; |
||||
|
} |
||||
|
|
||||
|
cap = /^([+-])?(\d+)?n([+-])?(\d+)?$/.exec(param); |
||||
|
|
||||
|
return { |
||||
|
group: cap[1] === '-' |
||||
|
? -(cap[2] || 1) |
||||
|
: +(cap[2] || 1), |
||||
|
offset: cap[4] |
||||
|
? (cap[3] === '-' ? -cap[4] : +cap[4]) |
||||
|
: 0 |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
var nth = function(param_, test, last) { |
||||
|
var param = parseNth(param_) |
||||
|
, group = param.group |
||||
|
, offset = param.offset |
||||
|
, find = !last ? child : lastChild |
||||
|
, advance = !last ? next : prev; |
||||
|
|
||||
|
return function(el) { |
||||
|
if (!parentIsElement(el)) return; |
||||
|
|
||||
|
var rel = find(el.parentNode) |
||||
|
, pos = 0; |
||||
|
|
||||
|
while (rel) { |
||||
|
if (test(rel, el)) pos++; |
||||
|
if (rel === el) { |
||||
|
pos -= offset; |
||||
|
return group && pos |
||||
|
? (pos % group) === 0 && (pos < 0 === group < 0) |
||||
|
: !pos; |
||||
|
} |
||||
|
rel = advance(rel); |
||||
|
} |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Simple Selectors |
||||
|
*/ |
||||
|
|
||||
|
var selectors = { |
||||
|
'*': (function() { |
||||
|
if (false/*function() { |
||||
|
var el = document.createElement('div'); |
||||
|
el.appendChild(document.createComment('')); |
||||
|
return !!el.getElementsByTagName('*')[0]; |
||||
|
}()*/) { |
||||
|
return function(el) { |
||||
|
if (el.nodeType === 1) return true; |
||||
|
}; |
||||
|
} |
||||
|
return function() { |
||||
|
return true; |
||||
|
}; |
||||
|
})(), |
||||
|
'type': function(type) { |
||||
|
type = type.toLowerCase(); |
||||
|
return function(el) { |
||||
|
return el.nodeName.toLowerCase() === type; |
||||
|
}; |
||||
|
}, |
||||
|
'attr': function(key, op, val, i) { |
||||
|
op = operators[op]; |
||||
|
return function(el) { |
||||
|
var attr; |
||||
|
switch (key) { |
||||
|
case 'for': |
||||
|
attr = el.htmlFor; |
||||
|
break; |
||||
|
case 'class': |
||||
|
// className is '' when non-existent
|
||||
|
// getAttribute('class') is null
|
||||
|
attr = el.className; |
||||
|
if (attr === '' && el.getAttribute('class') == null) { |
||||
|
attr = null; |
||||
|
} |
||||
|
break; |
||||
|
case 'href': |
||||
|
case 'src': |
||||
|
attr = el.getAttribute(key, 2); |
||||
|
break; |
||||
|
case 'title': |
||||
|
// getAttribute('title') can be '' when non-existent sometimes?
|
||||
|
attr = el.getAttribute('title') || null; |
||||
|
break; |
||||
|
// careful with attributes with special getter functions
|
||||
|
case 'id': |
||||
|
case 'lang': |
||||
|
case 'dir': |
||||
|
case 'accessKey': |
||||
|
case 'hidden': |
||||
|
case 'tabIndex': |
||||
|
case 'style': |
||||
|
if (el.getAttribute) { |
||||
|
attr = el.getAttribute(key); |
||||
|
break; |
||||
|
} |
||||
|
/* falls through */ |
||||
|
default: |
||||
|
if (el.hasAttribute && !el.hasAttribute(key)) { |
||||
|
break; |
||||
|
} |
||||
|
attr = el[key] != null |
||||
|
? el[key] |
||||
|
: el.getAttribute && el.getAttribute(key); |
||||
|
break; |
||||
|
} |
||||
|
if (attr == null) return; |
||||
|
attr = attr + ''; |
||||
|
if (i) { |
||||
|
attr = attr.toLowerCase(); |
||||
|
val = val.toLowerCase(); |
||||
|
} |
||||
|
return op(attr, val); |
||||
|
}; |
||||
|
}, |
||||
|
':first-child': function(el) { |
||||
|
return !prev(el) && parentIsElement(el); |
||||
|
}, |
||||
|
':last-child': function(el) { |
||||
|
return !next(el) && parentIsElement(el); |
||||
|
}, |
||||
|
':only-child': function(el) { |
||||
|
return !prev(el) && !next(el) && parentIsElement(el); |
||||
|
}, |
||||
|
':nth-child': function(param, last) { |
||||
|
return nth(param, function() { |
||||
|
return true; |
||||
|
}, last); |
||||
|
}, |
||||
|
':nth-last-child': function(param) { |
||||
|
return selectors[':nth-child'](param, true); |
||||
|
}, |
||||
|
':root': function(el) { |
||||
|
return el.ownerDocument.documentElement === el; |
||||
|
}, |
||||
|
':empty': function(el) { |
||||
|
return !el.firstChild; |
||||
|
}, |
||||
|
':not': function(sel) { |
||||
|
var test = compileGroup(sel); |
||||
|
return function(el) { |
||||
|
return !test(el); |
||||
|
}; |
||||
|
}, |
||||
|
':first-of-type': function(el) { |
||||
|
if (!parentIsElement(el)) return; |
||||
|
var type = el.nodeName; |
||||
|
/*jshint -W084 */ |
||||
|
while (el = prev(el)) { |
||||
|
if (el.nodeName === type) return; |
||||
|
} |
||||
|
return true; |
||||
|
}, |
||||
|
':last-of-type': function(el) { |
||||
|
if (!parentIsElement(el)) return; |
||||
|
var type = el.nodeName; |
||||
|
/*jshint -W084 */ |
||||
|
while (el = next(el)) { |
||||
|
if (el.nodeName === type) return; |
||||
|
} |
||||
|
return true; |
||||
|
}, |
||||
|
':only-of-type': function(el) { |
||||
|
return selectors[':first-of-type'](el) |
||||
|
&& selectors[':last-of-type'](el); |
||||
|
}, |
||||
|
':nth-of-type': function(param, last) { |
||||
|
return nth(param, function(rel, el) { |
||||
|
return rel.nodeName === el.nodeName; |
||||
|
}, last); |
||||
|
}, |
||||
|
':nth-last-of-type': function(param) { |
||||
|
return selectors[':nth-of-type'](param, true); |
||||
|
}, |
||||
|
':checked': function(el) { |
||||
|
return !!(el.checked || el.selected); |
||||
|
}, |
||||
|
':indeterminate': function(el) { |
||||
|
return !selectors[':checked'](el); |
||||
|
}, |
||||
|
':enabled': function(el) { |
||||
|
return !el.disabled && el.type !== 'hidden'; |
||||
|
}, |
||||
|
':disabled': function(el) { |
||||
|
return !!el.disabled; |
||||
|
}, |
||||
|
':target': function(el) { |
||||
|
return el.id === window.location.hash.substring(1); |
||||
|
}, |
||||
|
':focus': function(el) { |
||||
|
return el === el.ownerDocument.activeElement; |
||||
|
}, |
||||
|
':is': function(sel) { |
||||
|
return compileGroup(sel); |
||||
|
}, |
||||
|
// :matches is an older name for :is; see
|
||||
|
// https://github.com/w3c/csswg-drafts/issues/3258
|
||||
|
':matches': function(sel) { |
||||
|
return selectors[':is'](sel); |
||||
|
}, |
||||
|
':nth-match': function(param, last) { |
||||
|
var args = param.split(/\s*,\s*/) |
||||
|
, arg = args.shift() |
||||
|
, test = compileGroup(args.join(',')); |
||||
|
|
||||
|
return nth(arg, test, last); |
||||
|
}, |
||||
|
':nth-last-match': function(param) { |
||||
|
return selectors[':nth-match'](param, true); |
||||
|
}, |
||||
|
':links-here': function(el) { |
||||
|
return el + '' === window.location + ''; |
||||
|
}, |
||||
|
':lang': function(param) { |
||||
|
return function(el) { |
||||
|
while (el) { |
||||
|
if (el.lang) return el.lang.indexOf(param) === 0; |
||||
|
el = el.parentNode; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
':dir': function(param) { |
||||
|
return function(el) { |
||||
|
while (el) { |
||||
|
if (el.dir) return el.dir === param; |
||||
|
el = el.parentNode; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
':scope': function(el, con) { |
||||
|
var context = con || el.ownerDocument; |
||||
|
if (context.nodeType === 9) { |
||||
|
return el === context.documentElement; |
||||
|
} |
||||
|
return el === context; |
||||
|
}, |
||||
|
':any-link': function(el) { |
||||
|
return typeof el.href === 'string'; |
||||
|
}, |
||||
|
':local-link': function(el) { |
||||
|
if (el.nodeName) { |
||||
|
return el.href && el.host === window.location.host; |
||||
|
} |
||||
|
var param = +el + 1; |
||||
|
return function(el) { |
||||
|
if (!el.href) return; |
||||
|
|
||||
|
var url = window.location + '' |
||||
|
, href = el + ''; |
||||
|
|
||||
|
return truncateUrl(url, param) === truncateUrl(href, param); |
||||
|
}; |
||||
|
}, |
||||
|
':default': function(el) { |
||||
|
return !!el.defaultSelected; |
||||
|
}, |
||||
|
':valid': function(el) { |
||||
|
return el.willValidate || (el.validity && el.validity.valid); |
||||
|
}, |
||||
|
':invalid': function(el) { |
||||
|
return !selectors[':valid'](el); |
||||
|
}, |
||||
|
':in-range': function(el) { |
||||
|
return el.value > el.min && el.value <= el.max; |
||||
|
}, |
||||
|
':out-of-range': function(el) { |
||||
|
return !selectors[':in-range'](el); |
||||
|
}, |
||||
|
':required': function(el) { |
||||
|
return !!el.required; |
||||
|
}, |
||||
|
':optional': function(el) { |
||||
|
return !el.required; |
||||
|
}, |
||||
|
':read-only': function(el) { |
||||
|
if (el.readOnly) return true; |
||||
|
|
||||
|
var attr = el.getAttribute('contenteditable') |
||||
|
, prop = el.contentEditable |
||||
|
, name = el.nodeName.toLowerCase(); |
||||
|
|
||||
|
name = name !== 'input' && name !== 'textarea'; |
||||
|
|
||||
|
return (name || el.disabled) && attr == null && prop !== 'true'; |
||||
|
}, |
||||
|
':read-write': function(el) { |
||||
|
return !selectors[':read-only'](el); |
||||
|
}, |
||||
|
':hover': function() { |
||||
|
throw new Error(':hover is not supported.'); |
||||
|
}, |
||||
|
':active': function() { |
||||
|
throw new Error(':active is not supported.'); |
||||
|
}, |
||||
|
':link': function() { |
||||
|
throw new Error(':link is not supported.'); |
||||
|
}, |
||||
|
':visited': function() { |
||||
|
throw new Error(':visited is not supported.'); |
||||
|
}, |
||||
|
':column': function() { |
||||
|
throw new Error(':column is not supported.'); |
||||
|
}, |
||||
|
':nth-column': function() { |
||||
|
throw new Error(':nth-column is not supported.'); |
||||
|
}, |
||||
|
':nth-last-column': function() { |
||||
|
throw new Error(':nth-last-column is not supported.'); |
||||
|
}, |
||||
|
':current': function() { |
||||
|
throw new Error(':current is not supported.'); |
||||
|
}, |
||||
|
':past': function() { |
||||
|
throw new Error(':past is not supported.'); |
||||
|
}, |
||||
|
':future': function() { |
||||
|
throw new Error(':future is not supported.'); |
||||
|
}, |
||||
|
// Non-standard, for compatibility purposes.
|
||||
|
':contains': function(param) { |
||||
|
return function(el) { |
||||
|
var text = el.innerText || el.textContent || el.value || ''; |
||||
|
return text.indexOf(param) !== -1; |
||||
|
}; |
||||
|
}, |
||||
|
':has': function(param) { |
||||
|
return function(el) { |
||||
|
return find(param, el).length > 0; |
||||
|
}; |
||||
|
} |
||||
|
// Potentially add more pseudo selectors for
|
||||
|
// compatibility with sizzle and most other
|
||||
|
// selector engines (?).
|
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Attribute Operators |
||||
|
*/ |
||||
|
|
||||
|
var operators = { |
||||
|
'-': function() { |
||||
|
return true; |
||||
|
}, |
||||
|
'=': function(attr, val) { |
||||
|
return attr === val; |
||||
|
}, |
||||
|
'*=': function(attr, val) { |
||||
|
return attr.indexOf(val) !== -1; |
||||
|
}, |
||||
|
'~=': function(attr, val) { |
||||
|
var i |
||||
|
, s |
||||
|
, f |
||||
|
, l; |
||||
|
|
||||
|
for (s = 0; true; s = i + 1) { |
||||
|
i = attr.indexOf(val, s); |
||||
|
if (i === -1) return false; |
||||
|
f = attr[i - 1]; |
||||
|
l = attr[i + val.length]; |
||||
|
if ((!f || f === ' ') && (!l || l === ' ')) return true; |
||||
|
} |
||||
|
}, |
||||
|
'|=': function(attr, val) { |
||||
|
var i = attr.indexOf(val) |
||||
|
, l; |
||||
|
|
||||
|
if (i !== 0) return; |
||||
|
l = attr[i + val.length]; |
||||
|
|
||||
|
return l === '-' || !l; |
||||
|
}, |
||||
|
'^=': function(attr, val) { |
||||
|
return attr.indexOf(val) === 0; |
||||
|
}, |
||||
|
'$=': function(attr, val) { |
||||
|
var i = attr.lastIndexOf(val); |
||||
|
return i !== -1 && i + val.length === attr.length; |
||||
|
}, |
||||
|
// non-standard
|
||||
|
'!=': function(attr, val) { |
||||
|
return attr !== val; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Combinator Logic |
||||
|
*/ |
||||
|
|
||||
|
var combinators = { |
||||
|
' ': function(test) { |
||||
|
return function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
while (el = el.parentNode) { |
||||
|
if (test(el)) return el; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
'>': function(test) { |
||||
|
return function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
if (el = el.parentNode) { |
||||
|
return test(el) && el; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
'+': function(test) { |
||||
|
return function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
if (el = prev(el)) { |
||||
|
return test(el) && el; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
'~': function(test) { |
||||
|
return function(el) { |
||||
|
/*jshint -W084 */ |
||||
|
while (el = prev(el)) { |
||||
|
if (test(el)) return el; |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
'noop': function(test) { |
||||
|
return function(el) { |
||||
|
return test(el) && el; |
||||
|
}; |
||||
|
}, |
||||
|
'ref': function(test, name) { |
||||
|
var node; |
||||
|
|
||||
|
function ref(el) { |
||||
|
var doc = el.ownerDocument |
||||
|
, nodes = doc.getElementsByTagName('*') |
||||
|
, i = nodes.length; |
||||
|
|
||||
|
while (i--) { |
||||
|
node = nodes[i]; |
||||
|
if (ref.test(el)) { |
||||
|
node = null; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
node = null; |
||||
|
} |
||||
|
|
||||
|
ref.combinator = function(el) { |
||||
|
if (!node || !node.getAttribute) return; |
||||
|
|
||||
|
var attr = node.getAttribute(name) || ''; |
||||
|
if (attr[0] === '#') attr = attr.substring(1); |
||||
|
|
||||
|
if (attr === el.id && test(node)) { |
||||
|
return node; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return ref; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Grammar |
||||
|
*/ |
||||
|
|
||||
|
var rules = { |
||||
|
escape: /\\(?:[^0-9A-Fa-f\r\n]|[0-9A-Fa-f]{1,6}[\r\n\t ]?)/g, |
||||
|
str_escape: /(escape)|\\(\n|\r\n?|\f)/g, |
||||
|
nonascii: /[\u00A0-\uFFFF]/, |
||||
|
cssid: /(?:(?!-?[0-9])(?:escape|nonascii|[-_a-zA-Z0-9])+)/, |
||||
|
qname: /^ *(cssid|\*)/, |
||||
|
simple: /^(?:([.#]cssid)|pseudo|attr)/, |
||||
|
ref: /^ *\/(cssid)\/ */, |
||||
|
combinator: /^(?: +([^ \w*.#\\]) +|( )+|([^ \w*.#\\]))(?! *$)/, |
||||
|
attr: /^\[(cssid)(?:([^\w]?=)(inside))?\]/, |
||||
|
pseudo: /^(:cssid)(?:\((inside)\))?/, |
||||
|
inside: /(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|<[^"'>]*>|\\["'>]|[^"'>])*/, |
||||
|
ident: /^(cssid)$/ |
||||
|
}; |
||||
|
|
||||
|
rules.cssid = replace(rules.cssid, 'nonascii', rules.nonascii); |
||||
|
rules.cssid = replace(rules.cssid, 'escape', rules.escape); |
||||
|
rules.qname = replace(rules.qname, 'cssid', rules.cssid); |
||||
|
rules.simple = replace(rules.simple, 'cssid', rules.cssid); |
||||
|
rules.ref = replace(rules.ref, 'cssid', rules.cssid); |
||||
|
rules.attr = replace(rules.attr, 'cssid', rules.cssid); |
||||
|
rules.pseudo = replace(rules.pseudo, 'cssid', rules.cssid); |
||||
|
rules.inside = replace(rules.inside, '[^"\'>]*', rules.inside); |
||||
|
rules.attr = replace(rules.attr, 'inside', makeInside('\\[', '\\]')); |
||||
|
rules.pseudo = replace(rules.pseudo, 'inside', makeInside('\\(', '\\)')); |
||||
|
rules.simple = replace(rules.simple, 'pseudo', rules.pseudo); |
||||
|
rules.simple = replace(rules.simple, 'attr', rules.attr); |
||||
|
rules.ident = replace(rules.ident, 'cssid', rules.cssid); |
||||
|
rules.str_escape = replace(rules.str_escape, 'escape', rules.escape); |
||||
|
|
||||
|
/** |
||||
|
* Compiling |
||||
|
*/ |
||||
|
|
||||
|
var compile = function(sel_) { |
||||
|
var sel = sel_.replace(/^\s+|\s+$/g, '') |
||||
|
, test |
||||
|
, filter = [] |
||||
|
, buff = [] |
||||
|
, subject |
||||
|
, qname |
||||
|
, cap |
||||
|
, op |
||||
|
, ref; |
||||
|
|
||||
|
/*jshint -W084 */ |
||||
|
while (sel) { |
||||
|
if (cap = rules.qname.exec(sel)) { |
||||
|
sel = sel.substring(cap[0].length); |
||||
|
qname = decodeid(cap[1]); |
||||
|
buff.push(tok(qname, true)); |
||||
|
} else if (cap = rules.simple.exec(sel)) { |
||||
|
sel = sel.substring(cap[0].length); |
||||
|
qname = '*'; |
||||
|
buff.push(tok(qname, true)); |
||||
|
buff.push(tok(cap)); |
||||
|
} else { |
||||
|
throw new SyntaxError('Invalid selector.'); |
||||
|
} |
||||
|
|
||||
|
while (cap = rules.simple.exec(sel)) { |
||||
|
sel = sel.substring(cap[0].length); |
||||
|
buff.push(tok(cap)); |
||||
|
} |
||||
|
|
||||
|
if (sel[0] === '!') { |
||||
|
sel = sel.substring(1); |
||||
|
subject = makeSubject(); |
||||
|
subject.qname = qname; |
||||
|
buff.push(subject.simple); |
||||
|
} |
||||
|
|
||||
|
if (cap = rules.ref.exec(sel)) { |
||||
|
sel = sel.substring(cap[0].length); |
||||
|
ref = combinators.ref(makeSimple(buff), decodeid(cap[1])); |
||||
|
filter.push(ref.combinator); |
||||
|
buff = []; |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (cap = rules.combinator.exec(sel)) { |
||||
|
sel = sel.substring(cap[0].length); |
||||
|
op = cap[1] || cap[2] || cap[3]; |
||||
|
if (op === ',') { |
||||
|
filter.push(combinators.noop(makeSimple(buff))); |
||||
|
break; |
||||
|
} |
||||
|
} else { |
||||
|
op = 'noop'; |
||||
|
} |
||||
|
|
||||
|
if (!combinators[op]) { throw new SyntaxError('Bad combinator.'); } |
||||
|
filter.push(combinators[op](makeSimple(buff))); |
||||
|
buff = []; |
||||
|
} |
||||
|
|
||||
|
test = makeTest(filter); |
||||
|
test.qname = qname; |
||||
|
test.sel = sel; |
||||
|
|
||||
|
if (subject) { |
||||
|
subject.lname = test.qname; |
||||
|
|
||||
|
subject.test = test; |
||||
|
subject.qname = subject.qname; |
||||
|
subject.sel = test.sel; |
||||
|
test = subject; |
||||
|
} |
||||
|
|
||||
|
if (ref) { |
||||
|
ref.test = test; |
||||
|
ref.qname = test.qname; |
||||
|
ref.sel = test.sel; |
||||
|
test = ref; |
||||
|
} |
||||
|
|
||||
|
return test; |
||||
|
}; |
||||
|
|
||||
|
var tok = function(cap, qname) { |
||||
|
// qname
|
||||
|
if (qname) { |
||||
|
return cap === '*' |
||||
|
? selectors['*'] |
||||
|
: selectors.type(cap); |
||||
|
} |
||||
|
|
||||
|
// class/id
|
||||
|
if (cap[1]) { |
||||
|
return cap[1][0] === '.' |
||||
|
// XXX unescape here? or in attr?
|
||||
|
? selectors.attr('class', '~=', decodeid(cap[1].substring(1)), false) |
||||
|
: selectors.attr('id', '=', decodeid(cap[1].substring(1)), false); |
||||
|
} |
||||
|
|
||||
|
// pseudo-name
|
||||
|
// inside-pseudo
|
||||
|
if (cap[2]) { |
||||
|
return cap[3] |
||||
|
? selectors[decodeid(cap[2])](unquote(cap[3])) |
||||
|
: selectors[decodeid(cap[2])]; |
||||
|
} |
||||
|
|
||||
|
// attr name
|
||||
|
// attr op
|
||||
|
// attr value
|
||||
|
if (cap[4]) { |
||||
|
var value = cap[6]; |
||||
|
var i = /["'\s]\s*I$/i.test(value); |
||||
|
if (i) { |
||||
|
value = value.replace(/\s*I$/i, ''); |
||||
|
} |
||||
|
return selectors.attr(decodeid(cap[4]), cap[5] || '-', unquote(value), i); |
||||
|
} |
||||
|
|
||||
|
throw new SyntaxError('Unknown Selector.'); |
||||
|
}; |
||||
|
|
||||
|
var makeSimple = function(func) { |
||||
|
var l = func.length |
||||
|
, i; |
||||
|
|
||||
|
// Potentially make sure
|
||||
|
// `el` is truthy.
|
||||
|
if (l < 2) return func[0]; |
||||
|
|
||||
|
return function(el) { |
||||
|
if (!el) return; |
||||
|
for (i = 0; i < l; i++) { |
||||
|
if (!func[i](el)) return; |
||||
|
} |
||||
|
return true; |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
var makeTest = function(func) { |
||||
|
if (func.length < 2) { |
||||
|
return function(el) { |
||||
|
return !!func[0](el); |
||||
|
}; |
||||
|
} |
||||
|
return function(el) { |
||||
|
var i = func.length; |
||||
|
while (i--) { |
||||
|
if (!(el = func[i](el))) return; |
||||
|
} |
||||
|
return true; |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
var makeSubject = function() { |
||||
|
var target; |
||||
|
|
||||
|
function subject(el) { |
||||
|
var node = el.ownerDocument |
||||
|
, scope = node.getElementsByTagName(subject.lname) |
||||
|
, i = scope.length; |
||||
|
|
||||
|
while (i--) { |
||||
|
if (subject.test(scope[i]) && target === el) { |
||||
|
target = null; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
target = null; |
||||
|
} |
||||
|
|
||||
|
subject.simple = function(el) { |
||||
|
target = el; |
||||
|
return true; |
||||
|
}; |
||||
|
|
||||
|
return subject; |
||||
|
}; |
||||
|
|
||||
|
var compileGroup = function(sel) { |
||||
|
var test = compile(sel) |
||||
|
, tests = [ test ]; |
||||
|
|
||||
|
while (test.sel) { |
||||
|
test = compile(test.sel); |
||||
|
tests.push(test); |
||||
|
} |
||||
|
|
||||
|
if (tests.length < 2) return test; |
||||
|
|
||||
|
return function(el) { |
||||
|
var l = tests.length |
||||
|
, i = 0; |
||||
|
|
||||
|
for (; i < l; i++) { |
||||
|
if (tests[i](el)) return true; |
||||
|
} |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Selection |
||||
|
*/ |
||||
|
|
||||
|
var find = function(sel, node) { |
||||
|
var results = [] |
||||
|
, test = compile(sel) |
||||
|
, scope = node.getElementsByTagName(test.qname) |
||||
|
, i = 0 |
||||
|
, el; |
||||
|
|
||||
|
/*jshint -W084 */ |
||||
|
while (el = scope[i++]) { |
||||
|
if (test(el)) results.push(el); |
||||
|
} |
||||
|
|
||||
|
if (test.sel) { |
||||
|
while (test.sel) { |
||||
|
test = compile(test.sel); |
||||
|
scope = node.getElementsByTagName(test.qname); |
||||
|
i = 0; |
||||
|
/*jshint -W084 */ |
||||
|
while (el = scope[i++]) { |
||||
|
if (test(el) && indexOf.call(results, el) === -1) { |
||||
|
results.push(el); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
results.sort(order); |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Expose |
||||
|
*/ |
||||
|
|
||||
|
module.exports = exports = function(sel, context) { |
||||
|
/* when context isn't a DocumentFragment and the selector is simple: */ |
||||
|
var id, r; |
||||
|
if (context.nodeType !== 11 && sel.indexOf(' ') === -1) { |
||||
|
if (sel[0] === '#' && context.rooted && /^#[A-Z_][-A-Z0-9_]*$/i.test(sel)) { |
||||
|
if (context.doc._hasMultipleElementsWithId) { |
||||
|
id = sel.substring(1); |
||||
|
if (!context.doc._hasMultipleElementsWithId(id)) { |
||||
|
r = context.doc.getElementById(id); |
||||
|
return r ? [r] : []; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (sel[0] === '.' && /^\.\w+$/.test(sel)) { |
||||
|
return context.getElementsByClassName(sel.substring(1)); |
||||
|
} |
||||
|
if (/^\w+$/.test(sel)) { |
||||
|
return context.getElementsByTagName(sel); |
||||
|
} |
||||
|
} |
||||
|
/* do things the hard/slow way */ |
||||
|
return find(sel, context); |
||||
|
}; |
||||
|
|
||||
|
exports.selectors = selectors; |
||||
|
exports.operators = operators; |
||||
|
exports.combinators = combinators; |
||||
|
|
||||
|
exports.matches = function(el, sel) { |
||||
|
var test = { sel: sel }; |
||||
|
do { |
||||
|
test = compile(test.sel); |
||||
|
if (test(el)) { return true; } |
||||
|
} while (test.sel); |
||||
|
return false; |
||||
|
}; |
@ -0,0 +1,24 @@ |
|||||
|
/* Domino uses sloppy-mode features (in particular, `with`) for a few |
||||
|
* minor things. This file encapsulates all the sloppiness; every |
||||
|
* other module should be strict. */ |
||||
|
/* jshint strict: false */ |
||||
|
/* jshint evil: true */ |
||||
|
/* jshint -W085 */ |
||||
|
module.exports = { |
||||
|
Window_run: function _run(code, file) { |
||||
|
if (file) code += '\n//@ sourceURL=' + file; |
||||
|
with(this) eval(code); |
||||
|
}, |
||||
|
EventHandlerBuilder_build: function build() { |
||||
|
try { |
||||
|
with(this.document.defaultView || Object.create(null)) |
||||
|
with(this.document) |
||||
|
with(this.form) |
||||
|
with(this.element) |
||||
|
return eval("(function(event){" + this.body + "})"); |
||||
|
} |
||||
|
catch (err) { |
||||
|
return function() { throw err; }; |
||||
|
} |
||||
|
} |
||||
|
}; |
@ -0,0 +1,57 @@ |
|||||
|
"use strict"; |
||||
|
var Element = require('./Element.js'); |
||||
|
var defineElement = require('./defineElement.js'); |
||||
|
var utils = require('./utils.js'); |
||||
|
var CSSStyleDeclaration = require('./CSSStyleDeclaration.js'); |
||||
|
|
||||
|
var svgElements = exports.elements = {}; |
||||
|
var svgNameToImpl = Object.create(null); |
||||
|
|
||||
|
exports.createElement = function(doc, localName, prefix) { |
||||
|
var impl = svgNameToImpl[localName] || SVGElement; |
||||
|
return new impl(doc, localName, prefix); |
||||
|
}; |
||||
|
|
||||
|
function define(spec) { |
||||
|
return defineElement(spec, SVGElement, svgElements, svgNameToImpl); |
||||
|
} |
||||
|
|
||||
|
var SVGElement = define({ |
||||
|
superclass: Element, |
||||
|
ctor: function SVGElement(doc, localName, prefix) { |
||||
|
Element.call(this, doc, localName, utils.NAMESPACE.SVG, prefix); |
||||
|
}, |
||||
|
props: { |
||||
|
style: { get: function() { |
||||
|
if (!this._style) |
||||
|
this._style = new CSSStyleDeclaration(this); |
||||
|
return this._style; |
||||
|
}} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
define({ |
||||
|
ctor: function SVGSVGElement(doc, localName, prefix) { |
||||
|
SVGElement.call(this, doc, localName, prefix); |
||||
|
}, |
||||
|
tag: 'svg', |
||||
|
props: { |
||||
|
createSVGRect: { value: function () { |
||||
|
return exports.createElement(this.ownerDocument, 'rect', null); |
||||
|
} } |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
define({ |
||||
|
tags: [ |
||||
|
'a', 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', 'animateMotion', 'animateTransform', |
||||
|
'circle', 'clipPath', 'color-profile', 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', |
||||
|
'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', |
||||
|
'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', |
||||
|
'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence', 'filter', |
||||
|
'font', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignObject', 'g', |
||||
|
'glyph', 'glyphRef', 'hkern', 'image', 'line', 'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', |
||||
|
'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'script', 'set', 'stop', 'style', |
||||
|
'switch', 'symbol', 'text', 'textPath', 'title', 'tref', 'tspan', 'use', 'view', 'vkern' |
||||
|
] |
||||
|
}); |
@ -0,0 +1,85 @@ |
|||||
|
"use strict"; |
||||
|
var DOMException = require('./DOMException.js'); |
||||
|
var ERR = DOMException; |
||||
|
var isApiWritable = require("./config.js").isApiWritable; |
||||
|
|
||||
|
exports.NAMESPACE = { |
||||
|
HTML: 'http://www.w3.org/1999/xhtml', |
||||
|
XML: 'http://www.w3.org/XML/1998/namespace', |
||||
|
XMLNS: 'http://www.w3.org/2000/xmlns/', |
||||
|
MATHML: 'http://www.w3.org/1998/Math/MathML', |
||||
|
SVG: 'http://www.w3.org/2000/svg', |
||||
|
XLINK: 'http://www.w3.org/1999/xlink' |
||||
|
}; |
||||
|
|
||||
|
//
|
||||
|
// Shortcut functions for throwing errors of various types.
|
||||
|
//
|
||||
|
exports.IndexSizeError = function() { throw new DOMException(ERR.INDEX_SIZE_ERR); }; |
||||
|
exports.HierarchyRequestError = function() { throw new DOMException(ERR.HIERARCHY_REQUEST_ERR); }; |
||||
|
exports.WrongDocumentError = function() { throw new DOMException(ERR.WRONG_DOCUMENT_ERR); }; |
||||
|
exports.InvalidCharacterError = function() { throw new DOMException(ERR.INVALID_CHARACTER_ERR); }; |
||||
|
exports.NoModificationAllowedError = function() { throw new DOMException(ERR.NO_MODIFICATION_ALLOWED_ERR); }; |
||||
|
exports.NotFoundError = function() { throw new DOMException(ERR.NOT_FOUND_ERR); }; |
||||
|
exports.NotSupportedError = function() { throw new DOMException(ERR.NOT_SUPPORTED_ERR); }; |
||||
|
exports.InvalidStateError = function() { throw new DOMException(ERR.INVALID_STATE_ERR); }; |
||||
|
exports.SyntaxError = function() { throw new DOMException(ERR.SYNTAX_ERR); }; |
||||
|
exports.InvalidModificationError = function() { throw new DOMException(ERR.INVALID_MODIFICATION_ERR); }; |
||||
|
exports.NamespaceError = function() { throw new DOMException(ERR.NAMESPACE_ERR); }; |
||||
|
exports.InvalidAccessError = function() { throw new DOMException(ERR.INVALID_ACCESS_ERR); }; |
||||
|
exports.TypeMismatchError = function() { throw new DOMException(ERR.TYPE_MISMATCH_ERR); }; |
||||
|
exports.SecurityError = function() { throw new DOMException(ERR.SECURITY_ERR); }; |
||||
|
exports.NetworkError = function() { throw new DOMException(ERR.NETWORK_ERR); }; |
||||
|
exports.AbortError = function() { throw new DOMException(ERR.ABORT_ERR); }; |
||||
|
exports.UrlMismatchError = function() { throw new DOMException(ERR.URL_MISMATCH_ERR); }; |
||||
|
exports.QuotaExceededError = function() { throw new DOMException(ERR.QUOTA_EXCEEDED_ERR); }; |
||||
|
exports.TimeoutError = function() { throw new DOMException(ERR.TIMEOUT_ERR); }; |
||||
|
exports.InvalidNodeTypeError = function() { throw new DOMException(ERR.INVALID_NODE_TYPE_ERR); }; |
||||
|
exports.DataCloneError = function() { throw new DOMException(ERR.DATA_CLONE_ERR); }; |
||||
|
|
||||
|
exports.nyi = function() { |
||||
|
throw new Error("NotYetImplemented"); |
||||
|
}; |
||||
|
|
||||
|
exports.shouldOverride = function() { |
||||
|
throw new Error("Abstract function; should be overriding in subclass."); |
||||
|
}; |
||||
|
|
||||
|
exports.assert = function(expr, msg) { |
||||
|
if (!expr) { |
||||
|
throw new Error("Assertion failed: " + (msg || "") + "\n" + new Error().stack); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
exports.expose = function(src, c) { |
||||
|
for (var n in src) { |
||||
|
Object.defineProperty(c.prototype, n, { value: src[n], writable: isApiWritable }); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
exports.merge = function(a, b) { |
||||
|
for (var n in b) { |
||||
|
a[n] = b[n]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Compare two nodes based on their document order. This function is intended
|
||||
|
// to be passed to sort(). Assumes that the array being sorted does not
|
||||
|
// contain duplicates. And that all nodes are connected and comparable.
|
||||
|
// Clever code by ppk via jeresig.
|
||||
|
exports.documentOrder = function(n,m) { |
||||
|
/* jshint bitwise: false */ |
||||
|
return 3 - (n.compareDocumentPosition(m) & 6); |
||||
|
}; |
||||
|
|
||||
|
exports.toASCIILowerCase = function(s) { |
||||
|
return s.replace(/[A-Z]+/g, function(c) { |
||||
|
return c.toLowerCase(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
exports.toASCIIUpperCase = function(s) { |
||||
|
return s.replace(/[a-z]+/g, function(c) { |
||||
|
return c.toUpperCase(); |
||||
|
}); |
||||
|
}; |
@ -0,0 +1,91 @@ |
|||||
|
"use strict"; |
||||
|
// This grammar is from the XML and XML Namespace specs. It specifies whether
|
||||
|
// a string (such as an element or attribute name) is a valid Name or QName.
|
||||
|
//
|
||||
|
// Name ::= NameStartChar (NameChar)*
|
||||
|
// NameStartChar ::= ":" | [A-Z] | "_" | [a-z] |
|
||||
|
// [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] |
|
||||
|
// [#x370-#x37D] | [#x37F-#x1FFF] |
|
||||
|
// [#x200C-#x200D] | [#x2070-#x218F] |
|
||||
|
// [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
|
||||
|
// [#xF900-#xFDCF] | [#xFDF0-#xFFFD] |
|
||||
|
// [#x10000-#xEFFFF]
|
||||
|
//
|
||||
|
// NameChar ::= NameStartChar | "-" | "." | [0-9] |
|
||||
|
// #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
|
||||
|
//
|
||||
|
// QName ::= PrefixedName| UnprefixedName
|
||||
|
// PrefixedName ::= Prefix ':' LocalPart
|
||||
|
// UnprefixedName ::= LocalPart
|
||||
|
// Prefix ::= NCName
|
||||
|
// LocalPart ::= NCName
|
||||
|
// NCName ::= Name - (Char* ':' Char*)
|
||||
|
// # An XML Name, minus the ":"
|
||||
|
//
|
||||
|
|
||||
|
exports.isValidName = isValidName; |
||||
|
exports.isValidQName = isValidQName; |
||||
|
|
||||
|
// Most names will be ASCII only. Try matching against simple regexps first
|
||||
|
var simplename = /^[_:A-Za-z][-.:\w]+$/; |
||||
|
var simpleqname = /^([_A-Za-z][-.\w]+|[_A-Za-z][-.\w]+:[_A-Za-z][-.\w]+)$/; |
||||
|
|
||||
|
// If the regular expressions above fail, try more complex ones that work
|
||||
|
// for any identifiers using codepoints from the Unicode BMP
|
||||
|
var ncnamestartchars = "_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02ff\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD"; |
||||
|
var ncnamechars = "-._A-Za-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02ff\u0300-\u037D\u037F-\u1FFF\u200C\u200D\u203f\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD"; |
||||
|
|
||||
|
var ncname = "[" + ncnamestartchars + "][" + ncnamechars + "]*"; |
||||
|
var namestartchars = ncnamestartchars + ":"; |
||||
|
var namechars = ncnamechars + ":"; |
||||
|
var name = new RegExp("^[" + namestartchars + "]" + "[" + namechars + "]*$"); |
||||
|
var qname = new RegExp("^(" + ncname + "|" + ncname + ":" + ncname + ")$"); |
||||
|
|
||||
|
// XML says that these characters are also legal:
|
||||
|
// [#x10000-#xEFFFF]. So if the patterns above fail, and the
|
||||
|
// target string includes surrogates, then try the following
|
||||
|
// patterns that allow surrogates and then run an extra validation
|
||||
|
// step to make sure that the surrogates are in valid pairs and in
|
||||
|
// the right range. Note that since the characters \uf0000 to \u1f0000
|
||||
|
// are not allowed, it means that the high surrogate can only go up to
|
||||
|
// \uDB7f instead of \uDBFF.
|
||||
|
var hassurrogates = /[\uD800-\uDB7F\uDC00-\uDFFF]/; |
||||
|
var surrogatechars = /[\uD800-\uDB7F\uDC00-\uDFFF]/g; |
||||
|
var surrogatepairs = /[\uD800-\uDB7F][\uDC00-\uDFFF]/g; |
||||
|
|
||||
|
// Modify the variables above to allow surrogates
|
||||
|
ncnamestartchars += "\uD800-\uDB7F\uDC00-\uDFFF"; |
||||
|
ncnamechars += "\uD800-\uDB7F\uDC00-\uDFFF"; |
||||
|
ncname = "[" + ncnamestartchars + "][" + ncnamechars + "]*"; |
||||
|
namestartchars = ncnamestartchars + ":"; |
||||
|
namechars = ncnamechars + ":"; |
||||
|
|
||||
|
// Build another set of regexps that include surrogates
|
||||
|
var surrogatename = new RegExp("^[" + namestartchars + "]" + "[" + namechars + "]*$"); |
||||
|
var surrogateqname = new RegExp("^(" + ncname + "|" + ncname + ":" + ncname + ")$"); |
||||
|
|
||||
|
function isValidName(s) { |
||||
|
if (simplename.test(s)) return true; // Plain ASCII
|
||||
|
if (name.test(s)) return true; // Unicode BMP
|
||||
|
|
||||
|
// Maybe the tests above failed because s includes surrogate pairs
|
||||
|
// Most likely, though, they failed for some more basic syntax problem
|
||||
|
if (!hassurrogates.test(s)) return false; |
||||
|
|
||||
|
// Is the string a valid name if we allow surrogates?
|
||||
|
if (!surrogatename.test(s)) return false; |
||||
|
|
||||
|
// Finally, are the surrogates all correctly paired up?
|
||||
|
var chars = s.match(surrogatechars), pairs = s.match(surrogatepairs); |
||||
|
return pairs !== null && 2*pairs.length === chars.length; |
||||
|
} |
||||
|
|
||||
|
function isValidQName(s) { |
||||
|
if (simpleqname.test(s)) return true; // Plain ASCII
|
||||
|
if (qname.test(s)) return true; // Unicode BMP
|
||||
|
|
||||
|
if (!hassurrogates.test(s)) return false; |
||||
|
if (!surrogateqname.test(s)) return false; |
||||
|
var chars = s.match(surrogatechars), pairs = s.match(surrogatepairs); |
||||
|
return pairs !== null && 2*pairs.length === chars.length; |
||||
|
} |
1535
govue/axios.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,28 @@ |
|||||
|
window = domino.createWindow("", "http://127.0.0.1/"); |
||||
|
document = window.document; |
||||
|
location = window.location; |
||||
|
XMLHttpRequest = function () { |
||||
|
console.log("XMLHttpRequest"); |
||||
|
this.open = function (m, url) { |
||||
|
console.log("XMLHttpRequest.open"); |
||||
|
this.m = m; |
||||
|
this.url = url; |
||||
|
this.onreadystatechange("", "", 0, 0) |
||||
|
}; |
||||
|
this.send = function (data) { |
||||
|
this.onreadystatechange("", "", 1, 0); |
||||
|
this.onreadystatechange("", "", 2, 0); |
||||
|
this.onreadystatechange("", "", 3, 0); |
||||
|
var result = GO_AJAX(m, url, data); |
||||
|
result = JSON.parse(result); |
||||
|
this.onreadystatechange(result["data"], result["data"], 4, parseInt(result["code"])) |
||||
|
}; |
||||
|
this.onreadystatechange = function (responseText, response, readyState, status) { |
||||
|
|
||||
|
}; |
||||
|
return this |
||||
|
}; |
||||
|
|
||||
|
navigator = { |
||||
|
"userAgent": "" |
||||
|
}; |
1183
govue/promise.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue