UNPKG

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