1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | var voidElements = {
|
14 | AREA:1, BASE:1, BR:1, COL:1, EMBED:1, HR:1, IMG:1, INPUT:1,
|
15 | KEYGEN:1, LINK:1, MENUITEM:1, META:1, PARAM:1, SOURCE:1, TRACK:1, WBR:1
|
16 | }
|
17 | , hasOwn = Object.prototype.hasOwnProperty
|
18 | , selectorCache = {}
|
19 | , selectorRe = /([.#:[])([-\w]+)(?:([~^$*|]?)=((["'\/])(?:\\?.)*?\4|[-\w]+)])?]?/g
|
20 | , lastSelectorRe = /(\s*[>+]?\s*)((["'\/])(?:\\?.)*?\2|[^\s+>])+$/
|
21 | , pseudoClasses = {
|
22 | "empty": "!_.hasChildNodes()",
|
23 | "first-child": "_.parentNode&&_.parentNode.firstChild==_",
|
24 | "last-child" : "_.parentNode&&_.parentNode.lastChild==_",
|
25 | "link": "_.nodeName=='A'&&_.getAttribute('href')"
|
26 | }
|
27 |
|
28 |
|
29 | function extend(obj, _super, extras) {
|
30 | obj.prototype = Object.create(_super.prototype)
|
31 | for (var key in extras) {
|
32 | obj.prototype[key] = extras[key]
|
33 | }
|
34 | obj.prototype.constructor = obj
|
35 | }
|
36 |
|
37 | function StyleMap(style) {
|
38 | var styleMap = this
|
39 | if (style) style.split(/\s*;\s*/g).map(function(val) {
|
40 | val = val.split(/\s*:\s*/)
|
41 | if(val[1]) styleMap[val[0]] = val[1]
|
42 | })
|
43 | }
|
44 |
|
45 | StyleMap.prototype.valueOf = function() {
|
46 | var styleMap = this
|
47 | return Object.keys(styleMap).map(function(key) {
|
48 | return key + ": " + styleMap[key]
|
49 | }).join("; ")
|
50 | }
|
51 |
|
52 | function Node(){}
|
53 |
|
54 | function getSibling(node, step) {
|
55 | var silbings = node.parentNode && node.parentNode.childNodes
|
56 | , index = silbings && silbings.indexOf(node)
|
57 |
|
58 | return silbings && index > -1 && silbings[ index + step ] || null
|
59 | }
|
60 |
|
61 | Node.prototype = {
|
62 | nodeName: null,
|
63 | parentNode: null,
|
64 | ownerDocument: null,
|
65 | childNodes: null,
|
66 | get nodeValue() {
|
67 | return this.nodeType === 3 || this.nodeType === 8 ? this.data : null
|
68 | },
|
69 | set nodeValue(text) {
|
70 | return this.nodeType === 3 || this.nodeType === 8 ? (this.data = text) : null
|
71 | },
|
72 | get textContent() {
|
73 | return this.hasChildNodes() ? this.childNodes.map(function(child) {
|
74 | return child[ child.nodeType == 3 ? "data" : "textContent" ]
|
75 | }).join("") : this.nodeType === 3 ? this.data : ""
|
76 | },
|
77 | set textContent(text) {
|
78 | if (this.nodeType === 3) return (this.data = text)
|
79 | for (var node = this; node.firstChild;) node.removeChild(node.firstChild)
|
80 | node.appendChild(node.ownerDocument.createTextNode(text))
|
81 | },
|
82 | get firstChild() {
|
83 | return this.childNodes && this.childNodes[0] || null
|
84 | },
|
85 | get lastChild() {
|
86 | return this.childNodes && this.childNodes[ this.childNodes.length - 1 ] || null
|
87 | },
|
88 | get previousSibling() {
|
89 | return getSibling(this, -1)
|
90 | },
|
91 | get nextSibling() {
|
92 | return getSibling(this, 1)
|
93 | },
|
94 | get innerHTML() {
|
95 | return Node.prototype.toString.call(this)
|
96 | },
|
97 | get outerHTML() {
|
98 | return this.toString()
|
99 | },
|
100 | get htmlFor() {
|
101 | return this["for"]
|
102 | },
|
103 | set htmlFor(value) {
|
104 | this["for"] = value
|
105 | },
|
106 | get className() {
|
107 | return this["class"] || ""
|
108 | },
|
109 | set className(value) {
|
110 | this["class"] = value
|
111 | },
|
112 | get style() {
|
113 | return this.styleMap || (this.styleMap = new StyleMap())
|
114 | },
|
115 | set style(value) {
|
116 | this.styleMap = new StyleMap(value)
|
117 | },
|
118 | hasChildNodes: function() {
|
119 | return this.childNodes && this.childNodes.length > 0
|
120 | },
|
121 | appendChild: function(el) {
|
122 | return this.insertBefore(el)
|
123 | },
|
124 | insertBefore: function(el, ref) {
|
125 | var node = this
|
126 | , childs = node.childNodes
|
127 |
|
128 | if (el.nodeType == 11) {
|
129 | while (el.firstChild) node.insertBefore(el.firstChild, ref)
|
130 | } else {
|
131 | if (el.parentNode) el.parentNode.removeChild(el)
|
132 | el.parentNode = node
|
133 |
|
134 |
|
135 | childs.splice(ref ? childs.indexOf(ref) : childs.length, 0, el)
|
136 | }
|
137 | return el
|
138 | },
|
139 | removeChild: function(el) {
|
140 | var node = this
|
141 | , index = node.childNodes.indexOf(el)
|
142 | if (index == -1) throw new Error("NOT_FOUND_ERR")
|
143 |
|
144 | node.childNodes.splice(index, 1)
|
145 | el.parentNode = null
|
146 | return el
|
147 | },
|
148 | replaceChild: function(el, ref) {
|
149 | this.insertBefore(el, ref)
|
150 | return this.removeChild(ref)
|
151 | },
|
152 | cloneNode: function(deep) {
|
153 | var key
|
154 | , node = this
|
155 | , clone = new node.constructor(node.tagName || node.data)
|
156 | clone.ownerDocument = node.ownerDocument
|
157 |
|
158 | if (node.hasAttribute) {
|
159 | for (key in node) if (node.hasAttribute(key)) clone[key] = node[key].valueOf()
|
160 | }
|
161 |
|
162 | if (deep && node.hasChildNodes()) {
|
163 | node.childNodes.forEach(function(child) {
|
164 | clone.appendChild(child.cloneNode(deep))
|
165 | })
|
166 | }
|
167 | return clone
|
168 | },
|
169 | toString: function() {
|
170 | return this.hasChildNodes() ? this.childNodes.reduce(function(memo, node) {
|
171 | return memo + node
|
172 | }, "") : ""
|
173 | }
|
174 | }
|
175 |
|
176 |
|
177 | function DocumentFragment() {
|
178 | this.childNodes = []
|
179 | }
|
180 |
|
181 | extend(DocumentFragment, Node, {
|
182 | nodeType: 11,
|
183 | nodeName: "#document-fragment"
|
184 | })
|
185 |
|
186 | function Attribute(node, name) {
|
187 | this.name = name.toLowerCase()
|
188 |
|
189 | Object.defineProperty(this, "value", {
|
190 | get: function() {return node.getAttribute(name)},
|
191 | set: function(val) {node.setAttribute(name, val)}
|
192 | })
|
193 | }
|
194 | Attribute.prototype.toString = function() {
|
195 | if (!this.value) return this.name
|
196 |
|
197 | return this.name + '="' + this.value.replace(/&/g, "&").replace(/"/g, """) + '"'
|
198 | }
|
199 |
|
200 | function findEl(node, sel, first) {
|
201 | var el
|
202 | , i = 0
|
203 | , out = []
|
204 | , els = node.getElementsByTagName("*")
|
205 | , fn = selectorFn(sel.split(/\s*,\s*/).map(function(sel) {
|
206 | return "_.matches('" + sel + "')"
|
207 | }).join("||"))
|
208 |
|
209 | for (; (el = els[i++]); ) if (fn(el)) {
|
210 | if (first) return el
|
211 | out.push(el)
|
212 | }
|
213 | return first ? null : out
|
214 | }
|
215 |
|
216 | function escapeAttributeName(name) {
|
217 | name = name.toLowerCase()
|
218 | if (name === "constructor" || name === "attributes") return name.toUpperCase()
|
219 | return name
|
220 | }
|
221 |
|
222 | function selectorFnStr(sel) {
|
223 | var rules = ["_"]
|
224 | , tag = sel.replace(selectorRe, function(_, op, key, fn, val, quotation, len) {
|
225 | if (quotation) val = val.slice(1, -1)
|
226 | if (val) {
|
227 | len = val.length
|
228 | val = val.replace(/'/g, "\\'")
|
229 | }
|
230 | rules.push(
|
231 | op == "." ? "(' '+_.className+' ').indexOf(' " + key + " ')>-1" :
|
232 | op == "#" ? "_.id=='" + key + "'" :
|
233 | op == ":" && pseudoClasses[key] ||
|
234 | "(a=_.getAttribute('" + key + "'))" + (!fn && val ? "=='" + val + "'" : "")
|
235 | )
|
236 | if (fn) {
|
237 | rules.push(
|
238 | fn == "^" ? "a.slice(0," + len + ")=='" + val + "'" :
|
239 | fn == "|" ? "a.split('-')[0]=='" + val + "'" :
|
240 | fn == "$" ? "a.slice(-" + len + ")=='" + val + "'" :
|
241 | fn == "~" ? "(' '+a+' ').indexOf(' " + val + " ')>-1" :
|
242 | "a.indexOf('" + val + "')>-1"
|
243 | )
|
244 | }
|
245 | return ""
|
246 | })
|
247 |
|
248 | if (tag && tag != "*") rules.unshift("_.nodeName=='" + tag.toUpperCase() + "'")
|
249 | return rules.join("&&")
|
250 | }
|
251 |
|
252 | function selectorFn(str) {
|
253 |
|
254 | return selectorCache[str] ||
|
255 | (selectorCache[str] = Function("_,a", "return " + str))
|
256 | }
|
257 |
|
258 | function HTMLElement(tag) {
|
259 | var element = this
|
260 | element.nodeName = element.tagName = tag.toUpperCase()
|
261 | element.localName = tag.toLowerCase()
|
262 | element.childNodes = []
|
263 | }
|
264 |
|
265 | extend(HTMLElement, Node, {
|
266 | matches: function(sel) {
|
267 | var relation, from
|
268 | , parentSel = sel.replace(lastSelectorRe, function(_, _rel, a, b, start) {
|
269 | from = start + _rel.length
|
270 | relation = _rel.trim()
|
271 | return ""
|
272 | })
|
273 | , next = relation == "+" ? this.previousSibling : this.parentNode
|
274 | , fn = selectorFn(selectorFnStr(sel.slice(from)))
|
275 |
|
276 | if (!fn(this)) return false
|
277 |
|
278 | if (parentSel) {
|
279 | if (!relation) return !!(next && next.closest && next.closest(parentSel))
|
280 | return next && next.matches && next.matches(parentSel) || false
|
281 | }
|
282 | return true
|
283 | },
|
284 | closest: function(sel) {
|
285 | for (var el = this; el; el = el.parentNode) if (el.matches && el.matches(sel)) return el
|
286 | return null
|
287 | },
|
288 | namespaceURI: "http://www.w3.org/1999/xhtml",
|
289 | nodeType: 1,
|
290 | localName: null,
|
291 | tagName: null,
|
292 | styleMap: null,
|
293 | hasAttribute: function(name) {
|
294 | name = escapeAttributeName(name)
|
295 | return name == "style" && !!this.style.valueOf() || hasOwn.call(this, name)
|
296 | },
|
297 | getAttribute: function(name) {
|
298 | name = escapeAttributeName(name)
|
299 | return this.hasAttribute(name) ? "" + this[name] : null
|
300 | },
|
301 | setAttribute: function(name, value) {
|
302 | this[escapeAttributeName(name)] = "" + value
|
303 | },
|
304 | removeAttribute: function(name) {
|
305 | name = escapeAttributeName(name)
|
306 | this[name] = ""
|
307 | delete this[name]
|
308 | },
|
309 | getElementById: function(id) {
|
310 | if (this.id == id) return this
|
311 | for (var el, found, i = 0; !found && (el = this.childNodes[i++]);) {
|
312 | if (el.nodeType == 1) found = el.getElementById(id)
|
313 | }
|
314 | return found || null
|
315 | },
|
316 | getElementsByTagName: function(tag) {
|
317 | var el, els = [], next = this.firstChild
|
318 | tag = tag === "*" ? 1 : tag.toUpperCase()
|
319 | for (var i = 0, key = tag === 1 ? "nodeType" : "nodeName"; (el = next); ) {
|
320 | if (el[key] === tag) els[i++] = el
|
321 | next = el.firstChild || el.nextSibling
|
322 | while (!next && ((el = el.parentNode) !== this)) next = el.nextSibling
|
323 | }
|
324 | return els
|
325 | },
|
326 | querySelector: function(sel) {
|
327 | return findEl(this, sel, 1)
|
328 | },
|
329 | querySelectorAll: function(sel) {
|
330 | return findEl(this, sel)
|
331 | },
|
332 | toString: function() {
|
333 | var attrs = this.attributes.join(" ")
|
334 | return "<" + this.localName + (attrs ? " " + attrs : "") + ">" +
|
335 | (voidElements[this.tagName] ? "" : this.innerHTML + "</" + this.localName + ">")
|
336 | }
|
337 | })
|
338 |
|
339 | Object.defineProperty(HTMLElement.prototype, "attributes", {
|
340 | get: function() {
|
341 | var key
|
342 | , attrs = []
|
343 | , element = this
|
344 | for (key in element) if (key === escapeAttributeName(key) && element.hasAttribute(key))
|
345 | attrs.push(new Attribute(element, escapeAttributeName(key)))
|
346 | return attrs
|
347 | }
|
348 | })
|
349 |
|
350 | function ElementNS(namespace, tag) {
|
351 | var element = this
|
352 | element.namespaceURI = namespace
|
353 | element.nodeName = element.tagName = element.localName = tag
|
354 | element.childNodes = []
|
355 | }
|
356 |
|
357 | ElementNS.prototype = HTMLElement.prototype
|
358 |
|
359 | function Text(data) {
|
360 | this.data = data
|
361 | }
|
362 |
|
363 | extend(Text, Node, {
|
364 | nodeType: 3,
|
365 | nodeName: "#text",
|
366 | toString: function() {
|
367 | return ("" + this.data).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
368 | }
|
369 | })
|
370 |
|
371 | function Comment(data) {
|
372 | this.data = data
|
373 | }
|
374 |
|
375 | extend(Comment, Node, {
|
376 | nodeType: 8,
|
377 | nodeName: "#comment",
|
378 | toString: function() {
|
379 | return "<!--" + this.data + "-->"
|
380 | }
|
381 | })
|
382 |
|
383 | function Document() {
|
384 | this.childNodes = []
|
385 | this.documentElement = this.createElement("html")
|
386 | this.appendChild(this.documentElement)
|
387 | this.body = this.createElement("body")
|
388 | this.documentElement.appendChild(this.body)
|
389 | }
|
390 |
|
391 | function own(Element) {
|
392 | return function($1, $2) {
|
393 | var node = new Element($1, $2)
|
394 | node.ownerDocument = this
|
395 | return node
|
396 | }
|
397 | }
|
398 |
|
399 | extend(Document, Node, {
|
400 | nodeType: 9,
|
401 | nodeName: "#document",
|
402 | createElement: own(HTMLElement),
|
403 | createElementNS: own(ElementNS),
|
404 | createTextNode: own(Text),
|
405 | createComment: own(Comment),
|
406 | createDocumentFragment: own(DocumentFragment),
|
407 | getElementById: HTMLElement.prototype.getElementById,
|
408 | getElementsByTagName: HTMLElement.prototype.getElementsByTagName,
|
409 | querySelector: HTMLElement.prototype.querySelector,
|
410 | querySelectorAll: HTMLElement.prototype.querySelectorAll
|
411 | })
|
412 |
|
413 | module.exports = {
|
414 | document: new Document(),
|
415 | Document: Document,
|
416 | HTMLElement: HTMLElement
|
417 | }
|
418 |
|