PageSourceSearch

https://stecter.ru/bitrix/templates/dresscodeV2/js/componentLoader.js

js stecter.ru collected 2026-10-08 21:17:35 UTC 3,898 bytes, 167 lines download raw bytes

1class DwComponentLoader {
2	#spinner;
3	#spinnerActiveClass;
4
5	constructor(parameters) {
6		if (!parameters) {
7			throw new Error("Cannot create loader without parameters");
8		}
9
10		if (!parameters.spinnerSelector) {
11			throw new Error("Cannot create loader without spinnerSelector in parameters");
12		}
13
14		if (!parameters.spinnerActiveClass) {
15			throw new Error("Cannot create loader without spinnerActiveClass in parameters");
16		}
17
18		this.#spinner = document.querySelector(parameters.spinnerSelector);
19
20		if (!this.#spinner) {
21			throw new Error(`Cannot find spinner element: ${parameters.spinnerSelector}`);
22		}
23
24		this.#spinnerActiveClass = parameters.spinnerActiveClass;
25	}
26
27	async load(request) {
28		if (!(request instanceof Request)) {
29			throw new Error("Cannot load component without Request object");
30		}
31
32		this.#showSpinner();
33
34		try {
35			const response = await this.#sendRequest(request);
36			const data = await this.#parseResponse(response);
37			await this.#loadAssets(data);
38			this.#insert(data);
39		} finally {
40			this.#hideSpinner();
41		}
42	}
43
44	async #sendRequest(request) {
45		const response = await fetch(request);
46		if (!response.ok) {
47			throw {
48				message: `Cannot fetch component: HTTP ${response.status}`,
49				reasons: [`HTTP error: ${response.status}`],
50				status: response.status
51			};
52		}
53
54		return response;
55	}
56
57	async #parseResponse(response) {
58		const result = await response.json();
59		if (result.errors && result.errors.length > 0) {
60			throw {
61				message: "Cannot load component: API returned errors",
62				reasons: result.errors,
63				response: result
64			};
65		}
66
67		if (result.status !== "success" || !result.data) {
68			throw {
69				message: "Cannot parse response: invalid format",
70				reasons: ["Invalid response format"],
71				response: result
72			};
73		}
74
75		if (!result.data.html) {
76			throw {
77				message: "Cannot render component: no HTML content",
78				reasons: ["Missing HTML content in response"],
79				response: result
80			};
81		}
82
83		return result.data;
84	}
85
86	async #loadAssets(data) {
87		if (!data.assets) {
88			return;
89		}
90
91		const jsPaths = Array.isArray(data?.assets?.js) ? data.assets.js : [];
92		const cssPaths = Array.isArray(data?.assets?.css) ? data.assets.css : [];
93
94		await Promise.all([this.#loadScripts(...jsPaths), this.#loadStyles(...cssPaths)]);
95	}
96
97	async #loadScripts(...paths) {
98		const promises = paths.map((path) => this.#loadScript(path));
99		return Promise.all(promises);
100	}
101
102	async #loadStyles(...paths) {
103		const promises = paths.map((path) => this.#loadStyle(path));
104		return Promise.all(promises);
105	}
106
107	#loadScript(path) {
108		if (this.#hasScript(path)) {
109			return Promise.resolve();
110		}
111
112		return new Promise((resolve, reject) => {
113			const script = document.createElement("script");
114			script.src = path;
115			script.onload = resolve;
116			script.onerror = () =>
117				reject({
118					message: `Cannot load script: ${path}`,
119					reasons: [`Failed to load script: ${path}`]
120				});
121
122			document.head.appendChild(script);
123		});
124	}
125
126	#loadStyle(path) {
127		if (this.#hasStyle(path)) {
128			return Promise.resolve();
129		}
130
131		return new Promise((resolve, reject) => {
132			const link = document.createElement("link");
133			link.rel = "stylesheet";
134			link.href = path;
135			link.onload = resolve;
136			link.onerror = () =>
137				reject({
138					message: `Cannot load stylesheet: ${path}`,
139					reasons: [`Failed to load stylesheet: ${path}`]
140				});
141
142			document.head.appendChild(link);
143		});
144	}
145
146	#hasScript(path) {
147		return document.querySelector(`script[src="${path}"]`) !== null;
148	}
149
150	#hasStyle(path) {
151		return document.querySelector(`link[href="${path}"]`) !== null;
152	}
153
154	#insert(data) {
155		const fragment = document.createRange().createContextualFragment(data.html);
156
157		document.body.appendChild(fragment);
158	}
159
160	#showSpinner() {
161		this.#spinner.classList.add(this.#spinnerActiveClass);
162	}
163
164	#hideSpinner() {
165		this.#spinner.classList.remove(this.#spinnerActiveClass);
166	}
167}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.