UNPKG

4.75 kBJavaScriptView Raw
1"use strict";
2
3const cheerio = require("cheerio");
4const DocumentFragment = require("./DocumentFragment");
5const Element = require("./Element");
6const getLocation = require("./getLocation");
7const {EventEmitter} = require("events");
8const {Event} = require("./Events");
9const {HTMLCollection} = require("./HTMLCollection");
10
11module.exports = Document;
12
13function Document(source, cookieJar) {
14 const location = source.location || getLocation(source.url);
15 const referrer = source.referrer || "";
16 const $ = cheerio.load(source.text, {decodeEntities: false});
17 const loaded = [];
18
19 const emitter = new EventEmitter();
20
21 let fullscreenElement = null;
22 let forms;
23
24 const document = {
25 _getElement: getElement,
26 _emitter: emitter,
27 addEventListener,
28 createDocumentFragment() {
29 return DocumentFragment(Element);
30 },
31 createElement,
32 createElementNS,
33 createTextNode,
34 dispatchEvent,
35 exitFullscreen,
36 getElementById,
37 getElementsByTagName(name) {
38 return this.documentElement.getElementsByTagName(name);
39 },
40 getElementsByClassName(classNames) {
41 return this.documentElement.getElementsByClassName(classNames);
42 },
43 getElementsByName,
44 importNode,
45 location,
46 removeEventListener,
47 referrer,
48 textContent: null,
49 $,
50 };
51
52 Object.defineProperty(document, "head", {
53 get: getHead
54 });
55
56 Object.defineProperty(document, "body", {
57 get: getBody
58 });
59
60 Object.defineProperty(document, "documentElement", {
61 get: () => getElement($("html"))
62 });
63
64 Object.defineProperty(document, "firstElementChild", {
65 get: () => getElement($("html"))
66 });
67
68 Object.defineProperty(document, "firstChild", {
69 get: () => getElement($("> :first-child"))
70 });
71
72 Object.defineProperty(document, "fullscreenElement", {
73 get: () => fullscreenElement
74 });
75
76 Object.defineProperty(document, "cookie", {
77 get() {
78 return cookieJar.getCookies({path: location.pathname, script: true, domain: location.hostname, secure: location.protocol === "https:"}).toValueString();
79 },
80 set: (value) => {
81 cookieJar.setCookie(value);
82 }
83 });
84
85 Object.defineProperty(document, "title", {
86 get: () => getElement($("head > title")).textContent,
87 set: (value) => {
88 getElement($("head > title")).textContent = value;
89 }
90 });
91
92 Object.defineProperty(document, "nodeType", {
93 get: () => 9
94 });
95
96 Object.defineProperty(document, "forms", {
97 get() {
98 if (!forms) forms = document.documentElement.getElementsByTagName("form");
99 return forms;
100 }
101 });
102
103 return document;
104
105 function exitFullscreen() {
106 const fullscreenchangeEvent = new Event("fullscreenchange");
107 fullscreenchangeEvent.target = fullscreenElement;
108
109 document.dispatchEvent(fullscreenchangeEvent);
110 }
111
112 function getHead() {
113 return getElement($("head"));
114 }
115
116 function getBody() {
117 return getElement($("body"));
118 }
119
120 function getElement($elm) {
121 if ($elm === $) return document;
122 if (!$elm.length) return;
123
124 let mockElement = loaded.find((mockedElm) => mockedElm.$elm[0] === $elm[0]);
125 if (mockElement) {
126 return mockElement;
127 }
128
129 mockElement = Element(document, $elm);
130
131 loaded.push(mockElement);
132 return mockElement;
133 }
134
135 function getElementById(id) {
136 const $idElm = $(`#${id}`).eq(0);
137
138 if ($idElm && $idElm.length) return getElement($idElm);
139 return null;
140 }
141
142 function getElementsByName(name) {
143 return new HTMLCollection(document.documentElement, `[name="${name}"],#${name}`, {attributes: true});
144 }
145
146 function createElement(elementTagName) {
147 const element = Element(document, document.$(`<${elementTagName}></${elementTagName}>`));
148 loaded.push(element);
149 return element;
150 }
151
152 function createElementNS(namespaceURI, elementTagName) {
153 return createElement(elementTagName);
154 }
155
156 function createTextNode(text) {
157 return {
158 textContent: text,
159 };
160 }
161
162 function addEventListener(...args) {
163 emitter.on(...args);
164 }
165
166 function removeEventListener(...args) {
167 emitter.removeListener(...args);
168 }
169
170 function dispatchEvent(event) {
171 if (event && event.type === "fullscreenchange") {
172 _handleFullscreenChange(event);
173 } else {
174 emitter.emit(event.type, event);
175 }
176 }
177
178 function _handleFullscreenChange(event) {
179 if (!event.target) return;
180
181 if (fullscreenElement === null) {
182 fullscreenElement = event.target;
183 } else if (fullscreenElement === event.target) {
184 fullscreenElement = null;
185 }
186
187 emitter.emit("fullscreenchange", event);
188 }
189
190 function importNode(element, deep) {
191 if (element instanceof DocumentFragment) {
192 return element._clone(deep);
193 }
194
195 return element.cloneNode(deep);
196 }
197}