@yander/translation-widget 1.1.4-yander.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3000 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ const styles = 'Base Styles :root{--jigts-base-color: white;--jigts-text-color: black;--jigts-bg-color: color-mix(in srgb, var(--jigts-base-color) 10%, white);--jigts-bg-hover: color-mix(in srgb, var(--jigts-text-color) 10%, white);--jigts-bg-active: color-mix(in srgb, var(--jigts-base-color) 30%, white)}*{box-sizing:border-box}.jigts-translation-widget{--jigts-base-color: var(--jigts-custom-base-color, white);--jigts-text-color: var(--jigts-custom-text-color, black);--jigts-bg-color: color-mix(in srgb, var(--jigts-base-color) 10%, white);--jigts-bg-hover: color-mix(in srgb, var(--jigts-text-color) 10%, white);--jigts-bg-active: color-mix(in srgb, var(--jigts-base-color) 30%, white);position:fixed;z-index:1000;color:var(--jigts-text-color)}.jigts-translation-widget.jigts-position-top-right{top:2rem;right:2rem}.jigts-translation-widget.jigts-position-top-left{top:2rem;left:2rem}.jigts-translation-widget.jigts-position-bottom-left{bottom:2rem;left:2rem}.jigts-translation-widget.jigts-position-bottom-right{bottom:2rem;right:2rem}.jigts-position-top-right .jigts-widget-dropdown,.jigts-position-top-left .jigts-widget-dropdown{top:calc(100% + .5rem)}.jigts-position-bottom-right .jigts-widget-dropdown,.jigts-position-bottom-left .jigts-widget-dropdown{bottom:calc(100% + .5rem)}.jigts-position-top-right .jigts-widget-dropdown,.jigts-position-bottom-right .jigts-widget-dropdown{right:0}.jigts-position-top-left .jigts-widget-dropdown,.jigts-position-bottom-left .jigts-widget-dropdown{left:0}.jigts-translation-widget{position:fixed}.jigts-translation-widget{max-width:fit-content}.jigts-trigger-content{display:flex;cursor:pointer;border-radius:6px;transition:all .2s ease}.jigts-trigger-icon{display:flex;align-items:center}.jigts-lang-code{font-weight:500;color:var(--jigts-text-color);font-size:14px}.jigts-lang-name{color:var(--jigts-text-color);font-size:14px;opacity:0;max-width:0;overflow:hidden;white-space:nowrap;transition:all .3s ease-in-out;display:inline-block}.jigts-lang-code{transition:all .3s ease-in-out}.jigts-widget-trigger:hover .jigts-lang-code{background:#e5e7eb;border-radius:50%;padding:.1rem .3rem;color:var(--jigts-text-color);font-weight:600;font-size:12px;transition:all .3s ease-in-out}.jigts-widget-trigger:hover .jigts-lang-name{opacity:1;margin-left:.5rem;max-width:150px}.jigts-widget-trigger{background:var(--jigts-bg-color);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid color-mix(in srgb,var(--jigts-base-color) 30%,white);border-radius:.75rem;padding:.3rem .6rem;cursor:pointer;display:flex;align-items:center;gap:.2rem;font-size:.9rem;font-weight:500;color:var(--jigts-text-color);box-shadow:0 10px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden;min-height:2.2rem;min-width:unset}.jigts-widget-trigger:hover{transform:scale(1.05) translateY(-2px);box-shadow:0 20px 40px -10px #00000026,0 10px 20px -5px #0000001a}.jigts-widget-trigger:active{transform:scale(.98)}.jigts-widget-trigger:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(var(--jigts-base-color),.1),transparent);transition:left .6s ease}.jigts-widget-trigger:hover:before{left:100%}.jigts-widget-dropdown{position:absolute;width:20rem;background:var(--jigts-bg-color);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid color-mix(in srgb,var(--jigts-base-color) 30%,white);border-radius:.75rem;box-shadow:0 25px 50px -12px #00000040;opacity:0;visibility:hidden;transform:scale(.95) translateY(10px);transition:all .3s cubic-bezier(.4,0,.2,1);max-height:28rem;overflow:hidden;display:none;flex-direction:column;z-index:1000}.jigts-widget-dropdown.jigts-open{opacity:1;visibility:visible;transform:scale(1) translateY(0);display:flex}.jigts-widget-dropdown.jigts-closing{opacity:0;transform:translateY(10px)}.jigts-dropdown-header{padding:1rem;border-bottom:1px solid color-mix(in srgb,var(--jigts-base-color) 20%,white);background:var(--jigts-bg-hover);border-radius:.75rem .75rem 0 0;animation:headerSlideDown .4s ease .1s both}.jigts-dropdown-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem}.jigts-title-left{display:flex;align-items:center;gap:.5rem}.jigts-languages-icon{width:1rem;height:1rem;color:var(--jigts-text-color)}.jigts-title-text{font-size:.875rem;font-weight:600;color:var(--jigts-text-color)}.jigts-language-count{background:var(--jigts-bg-hover);border:1px solid color-mix(in srgb,var(--jigts-base-color) 30%,white);color:var(--jigts-text-color);padding:.125rem .5rem;border-radius:.375rem;font-size:.75rem;font-weight:500}.jigts-search-container{position:relative}.jigts-search-input{width:100%;padding:.5rem .75rem .5rem 2.5rem;border:1px solid #d1d5db;border-radius:.5rem;outline:none;font-size:.875rem;color:var(--jigts-text-color);background:#fff;transition:all .2s ease}.jigts-search-icon{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;color:var(--jigts-text-color);opacity:.7}.jigts-clear-search{position:absolute;right:.75rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;color:var(--jigts-text-color);cursor:pointer;opacity:0;transition:all .2s ease}.jigts-clear-search.jigts-visible{opacity:.7}.jigts-clear-search:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.jigts-reset-option{padding:.75rem 1rem;border-bottom:1px solid color-mix(in srgb,var(--jigts-base-color) 20%,white);cursor:pointer;display:flex;align-items:center;gap:.75rem;transition:background-color .2s ease;animation:resetSlideIn .4s ease .15s both;background:var(--jigts-bg-color)}.jigts-reset-option:hover{background:var(--jigts-bg-hover)}.jigts-reset-icon{width:1rem;height:1rem;color:var(--jigts-text-color);transition:transform .3s ease}.jigts-reset-option:hover .jigts-reset-icon{transform:rotate(-180deg)}.jigts-reset-text{display:flex;flex-direction:column}.jigts-reset-title{font-weight:500;color:var(--jigts-text-color);font-size:.875rem}.jigts-reset-subtitle{font-size:.75rem;color:var(--jigts-text-color);opacity:.7}.jigts-language-list{flex:1;overflow-y:auto;padding:.5rem;position:relative;min-height:200px}.jigts-language-item{display:flex;align-items:center;justify-content:space-between;margin-left:.5rem;margin-right:.5rem;margin-bottom:.5rem;padding:.625rem .75rem;border-radius:.5rem;cursor:pointer;transition:all .2s ease;border:1px solid transparent;animation:languageSlideIn .4s ease both;background:var(--jigts-bg-color)}.jigts-language-item.jigts-focused{background:var(--jigts-bg-hover);border-color:color-mix(in srgb,var(--jigts-base-color) 40%,white)}.jigts-language-item.jigts-selected{background:var(--jigts-bg-active)}.jigts-language-item:hover{background:var(--jigts-bg-hover)}.jigts-language-info{display:flex;flex-direction:column;align-items:flex-start;min-width:0;flex:1}.jigts-language-main{display:flex;align-items:center;gap:.5rem;width:100%}.jigts-language-name{font-weight:500;color:var(--jigts-text-color);font-size:.875rem;transition:color .2s ease;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jigts-language-item:hover .jigts-language-name{color:var(--jigts-text-color);opacity:.8}.jigts-language-code{background:var(--jigts-bg-hover);color:var(--jigts-text-color);padding:.125rem .375rem;border-radius:.25rem;font-size:.75rem;font-weight:600;flex-shrink:0}.jigts-language-details{display:flex;align-items:center;gap:.25rem;font-size:.75rem;color:var(--jigts-text-color);opacity:.7;width:100%;margin-top:.125rem}.jigts-language-native{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jigts-language-separator,.jigts-language-region{flex-shrink:0}.jigts-globe-icon{width:1rem;height:1rem;color:var(--jigts-text-color);transition:transform .5s ease}.jigts-widget-trigger:hover .jigts-globe-icon{transform:rotate(360deg)}.jigts-check-icon{width:1rem;height:1rem;color:var(--jigts-text-color);opacity:0;transform:scale(0);transition:all .3s cubic-bezier(.34,1.56,.64,1)}.jigts-language-item.jigts-selected .jigts-check-icon{opacity:1;transform:scale(1)}.jigts-loading-spinner{width:1rem;height:1rem;border:2px solid #e5e7eb;border-top:2px solid var(--jigts-base-color);border-radius:50%;animation:spin 1s linear infinite}.jigts-trigger-loading{display:none}.jigts-no-results{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:none;flex-direction:column;align-items:center;justify-content:center;color:var(--jigts-text-color);text-align:center;padding:24px;animation:fadeIn .4s ease}.jigts-no-results-icon{width:2rem;height:2rem;margin-bottom:.5rem;color:var(--jigts-text-color);opacity:.5}.jigts-no-results-title{font-size:.875rem;margin-bottom:.25rem}.jigts-no-results-subtitle{font-size:.75rem}.jigts-dropdown-footer{padding:.5rem 1rem;border-top:1px solid color-mix(in srgb,var(--jigts-base-color) 20%,white);background:var(--jigts-bg-hover);border-radius:0 0 .75rem .75rem;animation:footerSlideUp .4s ease .2s both}.jigts-footer-text{font-size:.75rem;color:var(--jigts-text-color);text-align:center;opacity:.7}.jigts-language-list::-webkit-scrollbar{width:6px}.jigts-language-list::-webkit-scrollbar-track{background:#f3f4f6;border-radius:3px}.jigts-language-list::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.jigts-language-list::-webkit-scrollbar-thumb:hover{background:#9ca3af}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes badgeSlideIn{0%{transform:scale(0) translate(10px);opacity:0}to{transform:scale(1) translate(0);opacity:1}}@keyframes headerSlideDown{0%{transform:translateY(-20px);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes resetSlideIn{0%{transform:translate(-20px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes languageSlideIn{0%{transform:translate(-20px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes footerSlideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.jigts-language-item:nth-child(1){animation-delay:.2s}.jigts-language-item:nth-child(2){animation-delay:.22s}.jigts-language-item:nth-child(3){animation-delay:.24s}.jigts-language-item:nth-child(4){animation-delay:.26s}.jigts-language-item:nth-child(5){animation-delay:.28s}.jigts-language-item:nth-child(6){animation-delay:.3s}.jigts-language-item:nth-child(7){animation-delay:.32s}.jigts-language-item:nth-child(8){animation-delay:.34s}.jigts-language-item:nth-child(9){animation-delay:.36s}.jigts-language-item:nth-child(10){animation-delay:.38s}@media(max-width:400px){.jigts-widget-dropdown{width:70vw;right:auto;left:50%;transform:translate(-59%) scale(.95) translateY(10px)}.jigts-widget-dropdown.jigts-open{transform:translate(-90%) scale(1) translateY(0)}}@media(prefers-contrast:high){.jigts-widget-trigger,.jigts-widget-dropdown{border:2px solid #000}}@media(prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}.jigts-translated-content{transition:all .3s ease}.jigts-translated-content.jigts-has-html{line-height:1.5}.jigts-translated-content.jigts-font-adjusted{transition:font-size .3s ease}.jigts-content-transition{animation:jigtsContentFade .3s ease-in-out}@keyframes jigtsContentFade{0%{opacity:.7;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}';
5
+ const BATCH_SIZE = 90;
6
+ const CACHE_PREFIX = "jss-";
7
+ const MAX_REQUESTS_PER_SECOND = 45;
8
+ const DEFAULT_TRANSLATE_API_URL = "https://api.jigsawstack.com/v1/ai/translate";
9
+ const DEFAULT_TARGET_LANGUAGE_CODES = ["zh"];
10
+ const DEFAULT_SOURCE_LANGUAGE_CODE = "en";
11
+ const DEFAULT_CONFIG = {
12
+ pageLanguage: "en",
13
+ autoDetectLanguage: false,
14
+ adjustFontSize: false,
15
+ mockMode: false,
16
+ position: "top-right",
17
+ targetLanguages: DEFAULT_TARGET_LANGUAGE_CODES,
18
+ apiUrl: DEFAULT_TRANSLATE_API_URL,
19
+ requestHeaders: {},
20
+ includeApiKeyHeader: true
21
+ };
22
+ class RateLimiter {
23
+ constructor(maxRequests, intervalMs = 1e3) {
24
+ __publicField(this, "timestamps", []);
25
+ __publicField(this, "pending", Promise.resolve());
26
+ this.maxRequests = maxRequests;
27
+ this.intervalMs = intervalMs;
28
+ }
29
+ acquire() {
30
+ this.pending = this.pending.then(() => this._acquire());
31
+ return this.pending;
32
+ }
33
+ async _acquire() {
34
+ const now = Date.now();
35
+ this.timestamps = this.timestamps.filter((t) => now - t < this.intervalMs);
36
+ if (this.timestamps.length < this.maxRequests) {
37
+ this.timestamps.push(Date.now());
38
+ return;
39
+ }
40
+ const waitTime = this.intervalMs - (now - this.timestamps[0]);
41
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
42
+ this.timestamps = this.timestamps.filter((t) => Date.now() - t < this.intervalMs);
43
+ this.timestamps.push(Date.now());
44
+ }
45
+ }
46
+ const MOCK_LANGUAGE_LABELS = {
47
+ ar: "AR",
48
+ de: "DE",
49
+ en: "EN",
50
+ es: "ES",
51
+ fr: "FR",
52
+ hi: "HI",
53
+ it: "IT",
54
+ ja: "JA",
55
+ ko: "KO",
56
+ pt: "PT",
57
+ zh: "ZH"
58
+ };
59
+ const PSEUDO_TRANSLATION_MAP = {
60
+ a: "á",
61
+ e: "é",
62
+ i: "í",
63
+ o: "ó",
64
+ u: "ú",
65
+ A: "Á",
66
+ E: "É",
67
+ I: "Í",
68
+ O: "Ó",
69
+ U: "Ú"
70
+ };
71
+ function pseudoTranslateSegment(segment, targetLang) {
72
+ var _a, _b;
73
+ const trimmed = segment.trim();
74
+ if (!trimmed) return segment;
75
+ const prefix = `【${MOCK_LANGUAGE_LABELS[targetLang] || targetLang.toUpperCase()}】`;
76
+ const pseudoText = trimmed.replace(/[aeiouAEIOU]/g, (char) => PSEUDO_TRANSLATION_MAP[char] || char);
77
+ const leadingWhitespace = ((_a = segment.match(/^\s*/)) == null ? void 0 : _a[0]) || "";
78
+ const trailingWhitespace = ((_b = segment.match(/\s*$/)) == null ? void 0 : _b[0]) || "";
79
+ return `${leadingWhitespace}${prefix} ${pseudoText}${trailingWhitespace}`;
80
+ }
81
+ function mockTranslateText(text, targetLang) {
82
+ if (text.includes("<")) {
83
+ return text.split(/(<[^>]+>)/g).map((part) => part.startsWith("<") ? part : pseudoTranslateSegment(part, targetLang)).join("");
84
+ }
85
+ return pseudoTranslateSegment(text, targetLang);
86
+ }
87
+ class TranslationService {
88
+ constructor(publicKey, options = {}) {
89
+ __publicField(this, "publicKey");
90
+ __publicField(this, "apiUrl");
91
+ __publicField(this, "rateLimiter");
92
+ __publicField(this, "mockMode");
93
+ __publicField(this, "requestHeaders");
94
+ __publicField(this, "includeApiKeyHeader");
95
+ this.publicKey = publicKey;
96
+ this.rateLimiter = new RateLimiter(MAX_REQUESTS_PER_SECOND);
97
+ this.mockMode = options.mockMode ?? false;
98
+ this.apiUrl = options.apiUrl || DEFAULT_TRANSLATE_API_URL;
99
+ this.requestHeaders = options.requestHeaders ?? {};
100
+ this.includeApiKeyHeader = options.includeApiKeyHeader ?? true;
101
+ }
102
+ normalizeTranslations(result) {
103
+ var _a;
104
+ if (Array.isArray((_a = result.data) == null ? void 0 : _a.translations)) {
105
+ return result.data.translations.map((item) => (item == null ? void 0 : item.translatedText) || "");
106
+ }
107
+ if (Array.isArray(result.translated_text)) {
108
+ return result.translated_text;
109
+ }
110
+ if (typeof result.translated_text === "string") {
111
+ return [result.translated_text];
112
+ }
113
+ return [];
114
+ }
115
+ getRequestTargetLanguageCode(targetLang) {
116
+ if (targetLang === "zh") {
117
+ return "zh-CN";
118
+ }
119
+ return targetLang;
120
+ }
121
+ async translateBatchText(texts, targetLang, maxRetries = 2, retryDelay = 100) {
122
+ if (this.mockMode) {
123
+ await new Promise((resolve) => setTimeout(resolve, 120));
124
+ return texts.map((text) => mockTranslateText(text, targetLang));
125
+ }
126
+ await this.rateLimiter.acquire();
127
+ let attempt = 0;
128
+ while (attempt < maxRetries) {
129
+ try {
130
+ const headers = {
131
+ "Content-Type": "application/json",
132
+ ...this.requestHeaders
133
+ };
134
+ if (this.includeApiKeyHeader && this.publicKey) {
135
+ headers["x-api-key"] = this.publicKey;
136
+ }
137
+ const response = await fetch(this.apiUrl, {
138
+ method: "POST",
139
+ headers,
140
+ body: JSON.stringify({
141
+ texts,
142
+ targetLanguageCode: this.getRequestTargetLanguageCode(targetLang),
143
+ sourceLanguageCode: DEFAULT_SOURCE_LANGUAGE_CODE
144
+ })
145
+ });
146
+ if (!response.ok) {
147
+ const error = new Error(`Error translating text: ${response.statusText}`);
148
+ error.status = response.status;
149
+ error.response = response;
150
+ throw error;
151
+ }
152
+ const result = await response.json();
153
+ if (typeof result.code === "number" && result.code !== 1e3) {
154
+ const error = new Error(result.message || "Translation service returned an invalid code");
155
+ throw error;
156
+ }
157
+ const translations = this.normalizeTranslations(result);
158
+ if (translations.length === 0) {
159
+ const error = new Error("Translation service returned empty translations");
160
+ throw error;
161
+ }
162
+ return translations;
163
+ } catch (error) {
164
+ attempt++;
165
+ if (attempt >= maxRetries) {
166
+ console.error("Translation error after retries:", error);
167
+ return null;
168
+ }
169
+ await this.rateLimiter.acquire();
170
+ await new Promise((res) => setTimeout(res, retryDelay));
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ }
176
+ const languages = [
177
+ {
178
+ code: "af",
179
+ name: "Afrikaans",
180
+ native: "Afrikaans",
181
+ writing_system: "Latin"
182
+ },
183
+ {
184
+ code: "am",
185
+ name: "Amharic",
186
+ native: "አማርኛ",
187
+ writing_system: "Ethiopic"
188
+ },
189
+ {
190
+ code: "ar",
191
+ name: "Arabic",
192
+ native: "العربية",
193
+ rtl: 1,
194
+ writing_system: "Arabic"
195
+ },
196
+ {
197
+ code: "as",
198
+ name: "Assamese",
199
+ native: "অসমীয়া",
200
+ writing_system: "Bengali"
201
+ },
202
+ {
203
+ code: "az",
204
+ name: "Azerbaijani",
205
+ native: "Azərbaycanca / آذربايجان",
206
+ writing_system: "Latin"
207
+ },
208
+ {
209
+ code: "ba",
210
+ name: "Bashkir",
211
+ native: "Башҡорт",
212
+ writing_system: "Cyrillic"
213
+ },
214
+ {
215
+ code: "be",
216
+ name: "Belarusian",
217
+ native: "Беларуская",
218
+ writing_system: "Cyrillic"
219
+ },
220
+ {
221
+ code: "bg",
222
+ name: "Bulgarian",
223
+ native: "Български",
224
+ writing_system: "Cyrillic"
225
+ },
226
+ {
227
+ code: "bn",
228
+ name: "Bengali",
229
+ native: "বাংলা",
230
+ writing_system: "Bengali"
231
+ },
232
+ {
233
+ code: "bo",
234
+ name: "Tibetan",
235
+ native: "བོད་ཡིག / Bod skad",
236
+ writing_system: "Tibetan"
237
+ },
238
+ {
239
+ code: "br",
240
+ name: "Breton",
241
+ native: "Brezhoneg",
242
+ writing_system: "Latin"
243
+ },
244
+ {
245
+ code: "bs",
246
+ name: "Bosnian",
247
+ native: "Bosanski",
248
+ writing_system: "Latin"
249
+ },
250
+ {
251
+ code: "ca",
252
+ name: "Catalan",
253
+ native: "Català",
254
+ writing_system: "Latin"
255
+ },
256
+ {
257
+ code: "ch",
258
+ name: "Chamorro",
259
+ native: "Chamoru",
260
+ writing_system: "Latin"
261
+ },
262
+ {
263
+ code: "co",
264
+ name: "Corsican",
265
+ native: "Corsu",
266
+ writing_system: "Latin"
267
+ },
268
+ {
269
+ code: "cs",
270
+ name: "Czech",
271
+ native: "Česky",
272
+ writing_system: "Latin"
273
+ },
274
+ {
275
+ code: "cy",
276
+ name: "Welsh",
277
+ native: "Cymraeg",
278
+ writing_system: "Latin"
279
+ },
280
+ {
281
+ code: "da",
282
+ name: "Danish",
283
+ native: "Dansk",
284
+ writing_system: "Latin"
285
+ },
286
+ {
287
+ code: "de",
288
+ name: "German",
289
+ native: "Deutsch",
290
+ writing_system: "Latin"
291
+ },
292
+ {
293
+ code: "dv",
294
+ name: "Divehi",
295
+ native: "ދިވެހިބަސް",
296
+ rtl: 1,
297
+ writing_system: "Thaana"
298
+ },
299
+ {
300
+ code: "dz",
301
+ name: "Dzongkha",
302
+ native: "ཇོང་ཁ",
303
+ writing_system: "Tibetan"
304
+ },
305
+ {
306
+ code: "el",
307
+ name: "Greek",
308
+ native: "Ελληνικά",
309
+ writing_system: "Greek"
310
+ },
311
+ {
312
+ code: "en",
313
+ name: "English",
314
+ native: "English",
315
+ writing_system: "Latin"
316
+ },
317
+ {
318
+ code: "eo",
319
+ name: "Esperanto",
320
+ native: "Esperanto",
321
+ writing_system: "Latin"
322
+ },
323
+ {
324
+ code: "es",
325
+ name: "Spanish",
326
+ native: "Español",
327
+ writing_system: "Latin"
328
+ },
329
+ {
330
+ code: "et",
331
+ name: "Estonian",
332
+ native: "Eesti",
333
+ writing_system: "Latin"
334
+ },
335
+ {
336
+ code: "eu",
337
+ name: "Basque",
338
+ native: "Euskara",
339
+ writing_system: "Latin"
340
+ },
341
+ {
342
+ code: "fa",
343
+ name: "Persian",
344
+ native: "فارسی",
345
+ rtl: 1,
346
+ writing_system: "Arabic"
347
+ },
348
+ {
349
+ code: "ff",
350
+ name: "Peul",
351
+ native: "Fulfulde",
352
+ writing_system: "Latin"
353
+ },
354
+ {
355
+ code: "fi",
356
+ name: "Finnish",
357
+ native: "Suomi",
358
+ writing_system: "Latin"
359
+ },
360
+ {
361
+ code: "fj",
362
+ name: "Fijian",
363
+ native: "Na Vosa Vakaviti",
364
+ writing_system: "Latin"
365
+ },
366
+ {
367
+ code: "fo",
368
+ name: "Faroese",
369
+ native: "Føroyskt",
370
+ writing_system: "Latin"
371
+ },
372
+ {
373
+ code: "fr",
374
+ name: "French",
375
+ native: "Français",
376
+ writing_system: "Latin"
377
+ },
378
+ {
379
+ code: "fy",
380
+ name: "West Frisian",
381
+ native: "Frysk",
382
+ writing_system: "Latin"
383
+ },
384
+ {
385
+ code: "ga",
386
+ name: "Irish",
387
+ native: "Gaeilge",
388
+ writing_system: "Latin"
389
+ },
390
+ {
391
+ code: "gd",
392
+ name: "Scottish Gaelic",
393
+ native: "Gàidhlig",
394
+ writing_system: "Latin"
395
+ },
396
+ {
397
+ code: "gl",
398
+ name: "Galician",
399
+ native: "Galego",
400
+ writing_system: "Latin"
401
+ },
402
+ {
403
+ code: "gn",
404
+ name: "Guarani",
405
+ native: "Avañe'ẽ",
406
+ writing_system: "Latin"
407
+ },
408
+ {
409
+ code: "gu",
410
+ name: "Gujarati",
411
+ native: "ગુજરાતી",
412
+ writing_system: "Gujarati"
413
+ },
414
+ {
415
+ code: "gv",
416
+ name: "Manx",
417
+ native: "Gaelg",
418
+ writing_system: "Latin"
419
+ },
420
+ {
421
+ code: "ha",
422
+ name: "Hausa",
423
+ native: "هَوُسَ",
424
+ rtl: 1,
425
+ writing_system: "Latin"
426
+ },
427
+ {
428
+ code: "he",
429
+ name: "Hebrew",
430
+ native: "עברית",
431
+ rtl: 1,
432
+ writing_system: "Hebrew"
433
+ },
434
+ {
435
+ code: "hi",
436
+ name: "Hindi",
437
+ native: "हिन्दी",
438
+ writing_system: "Devanagari"
439
+ },
440
+ {
441
+ code: "hr",
442
+ name: "Croatian",
443
+ native: "Hrvatski",
444
+ writing_system: "Latin"
445
+ },
446
+ {
447
+ code: "ht",
448
+ name: "Haitian",
449
+ native: "Krèyol ayisyen",
450
+ writing_system: "Latin"
451
+ },
452
+ {
453
+ code: "hu",
454
+ name: "Hungarian",
455
+ native: "Magyar",
456
+ writing_system: "Latin"
457
+ },
458
+ {
459
+ code: "hy",
460
+ name: "Armenian",
461
+ native: "Հայերեն",
462
+ writing_system: "Armenian"
463
+ },
464
+ {
465
+ code: "id",
466
+ name: "Indonesian",
467
+ native: "Bahasa Indonesia",
468
+ writing_system: "Latin"
469
+ },
470
+ {
471
+ code: "ig",
472
+ name: "Igbo",
473
+ native: "Igbo",
474
+ writing_system: "Latin"
475
+ },
476
+ {
477
+ code: "is",
478
+ name: "Icelandic",
479
+ native: "Íslenska",
480
+ writing_system: "Latin"
481
+ },
482
+ {
483
+ code: "it",
484
+ name: "Italian",
485
+ native: "Italiano",
486
+ writing_system: "Latin"
487
+ },
488
+ {
489
+ code: "iu",
490
+ name: "Inuktitut",
491
+ native: "ᐃᓄᒃᑎᑐᑦ",
492
+ writing_system: "Unified Canadian Aboriginal Syllabics"
493
+ },
494
+ {
495
+ code: "ja",
496
+ name: "Japanese",
497
+ native: "日本語",
498
+ writing_system: "Japanese"
499
+ },
500
+ {
501
+ code: "jv",
502
+ name: "Javanese",
503
+ native: "Basa Jawa",
504
+ writing_system: "Javanese"
505
+ },
506
+ {
507
+ code: "ka",
508
+ name: "Georgian",
509
+ native: "ქართული",
510
+ writing_system: "Georgian"
511
+ },
512
+ {
513
+ code: "kg",
514
+ name: "Kongo",
515
+ native: "KiKongo",
516
+ writing_system: "Latin"
517
+ },
518
+ {
519
+ code: "ki",
520
+ name: "Kikuyu",
521
+ native: "Gĩkũyũ",
522
+ writing_system: "Latin"
523
+ },
524
+ {
525
+ code: "kj",
526
+ name: "Kuanyama",
527
+ native: "Kuanyama",
528
+ writing_system: "Latin"
529
+ },
530
+ {
531
+ code: "kk",
532
+ name: "Kazakh",
533
+ native: "Қазақша",
534
+ writing_system: "Cyrillic"
535
+ },
536
+ {
537
+ code: "kl",
538
+ name: "Greenlandic",
539
+ native: "Kalaallisut",
540
+ writing_system: "Latin"
541
+ },
542
+ {
543
+ code: "km",
544
+ name: "Cambodian",
545
+ native: "ភាសាខ្មែរ",
546
+ writing_system: "Khmer"
547
+ },
548
+ {
549
+ code: "kn",
550
+ name: "Kannada",
551
+ native: "ಕನ್ನಡ",
552
+ writing_system: "Kannada"
553
+ },
554
+ {
555
+ code: "ko",
556
+ name: "Korean",
557
+ native: "한국어",
558
+ writing_system: "Korean"
559
+ },
560
+ {
561
+ code: "kr",
562
+ name: "Kanuri",
563
+ native: "Kanuri",
564
+ writing_system: "Latin"
565
+ },
566
+ {
567
+ code: "ks",
568
+ name: "Kashmiri",
569
+ native: "कश्मीरी / كشميري",
570
+ rtl: 1,
571
+ writing_system: "Arabic"
572
+ },
573
+ {
574
+ code: "ku",
575
+ name: "Kurdish",
576
+ native: "Kurdî / كوردی",
577
+ rtl: 1,
578
+ writing_system: "Arabic"
579
+ },
580
+ {
581
+ code: "kv",
582
+ name: "Komi",
583
+ native: "Коми",
584
+ writing_system: "Cyrillic"
585
+ },
586
+ {
587
+ code: "kw",
588
+ name: "Cornish",
589
+ native: "Kernewek",
590
+ writing_system: "Latin"
591
+ },
592
+ {
593
+ code: "ky",
594
+ name: "Kirghiz",
595
+ native: "Kırgızca / Кыргызча",
596
+ writing_system: "Cyrillic"
597
+ },
598
+ {
599
+ code: "la",
600
+ name: "Latin",
601
+ native: "Latina",
602
+ writing_system: "Latin"
603
+ },
604
+ {
605
+ code: "lb",
606
+ name: "Luxembourgish",
607
+ native: "Lëtzebuergesch",
608
+ writing_system: "Latin"
609
+ },
610
+ {
611
+ code: "lg",
612
+ name: "Ganda",
613
+ native: "Luganda",
614
+ writing_system: "Latin"
615
+ },
616
+ {
617
+ code: "li",
618
+ name: "Limburgian",
619
+ native: "Limburgs",
620
+ writing_system: "Latin"
621
+ },
622
+ {
623
+ code: "ln",
624
+ name: "Lingala",
625
+ native: "Lingála",
626
+ writing_system: "Latin"
627
+ },
628
+ {
629
+ code: "lo",
630
+ name: "Laotian",
631
+ native: "ລາວ / Pha xa lao",
632
+ writing_system: "Lao"
633
+ },
634
+ {
635
+ code: "lt",
636
+ name: "Lithuanian",
637
+ native: "Lietuvių",
638
+ writing_system: "Latin"
639
+ },
640
+ {
641
+ code: "lu",
642
+ name: "Luba-Katanga",
643
+ native: "Tshiluba",
644
+ writing_system: "Latin"
645
+ },
646
+ {
647
+ code: "lv",
648
+ name: "Latvian",
649
+ native: "Latviešu",
650
+ writing_system: "Latin"
651
+ },
652
+ {
653
+ code: "mg",
654
+ name: "Malagasy",
655
+ native: "Malagasy",
656
+ writing_system: "Latin"
657
+ },
658
+ {
659
+ code: "mh",
660
+ name: "Marshallese",
661
+ native: "Kajin Majel / Ebon",
662
+ writing_system: "Latin"
663
+ },
664
+ {
665
+ code: "mi",
666
+ name: "Maori",
667
+ native: "Māori",
668
+ writing_system: "Latin"
669
+ },
670
+ {
671
+ code: "mk",
672
+ name: "Macedonian",
673
+ native: "Македонски",
674
+ writing_system: "Cyrillic"
675
+ },
676
+ {
677
+ code: "ml",
678
+ name: "Malayalam",
679
+ native: "മലയാളം",
680
+ writing_system: "Malayalam"
681
+ },
682
+ {
683
+ code: "mn",
684
+ name: "Mongolian",
685
+ native: "Монгол",
686
+ writing_system: "Mongolian"
687
+ },
688
+ {
689
+ code: "mo",
690
+ name: "Moldovan",
691
+ native: "Moldovenească",
692
+ writing_system: "Latin"
693
+ },
694
+ {
695
+ code: "mr",
696
+ name: "Marathi",
697
+ native: "मराठी",
698
+ writing_system: "Devanagari"
699
+ },
700
+ {
701
+ code: "ms",
702
+ name: "Malay",
703
+ native: "Bahasa Melayu",
704
+ writing_system: "Latin"
705
+ },
706
+ {
707
+ code: "mt",
708
+ name: "Maltese",
709
+ native: "bil-Malti",
710
+ writing_system: "Latin"
711
+ },
712
+ {
713
+ code: "my",
714
+ name: "Burmese",
715
+ native: "မြန်မာစာ",
716
+ writing_system: "Myanmar"
717
+ },
718
+ {
719
+ code: "na",
720
+ name: "Nauruan",
721
+ native: "Dorerin Naoero",
722
+ writing_system: "Latin"
723
+ },
724
+ {
725
+ code: "nb",
726
+ name: "Norwegian Bokmål",
727
+ native: "Norsk bokmål",
728
+ writing_system: "Latin"
729
+ },
730
+ {
731
+ code: "nd",
732
+ name: "North Ndebele",
733
+ native: "Sindebele",
734
+ writing_system: "Latin"
735
+ },
736
+ {
737
+ code: "ne",
738
+ name: "Nepali",
739
+ native: "नेपाली",
740
+ writing_system: "Devanagari"
741
+ },
742
+ {
743
+ code: "ng",
744
+ name: "Ndonga",
745
+ native: "Oshiwambo",
746
+ writing_system: "Latin"
747
+ },
748
+ {
749
+ code: "nl",
750
+ name: "Dutch",
751
+ native: "Nederlands",
752
+ writing_system: "Latin"
753
+ },
754
+ {
755
+ code: "nn",
756
+ name: "Norwegian Nynorsk",
757
+ native: "Norsk nynorsk",
758
+ writing_system: "Latin"
759
+ },
760
+ {
761
+ code: "no",
762
+ name: "Norwegian",
763
+ native: "Norsk",
764
+ writing_system: "Latin"
765
+ },
766
+ {
767
+ code: "nr",
768
+ name: "South Ndebele",
769
+ native: "isiNdebele",
770
+ writing_system: "Latin"
771
+ },
772
+ {
773
+ code: "nv",
774
+ name: "Navajo",
775
+ native: "Diné bizaad",
776
+ writing_system: "Latin"
777
+ },
778
+ {
779
+ code: "ny",
780
+ name: "Chichewa",
781
+ native: "Chi-Chewa",
782
+ writing_system: "Latin"
783
+ },
784
+ {
785
+ code: "oc",
786
+ name: "Occitan",
787
+ native: "Occitan",
788
+ writing_system: "Latin"
789
+ },
790
+ {
791
+ code: "oj",
792
+ name: "Ojibwa",
793
+ native: "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin",
794
+ writing_system: "Unified Canadian Aboriginal Syllabics"
795
+ },
796
+ {
797
+ code: "om",
798
+ name: "Oromo",
799
+ native: "Oromoo",
800
+ writing_system: "Latin"
801
+ },
802
+ {
803
+ code: "or",
804
+ name: "Oriya",
805
+ native: "ଓଡ଼ିଆ",
806
+ writing_system: "Odia"
807
+ },
808
+ {
809
+ code: "os",
810
+ name: "Ossetian / Ossetic",
811
+ native: "Иронау",
812
+ writing_system: "Cyrillic"
813
+ },
814
+ {
815
+ code: "pa",
816
+ name: "Panjabi / Punjabi",
817
+ native: "ਪੰਜਾਬੀ / पंजाबी / پنجابي",
818
+ writing_system: "Gurmukhi"
819
+ },
820
+ {
821
+ code: "pi",
822
+ name: "Pali",
823
+ native: "Pāli / पाऴि",
824
+ writing_system: "Devanagari"
825
+ },
826
+ {
827
+ code: "pl",
828
+ name: "Polish",
829
+ native: "Polski",
830
+ writing_system: "Latin"
831
+ },
832
+ {
833
+ code: "ps",
834
+ name: "Pashto",
835
+ native: "پښتو",
836
+ rtl: 1,
837
+ writing_system: "Arabic"
838
+ },
839
+ {
840
+ code: "pt",
841
+ name: "Portuguese",
842
+ native: "Português",
843
+ writing_system: "Latin"
844
+ },
845
+ {
846
+ code: "qu",
847
+ name: "Quechua",
848
+ native: "Runa Simi",
849
+ writing_system: "Latin"
850
+ },
851
+ {
852
+ code: "rm",
853
+ name: "Raeto Romance",
854
+ native: "Rumantsch",
855
+ writing_system: "Latin"
856
+ },
857
+ {
858
+ code: "rn",
859
+ name: "Kirundi",
860
+ native: "Kirundi",
861
+ writing_system: "Latin"
862
+ },
863
+ {
864
+ code: "ro",
865
+ name: "Romanian",
866
+ native: "Română",
867
+ writing_system: "Latin"
868
+ },
869
+ {
870
+ code: "ru",
871
+ name: "Russian",
872
+ native: "Русский",
873
+ writing_system: "Cyrillic"
874
+ },
875
+ {
876
+ code: "rw",
877
+ name: "Rwandi",
878
+ native: "Kinyarwandi",
879
+ writing_system: "Latin"
880
+ },
881
+ {
882
+ code: "sa",
883
+ name: "Sanskrit",
884
+ native: "संस्कृतम्",
885
+ writing_system: "Devanagari"
886
+ },
887
+ {
888
+ code: "sc",
889
+ name: "Sardinian",
890
+ native: "Sardu",
891
+ writing_system: "Latin"
892
+ },
893
+ {
894
+ code: "sd",
895
+ name: "Sindhi",
896
+ native: "सिनधि",
897
+ writing_system: "Arabic"
898
+ },
899
+ {
900
+ code: "se",
901
+ name: "Northern Sami",
902
+ native: "Sámegiella",
903
+ writing_system: "Latin"
904
+ },
905
+ {
906
+ code: "sg",
907
+ name: "Sango",
908
+ native: "Sängö",
909
+ writing_system: "Latin"
910
+ },
911
+ {
912
+ code: "sh",
913
+ name: "Serbo-Croatian",
914
+ native: "Srpskohrvatski / Српскохрватски",
915
+ writing_system: "Latin"
916
+ },
917
+ {
918
+ code: "si",
919
+ name: "Sinhalese",
920
+ native: "සිංහල",
921
+ writing_system: "Sinhala"
922
+ },
923
+ {
924
+ code: "sk",
925
+ name: "Slovak",
926
+ native: "Slovenčina",
927
+ writing_system: "Latin"
928
+ },
929
+ {
930
+ code: "sl",
931
+ name: "Slovenian",
932
+ native: "Slovenščina",
933
+ writing_system: "Latin"
934
+ },
935
+ {
936
+ code: "sm",
937
+ name: "Samoan",
938
+ native: "Gagana Samoa",
939
+ writing_system: "Latin"
940
+ },
941
+ {
942
+ code: "sn",
943
+ name: "Shona",
944
+ native: "chiShona",
945
+ writing_system: "Latin"
946
+ },
947
+ {
948
+ code: "so",
949
+ name: "Somalia",
950
+ native: "Soomaaliga",
951
+ writing_system: "Latin"
952
+ },
953
+ {
954
+ code: "sq",
955
+ name: "Albanian",
956
+ native: "Shqip",
957
+ writing_system: "Latin"
958
+ },
959
+ {
960
+ code: "sr",
961
+ name: "Serbian",
962
+ native: "Српски",
963
+ writing_system: "Cyrillic"
964
+ },
965
+ {
966
+ code: "ss",
967
+ name: "Swati",
968
+ native: "SiSwati",
969
+ writing_system: "Latin"
970
+ },
971
+ {
972
+ code: "st",
973
+ name: "Southern Sotho",
974
+ native: "Sesotho",
975
+ writing_system: "Latin"
976
+ },
977
+ {
978
+ code: "su",
979
+ name: "Sundanese",
980
+ native: "Basa Sunda",
981
+ writing_system: "Sundanese"
982
+ },
983
+ {
984
+ code: "sv",
985
+ name: "Swedish",
986
+ native: "Svenska",
987
+ writing_system: "Latin"
988
+ },
989
+ {
990
+ code: "sw",
991
+ name: "Swahili",
992
+ native: "Kiswahili",
993
+ writing_system: "Latin"
994
+ },
995
+ {
996
+ code: "ta",
997
+ name: "Tamil",
998
+ native: "தமிழ்",
999
+ writing_system: "Tamil"
1000
+ },
1001
+ {
1002
+ code: "te",
1003
+ name: "Telugu",
1004
+ native: "తెలుగు",
1005
+ writing_system: "Telugu"
1006
+ },
1007
+ {
1008
+ code: "tg",
1009
+ name: "Tajik",
1010
+ native: "Тоҷикӣ",
1011
+ writing_system: "Cyrillic"
1012
+ },
1013
+ {
1014
+ code: "th",
1015
+ name: "Thai",
1016
+ native: "ไทย / Phasa Thai",
1017
+ writing_system: "Thai"
1018
+ },
1019
+ {
1020
+ code: "ti",
1021
+ name: "Tigrinya",
1022
+ native: "ትግርኛ",
1023
+ writing_system: "Ethiopic"
1024
+ },
1025
+ {
1026
+ code: "tk",
1027
+ name: "Turkmen",
1028
+ native: "Туркмен / تركمن",
1029
+ writing_system: "Latin"
1030
+ },
1031
+ {
1032
+ code: "tl",
1033
+ name: "Tagalog / Filipino",
1034
+ native: "Tagalog",
1035
+ writing_system: "Latin"
1036
+ },
1037
+ {
1038
+ code: "tn",
1039
+ name: "Tswana",
1040
+ native: "Setswana",
1041
+ writing_system: "Latin"
1042
+ },
1043
+ {
1044
+ code: "to",
1045
+ name: "Tonga",
1046
+ native: "Lea Faka-Tonga",
1047
+ writing_system: "Latin"
1048
+ },
1049
+ {
1050
+ code: "tr",
1051
+ name: "Turkish",
1052
+ native: "Türkçe",
1053
+ writing_system: "Latin"
1054
+ },
1055
+ {
1056
+ code: "ts",
1057
+ name: "Tsonga",
1058
+ native: "Xitsonga",
1059
+ writing_system: "Latin"
1060
+ },
1061
+ {
1062
+ code: "tt",
1063
+ name: "Tatar",
1064
+ native: "Tatarça",
1065
+ writing_system: "Cyrillic"
1066
+ },
1067
+ {
1068
+ code: "tw",
1069
+ name: "Twi",
1070
+ native: "Twi",
1071
+ writing_system: "Latin"
1072
+ },
1073
+ {
1074
+ code: "ty",
1075
+ name: "Tahitian",
1076
+ native: "Reo Mā`ohi",
1077
+ writing_system: "Latin"
1078
+ },
1079
+ {
1080
+ code: "ug",
1081
+ name: "Uyghur",
1082
+ native: "Uyƣurqə / ئۇيغۇرچە",
1083
+ writing_system: "Arabic"
1084
+ },
1085
+ {
1086
+ code: "uk",
1087
+ name: "Ukrainian",
1088
+ native: "Українська",
1089
+ writing_system: "Cyrillic"
1090
+ },
1091
+ {
1092
+ code: "ur",
1093
+ name: "Urdu",
1094
+ native: "اردو",
1095
+ rtl: 1,
1096
+ writing_system: "Arabic"
1097
+ },
1098
+ {
1099
+ code: "uz",
1100
+ name: "Uzbek",
1101
+ native: "Ўзбек",
1102
+ writing_system: "Latin"
1103
+ },
1104
+ {
1105
+ code: "ve",
1106
+ name: "Venda",
1107
+ native: "Tshivenḓa",
1108
+ writing_system: "Latin"
1109
+ },
1110
+ {
1111
+ code: "vi",
1112
+ name: "Vietnamese",
1113
+ native: "Tiếng Việt",
1114
+ writing_system: "Latin"
1115
+ },
1116
+ {
1117
+ code: "vo",
1118
+ name: "Volapük",
1119
+ native: "Volapük",
1120
+ writing_system: "Latin"
1121
+ },
1122
+ {
1123
+ code: "wo",
1124
+ name: "Wolof",
1125
+ native: "Wollof",
1126
+ writing_system: "Latin"
1127
+ },
1128
+ {
1129
+ code: "xh",
1130
+ name: "Xhosa",
1131
+ native: "isiXhosa",
1132
+ writing_system: "Latin"
1133
+ },
1134
+ {
1135
+ code: "yi",
1136
+ name: "Yiddish",
1137
+ native: "ייִדיש",
1138
+ rtl: 1,
1139
+ writing_system: "Hebrew"
1140
+ },
1141
+ {
1142
+ code: "yo",
1143
+ name: "Yoruba",
1144
+ native: "Yorùbá",
1145
+ writing_system: "Latin"
1146
+ },
1147
+ {
1148
+ code: "zh",
1149
+ name: "Chinese (Simplified)",
1150
+ native: "简体中文",
1151
+ writing_system: "Simplied Han"
1152
+ },
1153
+ {
1154
+ code: "zh-TW",
1155
+ name: "Chinese (Traditional)",
1156
+ native: "繁體中文",
1157
+ writing_system: "Traditional Han"
1158
+ },
1159
+ {
1160
+ code: "zu",
1161
+ name: "Zulu",
1162
+ native: "isiZulu",
1163
+ writing_system: "Latin"
1164
+ }
1165
+ ];
1166
+ function generateHashForContent(nodes) {
1167
+ const content = nodes.map((node) => {
1168
+ return node.text.replace(/\s+/g, " ").trim().toLocaleLowerCase();
1169
+ }).join(" ").trim();
1170
+ const hash = murmurhash3_32_gc(content.toLowerCase(), 42).toString(16);
1171
+ return hash;
1172
+ }
1173
+ function murmurhash3_32_gc(key, seed) {
1174
+ let remainder = key.length & 3, bytes = key.length - remainder;
1175
+ let h1 = seed, c1 = 3432918353, c2 = 461845907;
1176
+ let i = 0;
1177
+ while (i < bytes) {
1178
+ let k12 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
1179
+ ++i;
1180
+ k12 = (k12 & 65535) * c1 + (((k12 >>> 16) * c1 & 65535) << 16) & 4294967295;
1181
+ k12 = k12 << 15 | k12 >>> 17;
1182
+ k12 = (k12 & 65535) * c2 + (((k12 >>> 16) * c2 & 65535) << 16) & 4294967295;
1183
+ h1 ^= k12;
1184
+ h1 = h1 << 13 | h1 >>> 19;
1185
+ const h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
1186
+ h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
1187
+ }
1188
+ let k1 = 0;
1189
+ switch (remainder) {
1190
+ //@ts-expect-error - this is a valid case
1191
+ case 3:
1192
+ k1 ^= key.charCodeAt(i + 2) << 16;
1193
+ //@ts-expect-error - this is a valid case
1194
+ case 2:
1195
+ k1 ^= key.charCodeAt(i + 1) << 8;
1196
+ case 1:
1197
+ k1 ^= key.charCodeAt(i);
1198
+ k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
1199
+ k1 = k1 << 15 | k1 >>> 17;
1200
+ k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
1201
+ h1 ^= k1;
1202
+ }
1203
+ h1 ^= key.length;
1204
+ h1 ^= h1 >>> 16;
1205
+ h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
1206
+ h1 ^= h1 >>> 13;
1207
+ h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
1208
+ h1 ^= h1 >>> 16;
1209
+ return h1 >>> 0;
1210
+ }
1211
+ const removeEmojis = (text) => text.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, "");
1212
+ const getUserLanguage = () => {
1213
+ const userLanguages = window.navigator.languages;
1214
+ const userLanguage = languages.find((lang) => userLanguages.includes(lang.code));
1215
+ return (userLanguage == null ? void 0 : userLanguage.code) || "en";
1216
+ };
1217
+ function validatePublicApiKey(publicKey) {
1218
+ if (!publicKey) {
1219
+ return {
1220
+ isValid: false,
1221
+ message: "Public key is required to initialize the translation widget"
1222
+ };
1223
+ }
1224
+ if (publicKey.startsWith("sk_")) {
1225
+ return {
1226
+ isValid: false,
1227
+ message: "Please use public api key for security reasons. You can get one from https://jigsawstack.com"
1228
+ };
1229
+ }
1230
+ if (!publicKey.startsWith("pk_")) {
1231
+ return {
1232
+ isValid: false,
1233
+ message: "Please use proper api key. You can get one from https://jigsawstack.com"
1234
+ };
1235
+ }
1236
+ return { isValid: true };
1237
+ }
1238
+ class DocumentNavigator {
1239
+ /**
1240
+ * Retrieves text nodes eligible for translation from the document
1241
+ * @returns Collection of text nodes ready for translation
1242
+ */
1243
+ static findTranslatableContent() {
1244
+ var _a;
1245
+ if (typeof window === "undefined") return [];
1246
+ const validator = {
1247
+ acceptNode(node) {
1248
+ var _a2;
1249
+ if (node.nodeType !== Node.TEXT_NODE) return NodeFilter.FILTER_REJECT;
1250
+ const container = node.parentElement;
1251
+ if (!container) return NodeFilter.FILTER_REJECT;
1252
+ if (container.closest('[aria-hidden="true"]')) return NodeFilter.FILTER_REJECT;
1253
+ if (container.classList.contains("sr-only")) return NodeFilter.FILTER_REJECT;
1254
+ const skipBySelector = container.closest(
1255
+ "script, style, code, noscript, next-route-announcer, .jigts-translation-widget, .jigts-widget-trigger, .jigts-widget-dropdown, .notranslate"
1256
+ );
1257
+ if (skipBySelector) return NodeFilter.FILTER_REJECT;
1258
+ const isInSpanWrapper = container.tagName === "SPAN" && Array.from(container.childNodes).every((n) => n.nodeType === Node.TEXT_NODE);
1259
+ const interactiveAncestor = container.closest(
1260
+ "button, input, select, textarea, [role='button'], [role='link']"
1261
+ );
1262
+ if (interactiveAncestor && !isInSpanWrapper) {
1263
+ const isTextSiblingToElement = Array.from(container.childNodes).some(
1264
+ (n) => n.nodeType === Node.ELEMENT_NODE && node.parentNode === container
1265
+ // sibling to another element in same container
1266
+ );
1267
+ const isDirectChildOfInteractive = interactiveAncestor === container;
1268
+ if (isTextSiblingToElement && isDirectChildOfInteractive) {
1269
+ return NodeFilter.FILTER_REJECT;
1270
+ }
1271
+ }
1272
+ if (!((_a2 = node.textContent) == null ? void 0 : _a2.trim())) return NodeFilter.FILTER_REJECT;
1273
+ return NodeFilter.FILTER_ACCEPT;
1274
+ }
1275
+ };
1276
+ const navigator = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, validator);
1277
+ const groupedText = /* @__PURE__ */ new Map();
1278
+ let currentNode;
1279
+ while (currentNode = navigator.nextNode()) {
1280
+ const parentElement = currentNode.parentElement;
1281
+ if (!parentElement) continue;
1282
+ if (!groupedText.has(parentElement)) {
1283
+ groupedText.set(parentElement, []);
1284
+ }
1285
+ groupedText.get(parentElement).push(currentNode);
1286
+ }
1287
+ const results = [];
1288
+ let isNested = false;
1289
+ const processedNestedElements = /* @__PURE__ */ new Set();
1290
+ for (const [element, textNodes] of groupedText.entries()) {
1291
+ isNested = false;
1292
+ let combinedText = "";
1293
+ let isDescendantOfNested = false;
1294
+ for (const nestedElement of processedNestedElements) {
1295
+ if (nestedElement.contains(element)) {
1296
+ isDescendantOfNested = true;
1297
+ break;
1298
+ }
1299
+ }
1300
+ if (isDescendantOfNested) continue;
1301
+ const childNodes = Array.from(element.childNodes);
1302
+ const hasText = childNodes.some(
1303
+ (n) => {
1304
+ var _a2;
1305
+ return n.nodeType === Node.TEXT_NODE && ((_a2 = n.textContent) == null ? void 0 : _a2.trim());
1306
+ }
1307
+ );
1308
+ const hasInteractiveElements = childNodes.some(
1309
+ (n) => n.nodeType === Node.ELEMENT_NODE && ["BUTTON", "INPUT", "TEXTAREA", "SELECT"].includes(
1310
+ n.tagName
1311
+ )
1312
+ );
1313
+ const isTextMixedWithInteractivity = hasText && hasInteractiveElements;
1314
+ if (isTextMixedWithInteractivity) {
1315
+ continue;
1316
+ }
1317
+ for (const node of textNodes) {
1318
+ let text = ((_a = node.textContent) == null ? void 0 : _a.trim()) || "";
1319
+ const originalText = element.getAttribute("data-original-text");
1320
+ if (originalText) text = originalText;
1321
+ const textWithoutEmojis = removeEmojis(text);
1322
+ if (text.length === 0 || text.length === 1 || textWithoutEmojis.length === 0) continue;
1323
+ combinedText += (combinedText ? " " : "") + text;
1324
+ const hasMixedContent = Array.from(element.childNodes).some(
1325
+ (child) => child.nodeType !== Node.TEXT_NODE
1326
+ );
1327
+ if (hasMixedContent) {
1328
+ isNested = true;
1329
+ }
1330
+ }
1331
+ if (combinedText.length > 0) {
1332
+ if (isNested) {
1333
+ const fullHtml = element.outerHTML;
1334
+ results.push({ element, text: fullHtml, isNested });
1335
+ processedNestedElements.add(element);
1336
+ } else {
1337
+ results.push({ element, text: combinedText, isNested });
1338
+ }
1339
+ }
1340
+ }
1341
+ return results;
1342
+ }
1343
+ /**
1344
+ * Divides a collection into smaller groups
1345
+ * @param items Collection to divide
1346
+ * @param groupSize Maximum size of each group
1347
+ * @returns Array of item groups
1348
+ */
1349
+ static divideIntoGroups(items, groupSize) {
1350
+ const groups = [];
1351
+ for (let i = 0; i < items.length; i += groupSize) {
1352
+ groups.push(items.slice(i, i + groupSize));
1353
+ }
1354
+ return groups;
1355
+ }
1356
+ }
1357
+ var LOCALSTORAGE_KEYS = /* @__PURE__ */ ((LOCALSTORAGE_KEYS2) => {
1358
+ LOCALSTORAGE_KEYS2["PREFERRED_LANGUAGE"] = "jss-pref";
1359
+ return LOCALSTORAGE_KEYS2;
1360
+ })(LOCALSTORAGE_KEYS || {});
1361
+ var ATTRIBUTES = /* @__PURE__ */ ((ATTRIBUTES2) => {
1362
+ ATTRIBUTES2["TRANSLATED_LANG"] = "data-translated-lang";
1363
+ ATTRIBUTES2["ORIGINAL_TEXT"] = "data-original-text";
1364
+ ATTRIBUTES2["ORIGINAL_FONT_SIZE"] = "data-original-font-size";
1365
+ return ATTRIBUTES2;
1366
+ })(ATTRIBUTES || {});
1367
+ const LANG_PARAM = "lang";
1368
+ const widgetTemplate = '<!-- Widget Trigger Button -->\r\n<div class="jigts-widget-trigger" tabindex="0" role="button" aria-label="Open translation menu" aria-expanded="false">\r\n <!-- Normal State -->\r\n <div class="jigts-trigger-content">\r\n <span class="jigts-trigger-icon">\r\n <svg class="jigts-languages-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">\r\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"\r\n d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129">\r\n </path>\r\n </svg>\r\n </span>\r\n </div>\r\n\r\n <!-- Loading State (hidden by default) -->\r\n <div class="jigts-trigger-loading" style="display: none;">\r\n <div class="jigts-loading-spinner"></div>\r\n </div>\r\n</div>\r\n\r\n<!-- Dropdown Menu -->\r\n<div class="jigts-widget-dropdown">\r\n <!-- Header -->\r\n <div class="jigts-dropdown-header">\r\n <div class="jigts-dropdown-title">\r\n <div class="jigts-title-left">\r\n <svg class="jigts-languages-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">\r\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"\r\n d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129">\r\n </path>\r\n </svg>\r\n <span class="jigts-title-text">Select Language</span>\r\n </div>\r\n <div class="jigts-language-count">{{languageCountLabel}}</div>\r\n </div>\r\n\r\n {{searchSection}}\r\n </div>\r\n\r\n <!-- Reset Option -->\r\n <div class="jigts-reset-option" tabindex="0" role="button" aria-label="Reset to original language">\r\n <svg class="jigts-reset-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">\r\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"\r\n d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path>\r\n </svg>\r\n <div class="jigts-reset-text">\r\n <span class="jigts-reset-title">Original Language</span>\r\n <span class="jigts-reset-subtitle">Reset translation</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Language List -->\r\n <div class="jigts-language-list">\r\n {{languageOptions}}\r\n <div class="jigts-no-results" style="display: none;">\r\n <svg class="jigts-no-results-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">\r\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"\r\n d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>\r\n </svg>\r\n <span>No languages found</span>\r\n </div>\r\n </div>\r\n</div>\r\n';
1369
+ function getDefaultExportFromCjs(x) {
1370
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
1371
+ }
1372
+ var lzString = { exports: {} };
1373
+ var hasRequiredLzString;
1374
+ function requireLzString() {
1375
+ if (hasRequiredLzString) return lzString.exports;
1376
+ hasRequiredLzString = 1;
1377
+ (function(module) {
1378
+ var LZString2 = (function() {
1379
+ var f = String.fromCharCode;
1380
+ var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1381
+ var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
1382
+ var baseReverseDic = {};
1383
+ function getBaseValue(alphabet, character) {
1384
+ if (!baseReverseDic[alphabet]) {
1385
+ baseReverseDic[alphabet] = {};
1386
+ for (var i = 0; i < alphabet.length; i++) {
1387
+ baseReverseDic[alphabet][alphabet.charAt(i)] = i;
1388
+ }
1389
+ }
1390
+ return baseReverseDic[alphabet][character];
1391
+ }
1392
+ var LZString3 = {
1393
+ compressToBase64: function(input) {
1394
+ if (input == null) return "";
1395
+ var res = LZString3._compress(input, 6, function(a) {
1396
+ return keyStrBase64.charAt(a);
1397
+ });
1398
+ switch (res.length % 4) {
1399
+ // To produce valid Base64
1400
+ default:
1401
+ // When could this happen ?
1402
+ case 0:
1403
+ return res;
1404
+ case 1:
1405
+ return res + "===";
1406
+ case 2:
1407
+ return res + "==";
1408
+ case 3:
1409
+ return res + "=";
1410
+ }
1411
+ },
1412
+ decompressFromBase64: function(input) {
1413
+ if (input == null) return "";
1414
+ if (input == "") return null;
1415
+ return LZString3._decompress(input.length, 32, function(index) {
1416
+ return getBaseValue(keyStrBase64, input.charAt(index));
1417
+ });
1418
+ },
1419
+ compressToUTF16: function(input) {
1420
+ if (input == null) return "";
1421
+ return LZString3._compress(input, 15, function(a) {
1422
+ return f(a + 32);
1423
+ }) + " ";
1424
+ },
1425
+ decompressFromUTF16: function(compressed) {
1426
+ if (compressed == null) return "";
1427
+ if (compressed == "") return null;
1428
+ return LZString3._decompress(compressed.length, 16384, function(index) {
1429
+ return compressed.charCodeAt(index) - 32;
1430
+ });
1431
+ },
1432
+ //compress into uint8array (UCS-2 big endian format)
1433
+ compressToUint8Array: function(uncompressed) {
1434
+ var compressed = LZString3.compress(uncompressed);
1435
+ var buf = new Uint8Array(compressed.length * 2);
1436
+ for (var i = 0, TotalLen = compressed.length; i < TotalLen; i++) {
1437
+ var current_value = compressed.charCodeAt(i);
1438
+ buf[i * 2] = current_value >>> 8;
1439
+ buf[i * 2 + 1] = current_value % 256;
1440
+ }
1441
+ return buf;
1442
+ },
1443
+ //decompress from uint8array (UCS-2 big endian format)
1444
+ decompressFromUint8Array: function(compressed) {
1445
+ if (compressed === null || compressed === void 0) {
1446
+ return LZString3.decompress(compressed);
1447
+ } else {
1448
+ var buf = new Array(compressed.length / 2);
1449
+ for (var i = 0, TotalLen = buf.length; i < TotalLen; i++) {
1450
+ buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1];
1451
+ }
1452
+ var result = [];
1453
+ buf.forEach(function(c) {
1454
+ result.push(f(c));
1455
+ });
1456
+ return LZString3.decompress(result.join(""));
1457
+ }
1458
+ },
1459
+ //compress into a string that is already URI encoded
1460
+ compressToEncodedURIComponent: function(input) {
1461
+ if (input == null) return "";
1462
+ return LZString3._compress(input, 6, function(a) {
1463
+ return keyStrUriSafe.charAt(a);
1464
+ });
1465
+ },
1466
+ //decompress from an output of compressToEncodedURIComponent
1467
+ decompressFromEncodedURIComponent: function(input) {
1468
+ if (input == null) return "";
1469
+ if (input == "") return null;
1470
+ input = input.replace(/ /g, "+");
1471
+ return LZString3._decompress(input.length, 32, function(index) {
1472
+ return getBaseValue(keyStrUriSafe, input.charAt(index));
1473
+ });
1474
+ },
1475
+ compress: function(uncompressed) {
1476
+ return LZString3._compress(uncompressed, 16, function(a) {
1477
+ return f(a);
1478
+ });
1479
+ },
1480
+ _compress: function(uncompressed, bitsPerChar, getCharFromInt) {
1481
+ if (uncompressed == null) return "";
1482
+ var i, value, context_dictionary = {}, context_dictionaryToCreate = {}, context_c = "", context_wc = "", context_w = "", context_enlargeIn = 2, context_dictSize = 3, context_numBits = 2, context_data = [], context_data_val = 0, context_data_position = 0, ii;
1483
+ for (ii = 0; ii < uncompressed.length; ii += 1) {
1484
+ context_c = uncompressed.charAt(ii);
1485
+ if (!Object.prototype.hasOwnProperty.call(context_dictionary, context_c)) {
1486
+ context_dictionary[context_c] = context_dictSize++;
1487
+ context_dictionaryToCreate[context_c] = true;
1488
+ }
1489
+ context_wc = context_w + context_c;
1490
+ if (Object.prototype.hasOwnProperty.call(context_dictionary, context_wc)) {
1491
+ context_w = context_wc;
1492
+ } else {
1493
+ if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
1494
+ if (context_w.charCodeAt(0) < 256) {
1495
+ for (i = 0; i < context_numBits; i++) {
1496
+ context_data_val = context_data_val << 1;
1497
+ if (context_data_position == bitsPerChar - 1) {
1498
+ context_data_position = 0;
1499
+ context_data.push(getCharFromInt(context_data_val));
1500
+ context_data_val = 0;
1501
+ } else {
1502
+ context_data_position++;
1503
+ }
1504
+ }
1505
+ value = context_w.charCodeAt(0);
1506
+ for (i = 0; i < 8; i++) {
1507
+ context_data_val = context_data_val << 1 | value & 1;
1508
+ if (context_data_position == bitsPerChar - 1) {
1509
+ context_data_position = 0;
1510
+ context_data.push(getCharFromInt(context_data_val));
1511
+ context_data_val = 0;
1512
+ } else {
1513
+ context_data_position++;
1514
+ }
1515
+ value = value >> 1;
1516
+ }
1517
+ } else {
1518
+ value = 1;
1519
+ for (i = 0; i < context_numBits; i++) {
1520
+ context_data_val = context_data_val << 1 | value;
1521
+ if (context_data_position == bitsPerChar - 1) {
1522
+ context_data_position = 0;
1523
+ context_data.push(getCharFromInt(context_data_val));
1524
+ context_data_val = 0;
1525
+ } else {
1526
+ context_data_position++;
1527
+ }
1528
+ value = 0;
1529
+ }
1530
+ value = context_w.charCodeAt(0);
1531
+ for (i = 0; i < 16; i++) {
1532
+ context_data_val = context_data_val << 1 | value & 1;
1533
+ if (context_data_position == bitsPerChar - 1) {
1534
+ context_data_position = 0;
1535
+ context_data.push(getCharFromInt(context_data_val));
1536
+ context_data_val = 0;
1537
+ } else {
1538
+ context_data_position++;
1539
+ }
1540
+ value = value >> 1;
1541
+ }
1542
+ }
1543
+ context_enlargeIn--;
1544
+ if (context_enlargeIn == 0) {
1545
+ context_enlargeIn = Math.pow(2, context_numBits);
1546
+ context_numBits++;
1547
+ }
1548
+ delete context_dictionaryToCreate[context_w];
1549
+ } else {
1550
+ value = context_dictionary[context_w];
1551
+ for (i = 0; i < context_numBits; i++) {
1552
+ context_data_val = context_data_val << 1 | value & 1;
1553
+ if (context_data_position == bitsPerChar - 1) {
1554
+ context_data_position = 0;
1555
+ context_data.push(getCharFromInt(context_data_val));
1556
+ context_data_val = 0;
1557
+ } else {
1558
+ context_data_position++;
1559
+ }
1560
+ value = value >> 1;
1561
+ }
1562
+ }
1563
+ context_enlargeIn--;
1564
+ if (context_enlargeIn == 0) {
1565
+ context_enlargeIn = Math.pow(2, context_numBits);
1566
+ context_numBits++;
1567
+ }
1568
+ context_dictionary[context_wc] = context_dictSize++;
1569
+ context_w = String(context_c);
1570
+ }
1571
+ }
1572
+ if (context_w !== "") {
1573
+ if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
1574
+ if (context_w.charCodeAt(0) < 256) {
1575
+ for (i = 0; i < context_numBits; i++) {
1576
+ context_data_val = context_data_val << 1;
1577
+ if (context_data_position == bitsPerChar - 1) {
1578
+ context_data_position = 0;
1579
+ context_data.push(getCharFromInt(context_data_val));
1580
+ context_data_val = 0;
1581
+ } else {
1582
+ context_data_position++;
1583
+ }
1584
+ }
1585
+ value = context_w.charCodeAt(0);
1586
+ for (i = 0; i < 8; i++) {
1587
+ context_data_val = context_data_val << 1 | value & 1;
1588
+ if (context_data_position == bitsPerChar - 1) {
1589
+ context_data_position = 0;
1590
+ context_data.push(getCharFromInt(context_data_val));
1591
+ context_data_val = 0;
1592
+ } else {
1593
+ context_data_position++;
1594
+ }
1595
+ value = value >> 1;
1596
+ }
1597
+ } else {
1598
+ value = 1;
1599
+ for (i = 0; i < context_numBits; i++) {
1600
+ context_data_val = context_data_val << 1 | value;
1601
+ if (context_data_position == bitsPerChar - 1) {
1602
+ context_data_position = 0;
1603
+ context_data.push(getCharFromInt(context_data_val));
1604
+ context_data_val = 0;
1605
+ } else {
1606
+ context_data_position++;
1607
+ }
1608
+ value = 0;
1609
+ }
1610
+ value = context_w.charCodeAt(0);
1611
+ for (i = 0; i < 16; i++) {
1612
+ context_data_val = context_data_val << 1 | value & 1;
1613
+ if (context_data_position == bitsPerChar - 1) {
1614
+ context_data_position = 0;
1615
+ context_data.push(getCharFromInt(context_data_val));
1616
+ context_data_val = 0;
1617
+ } else {
1618
+ context_data_position++;
1619
+ }
1620
+ value = value >> 1;
1621
+ }
1622
+ }
1623
+ context_enlargeIn--;
1624
+ if (context_enlargeIn == 0) {
1625
+ context_enlargeIn = Math.pow(2, context_numBits);
1626
+ context_numBits++;
1627
+ }
1628
+ delete context_dictionaryToCreate[context_w];
1629
+ } else {
1630
+ value = context_dictionary[context_w];
1631
+ for (i = 0; i < context_numBits; i++) {
1632
+ context_data_val = context_data_val << 1 | value & 1;
1633
+ if (context_data_position == bitsPerChar - 1) {
1634
+ context_data_position = 0;
1635
+ context_data.push(getCharFromInt(context_data_val));
1636
+ context_data_val = 0;
1637
+ } else {
1638
+ context_data_position++;
1639
+ }
1640
+ value = value >> 1;
1641
+ }
1642
+ }
1643
+ context_enlargeIn--;
1644
+ if (context_enlargeIn == 0) {
1645
+ context_enlargeIn = Math.pow(2, context_numBits);
1646
+ context_numBits++;
1647
+ }
1648
+ }
1649
+ value = 2;
1650
+ for (i = 0; i < context_numBits; i++) {
1651
+ context_data_val = context_data_val << 1 | value & 1;
1652
+ if (context_data_position == bitsPerChar - 1) {
1653
+ context_data_position = 0;
1654
+ context_data.push(getCharFromInt(context_data_val));
1655
+ context_data_val = 0;
1656
+ } else {
1657
+ context_data_position++;
1658
+ }
1659
+ value = value >> 1;
1660
+ }
1661
+ while (true) {
1662
+ context_data_val = context_data_val << 1;
1663
+ if (context_data_position == bitsPerChar - 1) {
1664
+ context_data.push(getCharFromInt(context_data_val));
1665
+ break;
1666
+ } else context_data_position++;
1667
+ }
1668
+ return context_data.join("");
1669
+ },
1670
+ decompress: function(compressed) {
1671
+ if (compressed == null) return "";
1672
+ if (compressed == "") return null;
1673
+ return LZString3._decompress(compressed.length, 32768, function(index) {
1674
+ return compressed.charCodeAt(index);
1675
+ });
1676
+ },
1677
+ _decompress: function(length, resetValue, getNextValue) {
1678
+ var dictionary = [], enlargeIn = 4, dictSize = 4, numBits = 3, entry = "", result = [], i, w, bits, resb, maxpower, power, c, data = { val: getNextValue(0), position: resetValue, index: 1 };
1679
+ for (i = 0; i < 3; i += 1) {
1680
+ dictionary[i] = i;
1681
+ }
1682
+ bits = 0;
1683
+ maxpower = Math.pow(2, 2);
1684
+ power = 1;
1685
+ while (power != maxpower) {
1686
+ resb = data.val & data.position;
1687
+ data.position >>= 1;
1688
+ if (data.position == 0) {
1689
+ data.position = resetValue;
1690
+ data.val = getNextValue(data.index++);
1691
+ }
1692
+ bits |= (resb > 0 ? 1 : 0) * power;
1693
+ power <<= 1;
1694
+ }
1695
+ switch (bits) {
1696
+ case 0:
1697
+ bits = 0;
1698
+ maxpower = Math.pow(2, 8);
1699
+ power = 1;
1700
+ while (power != maxpower) {
1701
+ resb = data.val & data.position;
1702
+ data.position >>= 1;
1703
+ if (data.position == 0) {
1704
+ data.position = resetValue;
1705
+ data.val = getNextValue(data.index++);
1706
+ }
1707
+ bits |= (resb > 0 ? 1 : 0) * power;
1708
+ power <<= 1;
1709
+ }
1710
+ c = f(bits);
1711
+ break;
1712
+ case 1:
1713
+ bits = 0;
1714
+ maxpower = Math.pow(2, 16);
1715
+ power = 1;
1716
+ while (power != maxpower) {
1717
+ resb = data.val & data.position;
1718
+ data.position >>= 1;
1719
+ if (data.position == 0) {
1720
+ data.position = resetValue;
1721
+ data.val = getNextValue(data.index++);
1722
+ }
1723
+ bits |= (resb > 0 ? 1 : 0) * power;
1724
+ power <<= 1;
1725
+ }
1726
+ c = f(bits);
1727
+ break;
1728
+ case 2:
1729
+ return "";
1730
+ }
1731
+ dictionary[3] = c;
1732
+ w = c;
1733
+ result.push(c);
1734
+ while (true) {
1735
+ if (data.index > length) {
1736
+ return "";
1737
+ }
1738
+ bits = 0;
1739
+ maxpower = Math.pow(2, numBits);
1740
+ power = 1;
1741
+ while (power != maxpower) {
1742
+ resb = data.val & data.position;
1743
+ data.position >>= 1;
1744
+ if (data.position == 0) {
1745
+ data.position = resetValue;
1746
+ data.val = getNextValue(data.index++);
1747
+ }
1748
+ bits |= (resb > 0 ? 1 : 0) * power;
1749
+ power <<= 1;
1750
+ }
1751
+ switch (c = bits) {
1752
+ case 0:
1753
+ bits = 0;
1754
+ maxpower = Math.pow(2, 8);
1755
+ power = 1;
1756
+ while (power != maxpower) {
1757
+ resb = data.val & data.position;
1758
+ data.position >>= 1;
1759
+ if (data.position == 0) {
1760
+ data.position = resetValue;
1761
+ data.val = getNextValue(data.index++);
1762
+ }
1763
+ bits |= (resb > 0 ? 1 : 0) * power;
1764
+ power <<= 1;
1765
+ }
1766
+ dictionary[dictSize++] = f(bits);
1767
+ c = dictSize - 1;
1768
+ enlargeIn--;
1769
+ break;
1770
+ case 1:
1771
+ bits = 0;
1772
+ maxpower = Math.pow(2, 16);
1773
+ power = 1;
1774
+ while (power != maxpower) {
1775
+ resb = data.val & data.position;
1776
+ data.position >>= 1;
1777
+ if (data.position == 0) {
1778
+ data.position = resetValue;
1779
+ data.val = getNextValue(data.index++);
1780
+ }
1781
+ bits |= (resb > 0 ? 1 : 0) * power;
1782
+ power <<= 1;
1783
+ }
1784
+ dictionary[dictSize++] = f(bits);
1785
+ c = dictSize - 1;
1786
+ enlargeIn--;
1787
+ break;
1788
+ case 2:
1789
+ return result.join("");
1790
+ }
1791
+ if (enlargeIn == 0) {
1792
+ enlargeIn = Math.pow(2, numBits);
1793
+ numBits++;
1794
+ }
1795
+ if (dictionary[c]) {
1796
+ entry = dictionary[c];
1797
+ } else {
1798
+ if (c === dictSize) {
1799
+ entry = w + w.charAt(0);
1800
+ } else {
1801
+ return null;
1802
+ }
1803
+ }
1804
+ result.push(entry);
1805
+ dictionary[dictSize++] = w + entry.charAt(0);
1806
+ enlargeIn--;
1807
+ w = entry;
1808
+ if (enlargeIn == 0) {
1809
+ enlargeIn = Math.pow(2, numBits);
1810
+ numBits++;
1811
+ }
1812
+ }
1813
+ }
1814
+ };
1815
+ return LZString3;
1816
+ })();
1817
+ if (module != null) {
1818
+ module.exports = LZString2;
1819
+ } else if (typeof angular !== "undefined" && angular != null) {
1820
+ angular.module("LZString", []).factory("LZString", function() {
1821
+ return LZString2;
1822
+ });
1823
+ }
1824
+ })(lzString);
1825
+ return lzString.exports;
1826
+ }
1827
+ var lzStringExports = requireLzString();
1828
+ const LZString = /* @__PURE__ */ getDefaultExportFromCjs(lzStringExports);
1829
+ class LocalStorageWrapper {
1830
+ constructor(prefix = "") {
1831
+ __publicField(this, "prefix");
1832
+ __publicField(this, "COMPRESSION_THRESHOLD", 1e4);
1833
+ __publicField(this, "COMPRESSION_MARKER", "__COMPRESSED__");
1834
+ __publicField(this, "cache", {});
1835
+ this.prefix = prefix;
1836
+ }
1837
+ getPageKey(targetLang) {
1838
+ return `${this.prefix}${targetLang}`;
1839
+ }
1840
+ shouldCompress(value) {
1841
+ return value.length > this.COMPRESSION_THRESHOLD;
1842
+ }
1843
+ compress(value) {
1844
+ try {
1845
+ return LZString.compressToBase64(value);
1846
+ } catch (error) {
1847
+ console.error("Compression failed:", error);
1848
+ return value;
1849
+ }
1850
+ }
1851
+ decompress(value) {
1852
+ try {
1853
+ return LZString.decompressFromBase64(value) || value;
1854
+ } catch (error) {
1855
+ console.error("Decompression failed:", error);
1856
+ return value;
1857
+ }
1858
+ }
1859
+ getItem(key) {
1860
+ if (this.cache[key]) {
1861
+ return this.cache[key];
1862
+ }
1863
+ const item = localStorage.getItem(key);
1864
+ if (!item) return null;
1865
+ try {
1866
+ const decompressed = item.startsWith(this.COMPRESSION_MARKER) ? this.decompress(item.slice(this.COMPRESSION_MARKER.length)) : item;
1867
+ const parsed = JSON.parse(decompressed);
1868
+ this.cache[key] = parsed;
1869
+ return parsed;
1870
+ } catch (e) {
1871
+ console.error("Error parsing cached item:", e);
1872
+ return null;
1873
+ }
1874
+ }
1875
+ setItem(key, value) {
1876
+ const stringified = JSON.stringify(value);
1877
+ const storeValue = () => {
1878
+ try {
1879
+ const finalValue = this.shouldCompress(stringified) ? `${this.COMPRESSION_MARKER}${this.compress(stringified)}` : stringified;
1880
+ localStorage.setItem(key, finalValue);
1881
+ this.cache[key] = value;
1882
+ } catch (error) {
1883
+ console.error("Error storing item:", error);
1884
+ localStorage.setItem(key, stringified);
1885
+ this.cache[key] = value;
1886
+ }
1887
+ };
1888
+ if (typeof requestIdleCallback !== "undefined") {
1889
+ requestIdleCallback(() => storeValue());
1890
+ } else {
1891
+ setTimeout(storeValue, 0);
1892
+ }
1893
+ }
1894
+ // Get translation for a node from the page cache (object of originalText)
1895
+ getNodeTranslation(originalText, targetLang) {
1896
+ const pageKey = this.getPageKey(targetLang);
1897
+ const translations = this.getItem(pageKey) || {};
1898
+ return translations[originalText] || null;
1899
+ }
1900
+ // Store translation for a node in the page cache (object of originalText)
1901
+ setNodeTranslation(originalText, targetLang, translatedText) {
1902
+ const pageKey = this.getPageKey(targetLang);
1903
+ let translations = this.getItem(pageKey) || {};
1904
+ translations[originalText] = translatedText;
1905
+ this.setItem(pageKey, translations);
1906
+ }
1907
+ setBatchNodeTranslationsArray(targetLang, batch) {
1908
+ const pageKey = this.getPageKey(targetLang);
1909
+ const existing = this.getItem(pageKey) || {};
1910
+ batch.forEach(({ originalText, translatedText }) => {
1911
+ existing[originalText] = translatedText;
1912
+ });
1913
+ this.setItem(pageKey, existing);
1914
+ }
1915
+ removeItem(key) {
1916
+ localStorage.removeItem(key);
1917
+ delete this.cache[key];
1918
+ }
1919
+ clear(lang_code = []) {
1920
+ if (this.prefix) {
1921
+ for (let key in localStorage) {
1922
+ if (key.startsWith(this.prefix) && (!lang_code.length || lang_code.includes(key.split("--")[1]))) {
1923
+ localStorage.removeItem(key);
1924
+ }
1925
+ }
1926
+ } else if (lang_code && lang_code.length > 0) {
1927
+ for (let key in localStorage) {
1928
+ if (lang_code.includes(key.split("--")[1])) {
1929
+ localStorage.removeItem(key);
1930
+ delete this.cache[key];
1931
+ }
1932
+ }
1933
+ } else {
1934
+ localStorage.clear();
1935
+ this.cache = {};
1936
+ }
1937
+ }
1938
+ key(index) {
1939
+ return localStorage.key(index);
1940
+ }
1941
+ get length() {
1942
+ return localStorage.length;
1943
+ }
1944
+ }
1945
+ const _TranslationWidget = class _TranslationWidget {
1946
+ /**
1947
+ * @param publicKey - The public api key for the translation widget
1948
+ * @param config - The configuration for the translation widget
1949
+ */
1950
+ constructor(publicKey, config = {}) {
1951
+ __publicField(this, "config");
1952
+ __publicField(this, "translationService");
1953
+ __publicField(this, "targetLanguages");
1954
+ __publicField(this, "currentLanguage");
1955
+ __publicField(this, "widget");
1956
+ __publicField(this, "elements");
1957
+ __publicField(this, "autoDetectLanguage");
1958
+ __publicField(this, "isTranslated", false);
1959
+ __publicField(this, "userLanguage");
1960
+ __publicField(this, "isTranslating", false);
1961
+ __publicField(this, "observer", null);
1962
+ __publicField(this, "translationScheduled", false);
1963
+ __publicField(this, "scheduleTimeout", null);
1964
+ __publicField(this, "showUI", true);
1965
+ __publicField(this, "lastTranslated", null);
1966
+ __publicField(this, "currentTranslationPromise", null);
1967
+ __publicField(this, "lastRequestedLanguage", null);
1968
+ __publicField(this, "translationRequestId", 0);
1969
+ /**
1970
+ * Handles the URL change
1971
+ */
1972
+ __publicField(this, "onUrlChange", () => {
1973
+ this.scheduleTranslation();
1974
+ });
1975
+ const allowedPositions = ["top-right", "top-left", "bottom-left", "bottom-right"];
1976
+ let safeConfig = {
1977
+ ...DEFAULT_CONFIG,
1978
+ ...config,
1979
+ targetLanguages: config.targetLanguages ?? [...DEFAULT_CONFIG.targetLanguages],
1980
+ requestHeaders: {
1981
+ ...DEFAULT_CONFIG.requestHeaders,
1982
+ ...config.requestHeaders ?? {}
1983
+ }
1984
+ };
1985
+ const servicePublicKey = safeConfig.mockMode ? publicKey || "pk_demo_frontend_only" : publicKey;
1986
+ if (safeConfig.position && !allowedPositions.includes(safeConfig.position)) {
1987
+ console.warn(`Invalid position '${safeConfig.position}' passed to TranslationWidget. Falling back to 'top-right'.`);
1988
+ safeConfig.position = "top-right";
1989
+ }
1990
+ this.config = safeConfig;
1991
+ this.targetLanguages = this.resolveTargetLanguages(this.config.targetLanguages);
1992
+ this.autoDetectLanguage = this.config.autoDetectLanguage || false;
1993
+ this.currentLanguage = this.config.pageLanguage;
1994
+ this.userLanguage = getUserLanguage();
1995
+ this.widget = document.createElement("div");
1996
+ this.showUI = this.config.showUI ?? true;
1997
+ this.elements = {
1998
+ trigger: null,
1999
+ dropdown: null,
2000
+ searchInput: null,
2001
+ clearSearch: null,
2002
+ languageItems: null,
2003
+ loadingIndicator: null
2004
+ };
2005
+ if (!this.config.mockMode && this.config.includeApiKeyHeader) {
2006
+ const apiValidationResult = validatePublicApiKey(servicePublicKey);
2007
+ if (!apiValidationResult.isValid) {
2008
+ console.error("Error initializing TranslationWidget: ", apiValidationResult.message);
2009
+ return;
2010
+ }
2011
+ }
2012
+ this.translationService = new TranslationService(servicePublicKey, {
2013
+ mockMode: this.config.mockMode,
2014
+ apiUrl: this.config.apiUrl,
2015
+ requestHeaders: this.config.requestHeaders,
2016
+ includeApiKeyHeader: this.config.includeApiKeyHeader
2017
+ });
2018
+ this.initialize();
2019
+ _TranslationWidget.instance = this;
2020
+ }
2021
+ /**
2022
+ * Initializes the translation widget
2023
+ */
2024
+ initialize() {
2025
+ var _a;
2026
+ const urlLang = this.getUrlParameter(LANG_PARAM);
2027
+ let initialLang = this.config.pageLanguage;
2028
+ if (this.isTargetLanguage(urlLang)) {
2029
+ initialLang = urlLang;
2030
+ } else {
2031
+ const storedLanguage = localStorage.getItem(LOCALSTORAGE_KEYS.PREFERRED_LANGUAGE);
2032
+ if (this.isTargetLanguage(storedLanguage)) {
2033
+ initialLang = storedLanguage;
2034
+ }
2035
+ }
2036
+ if (initialLang === this.config.pageLanguage && this.autoDetectLanguage && this.isTargetLanguage(this.userLanguage)) {
2037
+ initialLang = this.userLanguage;
2038
+ } else if (!this.config.pageLanguage) {
2039
+ const htmlTag = document.querySelector("html");
2040
+ if (htmlTag && htmlTag.getAttribute(LANG_PARAM)) {
2041
+ initialLang = htmlTag.getAttribute(LANG_PARAM);
2042
+ } else {
2043
+ initialLang = "en";
2044
+ }
2045
+ }
2046
+ this.currentLanguage = initialLang;
2047
+ if (this.showUI) {
2048
+ this.createWidget();
2049
+ const triggerIcon = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-icon");
2050
+ if (triggerIcon && this.currentLanguage !== this.config.pageLanguage) {
2051
+ const langObj = this.findLanguageByCode(this.currentLanguage);
2052
+ const langName = langObj ? langObj.name : this.currentLanguage.toUpperCase();
2053
+ triggerIcon.innerHTML = `<span class="jigts-lang-code">${this.currentLanguage.toUpperCase()}</span><span class="jigts-lang-name">${langName}</span>`;
2054
+ }
2055
+ this.setupEventListeners();
2056
+ this.setupURLObserver();
2057
+ this.setupContentObserver();
2058
+ }
2059
+ if (this.currentLanguage !== this.config.pageLanguage) {
2060
+ this.translatePage(this.currentLanguage).catch((error) => {
2061
+ console.error("Initial translation error:", error);
2062
+ });
2063
+ }
2064
+ }
2065
+ resolveTargetLanguages(languageCodes) {
2066
+ const resolvedLanguages = languageCodes.map((code) => this.findLanguageByCode(code)).filter((language) => Boolean(language));
2067
+ if (resolvedLanguages.length > 0) {
2068
+ return resolvedLanguages;
2069
+ }
2070
+ return languages.filter((language) => language.code === "zh");
2071
+ }
2072
+ findLanguageByCode(code) {
2073
+ return languages.find((language) => language.code === code);
2074
+ }
2075
+ isTargetLanguage(code) {
2076
+ return Boolean(code) && this.targetLanguages.some((language) => language.code === code);
2077
+ }
2078
+ /**
2079
+ * Gets the value of a URL parameter
2080
+ * @param name - The name of the URL parameter
2081
+ * @returns The value of the URL parameter
2082
+ */
2083
+ getUrlParameter(name) {
2084
+ const urlParams = new URLSearchParams(window.location.search);
2085
+ return urlParams.get(name);
2086
+ }
2087
+ /**
2088
+ * Sets up the content observer
2089
+ */
2090
+ setupContentObserver() {
2091
+ if (!this.observer) {
2092
+ this.observer = new MutationObserver((mutations) => {
2093
+ if (this.isTranslating) return;
2094
+ const widgetContainer = this.widget;
2095
+ const relevantMutations = mutations.filter((mutation) => {
2096
+ if (widgetContainer.contains(mutation.target)) return false;
2097
+ if (mutation.type === "childList") {
2098
+ const addedScripts = Array.from(mutation.addedNodes).some((node) => node.nodeName === "SCRIPT");
2099
+ const removedScripts = Array.from(mutation.removedNodes).some((node) => node.nodeName === "SCRIPT");
2100
+ return !addedScripts && !removedScripts;
2101
+ }
2102
+ return true;
2103
+ });
2104
+ if (relevantMutations.length > 0) {
2105
+ this.scheduleTranslation();
2106
+ }
2107
+ });
2108
+ this.observeBody();
2109
+ } else {
2110
+ console.warn("Observer already exists. Skipping setupContentObserver");
2111
+ }
2112
+ }
2113
+ /**
2114
+ * Observes the body for changes
2115
+ */
2116
+ observeBody() {
2117
+ var _a;
2118
+ (_a = this.observer) == null ? void 0 : _a.observe(document.body, {
2119
+ childList: true,
2120
+ subtree: true,
2121
+ characterData: true,
2122
+ // this is needed to detect changes for "mobile-only" elements
2123
+ attributes: true
2124
+ });
2125
+ }
2126
+ /**
2127
+ * Sets up the URL observer
2128
+ */
2129
+ setupURLObserver() {
2130
+ const historyMethods = ["pushState", "replaceState"];
2131
+ historyMethods.forEach((method) => {
2132
+ const original = history[method];
2133
+ history[method] = function(state, title, url) {
2134
+ const result = original.call(this, state, title, url);
2135
+ window.dispatchEvent(new Event(method));
2136
+ return result;
2137
+ };
2138
+ window.addEventListener(method, this.onUrlChange);
2139
+ });
2140
+ window.addEventListener("popstate", this.onUrlChange);
2141
+ }
2142
+ /**
2143
+ * Creates the widget
2144
+ */
2145
+ createWidget() {
2146
+ var _a;
2147
+ const currentLanguageLabel = this.getCurrentLanguageLabel();
2148
+ this.widget = document.createElement("div");
2149
+ this.widget.className = `jigts-translation-widget jigts-position-${this.config.position || "top-right"}`;
2150
+ if (this.config.theme) {
2151
+ if (this.config.theme.baseColor) {
2152
+ this.widget.style.setProperty("--jigts-custom-base-color", this.config.theme.baseColor);
2153
+ }
2154
+ if (this.config.theme.textColor) {
2155
+ this.widget.style.setProperty("--jigts-custom-text-color", this.config.theme.textColor);
2156
+ }
2157
+ }
2158
+ document.body.appendChild(this.widget);
2159
+ this.widget.innerHTML = this.createWidgetHTML(currentLanguageLabel);
2160
+ this.elements = {
2161
+ trigger: this.widget.querySelector(".jigts-widget-trigger"),
2162
+ dropdown: this.widget.querySelector(".jigts-widget-dropdown"),
2163
+ searchInput: this.widget.querySelector(".jigts-search-input"),
2164
+ clearSearch: this.widget.querySelector(".jigts-clear-search"),
2165
+ languageItems: this.widget.querySelectorAll(".jigts-language-item"),
2166
+ loadingIndicator: this.widget.querySelector(".jigts-loading-spinner")
2167
+ };
2168
+ const triggerSpan = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector("span");
2169
+ if (triggerSpan) {
2170
+ triggerSpan.classList.add("jigts-fade-in");
2171
+ }
2172
+ }
2173
+ /**
2174
+ * Gets the current language label
2175
+ * @returns The current language label
2176
+ */
2177
+ getCurrentLanguageLabel() {
2178
+ var _a;
2179
+ return ((_a = this.findLanguageByCode(this.currentLanguage)) == null ? void 0 : _a.native) || "English";
2180
+ }
2181
+ /**
2182
+ * Creates the widget HTML
2183
+ * @param currentLanguageLabel - The current language label
2184
+ * @returns The widget HTML
2185
+ */
2186
+ createWidgetHTML(currentLanguageLabel) {
2187
+ const languageOptions = this.createLanguageOptions();
2188
+ const languageCount = this.targetLanguages.length;
2189
+ const languageCountLabel = `${languageCount} ${languageCount === 1 ? "language" : "languages"}`;
2190
+ const searchSection = languageCount > 1 ? `
2191
+ <!-- Search Input -->
2192
+ <div class="jigts-search-container">
2193
+ <svg class="jigts-search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2194
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2195
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
2196
+ </svg>
2197
+ <input type="text" class="jigts-search-input" placeholder="Search languages..." aria-label="Search languages">
2198
+ <svg class="jigts-clear-search" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2199
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
2200
+ </path>
2201
+ </svg>
2202
+ </div>
2203
+ ` : "";
2204
+ return widgetTemplate.replace("{{languageOptions}}", languageOptions).replace("{{currentLanguageLabel}}", currentLanguageLabel).replace("{{languageCountLabel}}", languageCountLabel).replace("{{searchSection}}", searchSection);
2205
+ }
2206
+ /**
2207
+ * Creates the language options
2208
+ * @returns The language options
2209
+ */
2210
+ createLanguageOptions() {
2211
+ const sortedLanguages = [...this.targetLanguages].sort((a, b) => a.native.localeCompare(b.native));
2212
+ const createLanguageItem = (lang, isSelected = false) => `
2213
+ <div class="jigts-language-item ${isSelected ? "jigts-selected" : ""}" tabindex="0" role="option" aria-selected="${isSelected}" data-language-code="${lang.code}">
2214
+ <div class="jigts-language-info">
2215
+ <div class="jigts-language-main">
2216
+ <span class="jigts-language-name">${lang.name}</span>
2217
+ <div class="jigts-language-code">${lang.code}</div>
2218
+ </div>
2219
+ <div class="jigts-language-details">
2220
+ <span class="jigts-language-native">${lang.native}</span>
2221
+ </div>
2222
+ </div>
2223
+ <svg class="jigts-check-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2224
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
2225
+ </svg>
2226
+ </div>
2227
+ `;
2228
+ return sortedLanguages.map((lang) => createLanguageItem(lang, lang.code === this.currentLanguage)).join("");
2229
+ }
2230
+ /**
2231
+ * Updates the trigger text
2232
+ * @param newText - The new text
2233
+ */
2234
+ async updateTriggerText(newText) {
2235
+ var _a;
2236
+ const triggerSpan = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector("span");
2237
+ if (!triggerSpan) return;
2238
+ triggerSpan.offsetHeight;
2239
+ triggerSpan.classList.remove("jigts-fade-in");
2240
+ triggerSpan.classList.add("jigts-fade-out");
2241
+ await new Promise((resolve) => setTimeout(resolve, 300));
2242
+ triggerSpan.textContent = newText;
2243
+ triggerSpan.offsetHeight;
2244
+ triggerSpan.classList.remove("jigts-fade-out");
2245
+ triggerSpan.classList.add("jigts-fade-in");
2246
+ }
2247
+ /**
2248
+ * Gets the text to translate
2249
+ * @param node - The node to translate
2250
+ * @param parent - The parent element
2251
+ * @param targetLang - The target language
2252
+ * @returns The text to translate
2253
+ */
2254
+ getTextToTranslate(node, parent, targetLang) {
2255
+ var _a, _b;
2256
+ if (!parent.hasAttribute(ATTRIBUTES.ORIGINAL_TEXT)) {
2257
+ const originalText = (_a = node.text) == null ? void 0 : _a.trim();
2258
+ if (originalText) {
2259
+ parent.setAttribute(ATTRIBUTES.TRANSLATED_LANG, targetLang);
2260
+ parent.setAttribute(ATTRIBUTES.ORIGINAL_TEXT, originalText);
2261
+ if (this.config.adjustFontSize && !parent.hasAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE)) {
2262
+ const computedStyle = window.getComputedStyle(parent);
2263
+ parent.setAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE, computedStyle.fontSize);
2264
+ }
2265
+ return originalText;
2266
+ }
2267
+ } else {
2268
+ const textToTranslate = (_b = node.text) == null ? void 0 : _b.trim();
2269
+ if (this.currentLanguage !== targetLang) {
2270
+ parent.setAttribute(ATTRIBUTES.TRANSLATED_LANG, targetLang);
2271
+ let originalText = parent.getAttribute(ATTRIBUTES.ORIGINAL_TEXT);
2272
+ if (originalText) {
2273
+ return originalText;
2274
+ }
2275
+ }
2276
+ return textToTranslate || null;
2277
+ }
2278
+ return null;
2279
+ }
2280
+ /**
2281
+ * Calculates the font size
2282
+ * @param text - The text to calculate the font size for
2283
+ * @param originalFontSize - The original font size
2284
+ * @param originalText - The original text
2285
+ * @returns The font size
2286
+ */
2287
+ calculateFontSize(text, originalFontSize, originalText) {
2288
+ const minFontSize = 12;
2289
+ const maxFontSize = parseInt(originalFontSize);
2290
+ const textLength = text.length;
2291
+ const originalLength = originalText.length;
2292
+ if (textLength <= originalLength) {
2293
+ return originalFontSize;
2294
+ }
2295
+ const ratio = originalLength / textLength;
2296
+ const fontSize = Math.max(minFontSize, Math.round(maxFontSize * ratio));
2297
+ return `${fontSize}px`;
2298
+ }
2299
+ /**
2300
+ * Updates the loading state
2301
+ * @param isLoading - Whether the widget is loading
2302
+ */
2303
+ updateLoadingState(isLoading) {
2304
+ var _a, _b;
2305
+ const triggerContent = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-content");
2306
+ const triggerLoading = (_b = this.elements.trigger) == null ? void 0 : _b.querySelector(".jigts-trigger-loading");
2307
+ if (triggerContent && triggerLoading) {
2308
+ if (isLoading) {
2309
+ triggerContent.style.display = "none";
2310
+ triggerLoading.style.display = "flex";
2311
+ } else {
2312
+ triggerLoading.style.display = "none";
2313
+ triggerContent.style.display = "flex";
2314
+ }
2315
+ }
2316
+ }
2317
+ /**
2318
+ * Main function to translate the page
2319
+ *
2320
+ * Handles the translation of the page for multiple languages
2321
+ *
2322
+ * 1. Increments the request ID for each new translation
2323
+ * 2. Waits for the current translation to complete
2324
+ * 3. If the target language is the default page language, resets the translations
2325
+ * 4. Creates a new promise for each translation and awaits it
2326
+ *
2327
+ * @param targetLang - The target language
2328
+ */
2329
+ async translatePage(targetLang) {
2330
+ const requestId = ++this.translationRequestId;
2331
+ this.lastRequestedLanguage = targetLang;
2332
+ this.updateLoadingState(true);
2333
+ if (this.currentTranslationPromise) {
2334
+ await this.currentTranslationPromise;
2335
+ }
2336
+ if (targetLang === this.config.pageLanguage) {
2337
+ this.resetTranslations();
2338
+ if (requestId === this.translationRequestId) {
2339
+ this.updateLoadingState(false);
2340
+ }
2341
+ return;
2342
+ }
2343
+ this.currentTranslationPromise = this._translatePage(targetLang);
2344
+ try {
2345
+ await this.currentTranslationPromise;
2346
+ } catch (error) {
2347
+ console.error("Translation failed:", error);
2348
+ this.resetToDefaultLanguage();
2349
+ } finally {
2350
+ if (requestId === this.translationRequestId) {
2351
+ this.currentTranslationPromise = null;
2352
+ this.updateLoadingState(false);
2353
+ }
2354
+ }
2355
+ }
2356
+ /**
2357
+ * Reset to default Page language
2358
+ * @returns
2359
+ */
2360
+ resetToDefaultLanguage() {
2361
+ var _a;
2362
+ if (this.isTranslating) {
2363
+ return;
2364
+ }
2365
+ this.resetTranslations();
2366
+ this.lastRequestedLanguage = this.config.pageLanguage;
2367
+ this.currentLanguage = this.config.pageLanguage;
2368
+ const languageItems = this.widget.querySelectorAll(".jigts-language-item");
2369
+ languageItems.forEach((item) => {
2370
+ const isSelected = item.getAttribute("data-language-code") === this.config.pageLanguage;
2371
+ item.classList.toggle("jigts-selected", isSelected);
2372
+ item.setAttribute("aria-selected", isSelected.toString());
2373
+ });
2374
+ localStorage.setItem(LOCALSTORAGE_KEYS.PREFERRED_LANGUAGE, this.config.pageLanguage);
2375
+ const triggerIcon = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-icon");
2376
+ if (triggerIcon) {
2377
+ triggerIcon.innerHTML = this.getLanguageSVG();
2378
+ }
2379
+ this.isTranslated = false;
2380
+ this.updateResetButtonVisibility();
2381
+ }
2382
+ async _translatePage(targetLang) {
2383
+ var _a;
2384
+ this.isTranslating = true;
2385
+ (_a = this.observer) == null ? void 0 : _a.disconnect();
2386
+ try {
2387
+ const nodes = DocumentNavigator.findTranslatableContent();
2388
+ const visibleNodes = nodes.filter((node) => {
2389
+ const rect = node.element.getBoundingClientRect();
2390
+ return rect.height > 0 && rect.top < window.innerHeight;
2391
+ });
2392
+ const nonVisibleNodes = nodes.filter((node) => {
2393
+ const rect = node.element.getBoundingClientRect();
2394
+ return rect.height === 0 || rect.top > window.innerHeight;
2395
+ });
2396
+ const visibleBatches = DocumentNavigator.divideIntoGroups(visibleNodes, BATCH_SIZE);
2397
+ const nonVisibleBatches = DocumentNavigator.divideIntoGroups(nonVisibleNodes, BATCH_SIZE);
2398
+ const cache = new LocalStorageWrapper(CACHE_PREFIX);
2399
+ const processBatches = async (batches) => {
2400
+ const allBatchNodes = [];
2401
+ const allBatchTexts = [];
2402
+ batches.forEach((batch) => {
2403
+ const textsToTranslate = [];
2404
+ const batchNodes = [];
2405
+ batch.forEach((node) => {
2406
+ const parent = node.element;
2407
+ if (!parent) return;
2408
+ const translatedLang = parent.getAttribute(ATTRIBUTES.TRANSLATED_LANG);
2409
+ if (parent.hasAttribute(ATTRIBUTES.ORIGINAL_TEXT) && targetLang === translatedLang) {
2410
+ return;
2411
+ }
2412
+ let textToTranslate = this.getTextToTranslate(node, parent, targetLang);
2413
+ textToTranslate = removeEmojis(textToTranslate || "");
2414
+ if (textToTranslate.length === 0 || textToTranslate.length === 1) {
2415
+ return;
2416
+ }
2417
+ if (textToTranslate) {
2418
+ const cacheObject = cache.getItem(cache.getPageKey(targetLang)) || {};
2419
+ const cachedTranslation = cacheObject[textToTranslate] || null;
2420
+ if (cachedTranslation) {
2421
+ if (this.lastRequestedLanguage === targetLang) {
2422
+ const originalText = textToTranslate;
2423
+ const translatedText = cachedTranslation;
2424
+ if (this.config.adjustFontSize) {
2425
+ const originalFontSize = parent.getAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE) || "16px";
2426
+ const newFontSize = this.calculateFontSize(translatedText, originalFontSize, originalText);
2427
+ parent.style.fontSize = newFontSize;
2428
+ }
2429
+ this.setTranslatedContent(parent, translatedText);
2430
+ }
2431
+ return;
2432
+ }
2433
+ textsToTranslate.push(textToTranslate.trim());
2434
+ batchNodes.push(node);
2435
+ }
2436
+ });
2437
+ if (textsToTranslate.length > 0) {
2438
+ allBatchNodes.push(batchNodes);
2439
+ allBatchTexts.push(textsToTranslate);
2440
+ }
2441
+ });
2442
+ if (allBatchTexts.length > 0) {
2443
+ const allTranslatedTexts = await Promise.all(allBatchTexts.map((texts) => {
2444
+ var _a2;
2445
+ return (_a2 = this.translationService) == null ? void 0 : _a2.translateBatchText(texts, targetLang);
2446
+ }));
2447
+ const successfulBatches = [];
2448
+ allTranslatedTexts.forEach((translations, batchIndex) => {
2449
+ if (translations && translations.length > 0) {
2450
+ successfulBatches.push({
2451
+ translations,
2452
+ nodes: allBatchNodes[batchIndex]
2453
+ });
2454
+ }
2455
+ });
2456
+ if (successfulBatches.length === 0) {
2457
+ this.updateLoadingState(false);
2458
+ this.isTranslating = false;
2459
+ this.resetToDefaultLanguage();
2460
+ return;
2461
+ }
2462
+ const batchArray = [];
2463
+ successfulBatches.forEach(({ translations, nodes: nodes2 }) => {
2464
+ nodes2.forEach((node, nodeIndex) => {
2465
+ const parent = node.element;
2466
+ if (parent && translations[nodeIndex]) {
2467
+ const originalText = node.text || "";
2468
+ const translatedText = translations[nodeIndex];
2469
+ const originalTextWithoutEmojis = removeEmojis(originalText);
2470
+ if (originalTextWithoutEmojis && translatedText) {
2471
+ batchArray.push({
2472
+ originalText: originalTextWithoutEmojis,
2473
+ translatedText
2474
+ });
2475
+ if (this.lastRequestedLanguage === targetLang) {
2476
+ if (this.config.adjustFontSize) {
2477
+ const originalFontSize = parent.getAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE) || "16px";
2478
+ const newFontSize = this.calculateFontSize(translatedText, originalFontSize, originalText);
2479
+ parent.style.fontSize = newFontSize;
2480
+ }
2481
+ this.setTranslatedContent(parent, translatedText);
2482
+ }
2483
+ }
2484
+ }
2485
+ });
2486
+ });
2487
+ if (batchArray.length > 0) {
2488
+ cache.setBatchNodeTranslationsArray(targetLang, batchArray);
2489
+ }
2490
+ }
2491
+ };
2492
+ await Promise.allSettled([
2493
+ processBatches(visibleBatches),
2494
+ processBatches(nonVisibleBatches)
2495
+ ]);
2496
+ if (this.lastRequestedLanguage === targetLang) {
2497
+ this.isTranslated = true;
2498
+ this.updateResetButtonVisibility();
2499
+ }
2500
+ } finally {
2501
+ this.isTranslating = false;
2502
+ this.observeBody();
2503
+ }
2504
+ }
2505
+ /**
2506
+ * Updates the visibility of the reset button
2507
+ */
2508
+ updateResetButtonVisibility() {
2509
+ const resetButton = this.widget.querySelector(".jigts-reset-option");
2510
+ if (resetButton) {
2511
+ resetButton.style.display = this.isTranslated ? "flex" : "none";
2512
+ }
2513
+ }
2514
+ /**
2515
+ * Resets the translations
2516
+ */
2517
+ resetTranslations() {
2518
+ if (this.observer) {
2519
+ this.observer.disconnect();
2520
+ }
2521
+ const elements = document.querySelectorAll(`[${ATTRIBUTES.ORIGINAL_TEXT}]`);
2522
+ elements.forEach((element) => {
2523
+ const textNodes = Array.from(element.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE);
2524
+ if (textNodes.length > 0) {
2525
+ const originalText = element.getAttribute(ATTRIBUTES.ORIGINAL_TEXT);
2526
+ if (originalText) {
2527
+ this.setTranslatedContent(element, originalText);
2528
+ }
2529
+ }
2530
+ const originalFontSize = element.getAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE);
2531
+ if (originalFontSize) {
2532
+ element.style.fontSize = originalFontSize;
2533
+ }
2534
+ element.removeAttribute(ATTRIBUTES.ORIGINAL_TEXT);
2535
+ element.removeAttribute(ATTRIBUTES.TRANSLATED_LANG);
2536
+ element.removeAttribute(ATTRIBUTES.ORIGINAL_FONT_SIZE);
2537
+ });
2538
+ this.isTranslated = false;
2539
+ this.currentLanguage = this.config.pageLanguage;
2540
+ const nodes = DocumentNavigator.findTranslatableContent();
2541
+ const hash = generateHashForContent(nodes);
2542
+ this.lastTranslated = {
2543
+ url: window.location.href,
2544
+ lang: this.config.pageLanguage,
2545
+ hash
2546
+ };
2547
+ this.updateResetButtonVisibility();
2548
+ this.observeBody();
2549
+ }
2550
+ /**
2551
+ * Adjusts the dropdown position
2552
+ */
2553
+ adjustDropdownPosition() {
2554
+ const { dropdown, trigger } = this.elements;
2555
+ if (!dropdown || !trigger) return;
2556
+ const triggerRect = trigger.getBoundingClientRect();
2557
+ const dropdownRect = dropdown.getBoundingClientRect();
2558
+ const viewportWidth = window.innerWidth;
2559
+ const viewportHeight = window.innerHeight;
2560
+ dropdown.style.top = "";
2561
+ dropdown.style.bottom = "";
2562
+ dropdown.style.left = "";
2563
+ dropdown.style.right = "";
2564
+ dropdown.style.transform = "";
2565
+ const spaceBelow = viewportHeight - triggerRect.bottom;
2566
+ const spaceAbove = triggerRect.top;
2567
+ const spaceRight = viewportWidth - triggerRect.right;
2568
+ const spaceLeft = triggerRect.left;
2569
+ if (spaceBelow < dropdownRect.height && spaceAbove > spaceBelow) {
2570
+ dropdown.style.bottom = "100%";
2571
+ dropdown.style.top = "auto";
2572
+ dropdown.style.marginBottom = "0.5rem";
2573
+ dropdown.style.marginTop = "0";
2574
+ } else {
2575
+ dropdown.style.top = "100%";
2576
+ dropdown.style.bottom = "auto";
2577
+ dropdown.style.marginTop = "0.5rem";
2578
+ dropdown.style.marginBottom = "0";
2579
+ }
2580
+ if (spaceRight < dropdownRect.width && spaceLeft > spaceRight) {
2581
+ dropdown.style.right = "0";
2582
+ dropdown.style.left = "auto";
2583
+ } else {
2584
+ dropdown.style.left = "0";
2585
+ dropdown.style.right = "auto";
2586
+ }
2587
+ const finalRect = dropdown.getBoundingClientRect();
2588
+ if (finalRect.right > viewportWidth) {
2589
+ dropdown.style.right = "0";
2590
+ dropdown.style.left = "auto";
2591
+ }
2592
+ if (finalRect.left < 0) {
2593
+ dropdown.style.left = "0";
2594
+ dropdown.style.right = "auto";
2595
+ }
2596
+ }
2597
+ /**
2598
+ * Sets up the event listeners
2599
+ */
2600
+ setupEventListeners() {
2601
+ const { trigger, dropdown, searchInput, clearSearch, languageItems } = this.elements;
2602
+ if (!trigger || !dropdown || !languageItems) {
2603
+ console.error("Failed to find required elements");
2604
+ return;
2605
+ }
2606
+ const resetButton = this.widget.querySelector(".jigts-reset-option");
2607
+ if (resetButton) {
2608
+ resetButton.addEventListener("click", () => {
2609
+ var _a;
2610
+ if (this.isTranslating) return;
2611
+ this.resetToDefaultLanguage();
2612
+ resetButton.classList.remove("jigts-active");
2613
+ this.isTranslated = false;
2614
+ this.updateResetButtonVisibility();
2615
+ const languageItems2 = this.widget.querySelectorAll(".jigts-language-item");
2616
+ languageItems2.forEach((item) => {
2617
+ const isSelected = item.getAttribute("data-language-code") === this.config.pageLanguage;
2618
+ item.classList.toggle("jigts-selected", isSelected);
2619
+ item.setAttribute("aria-selected", isSelected.toString());
2620
+ });
2621
+ const triggerIcon = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-icon");
2622
+ if (triggerIcon) {
2623
+ triggerIcon.innerHTML = this.getLanguageSVG();
2624
+ }
2625
+ dropdown.classList.remove("jigts-open");
2626
+ trigger.setAttribute("aria-expanded", "false");
2627
+ const triggerContent = trigger.querySelector(".jigts-trigger-content");
2628
+ if (triggerContent) triggerContent.classList.remove("jigts-has-translation");
2629
+ });
2630
+ }
2631
+ this.updateResetButtonVisibility();
2632
+ trigger.addEventListener("click", () => {
2633
+ dropdown.classList.toggle("jigts-open");
2634
+ const isOpen = dropdown.classList.contains("jigts-open");
2635
+ trigger.setAttribute("aria-expanded", isOpen.toString());
2636
+ if (isOpen && searchInput) {
2637
+ this.adjustDropdownPosition();
2638
+ searchInput.focus();
2639
+ }
2640
+ });
2641
+ window.addEventListener("resize", () => {
2642
+ if (dropdown.classList.contains("jigts-open")) {
2643
+ this.adjustDropdownPosition();
2644
+ }
2645
+ });
2646
+ document.addEventListener("click", (e) => {
2647
+ if (!e.target.closest(".jigts-translation-widget")) {
2648
+ if (dropdown.classList.contains("jigts-open")) {
2649
+ dropdown.classList.add("jigts-closing");
2650
+ setTimeout(() => {
2651
+ dropdown.classList.remove("jigts-open", "jigts-closing");
2652
+ trigger.setAttribute("aria-expanded", "false");
2653
+ }, 300);
2654
+ }
2655
+ }
2656
+ });
2657
+ if (searchInput && clearSearch) {
2658
+ searchInput.addEventListener("input", () => {
2659
+ const searchTerm = searchInput.value.toLowerCase();
2660
+ const hasValue = searchTerm.length > 0;
2661
+ clearSearch.classList.toggle("jigts-visible", hasValue);
2662
+ const items = this.widget.querySelectorAll(".jigts-language-item");
2663
+ const noResults = this.widget.querySelector(".jigts-no-results");
2664
+ let visibleCount = 0;
2665
+ items.forEach((item) => {
2666
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2667
+ const name = ((_b = (_a = item.querySelector(".jigts-language-name")) == null ? void 0 : _a.textContent) == null ? void 0 : _b.toLowerCase()) || "";
2668
+ const native = ((_d = (_c = item.querySelector(".jigts-language-native")) == null ? void 0 : _c.textContent) == null ? void 0 : _d.toLowerCase()) || "";
2669
+ const code = ((_f = (_e = item.querySelector(".jigts-language-code")) == null ? void 0 : _e.textContent) == null ? void 0 : _f.toLowerCase()) || "";
2670
+ const region = ((_h = (_g = item.querySelector(".jigts-language-region")) == null ? void 0 : _g.textContent) == null ? void 0 : _h.toLowerCase()) || "";
2671
+ const matches = name.includes(searchTerm) || native.includes(searchTerm) || code.includes(searchTerm) || region.includes(searchTerm);
2672
+ item.style.display = matches ? "" : "none";
2673
+ if (matches) visibleCount++;
2674
+ });
2675
+ if (noResults) {
2676
+ noResults.style.display = visibleCount === 0 ? "flex" : "none";
2677
+ }
2678
+ });
2679
+ clearSearch.addEventListener("click", () => {
2680
+ searchInput.value = "";
2681
+ clearSearch.classList.remove("jigts-visible");
2682
+ searchInput.focus();
2683
+ const items = this.widget.querySelectorAll(".jigts-language-item");
2684
+ const noResults = this.widget.querySelector(".jigts-no-results");
2685
+ items.forEach((item) => {
2686
+ item.style.display = "";
2687
+ });
2688
+ if (noResults) {
2689
+ noResults.style.display = "none";
2690
+ }
2691
+ });
2692
+ }
2693
+ languageItems.forEach((item) => {
2694
+ item.addEventListener("click", async () => {
2695
+ var _a, _b;
2696
+ languageItems.forEach((i) => {
2697
+ i.classList.remove("jigts-selected");
2698
+ i.setAttribute("aria-selected", "false");
2699
+ });
2700
+ item.classList.add("jigts-selected");
2701
+ item.setAttribute("aria-selected", "true");
2702
+ const langName = (_a = item.querySelector(".jigts-language-name")) == null ? void 0 : _a.textContent;
2703
+ const langCode = item.getAttribute("data-language-code");
2704
+ dropdown.classList.remove("jigts-open");
2705
+ trigger.setAttribute("aria-expanded", "false");
2706
+ if (langName) {
2707
+ await this.updateTriggerText(langName);
2708
+ }
2709
+ if (langCode) {
2710
+ localStorage.setItem(LOCALSTORAGE_KEYS.PREFERRED_LANGUAGE, langCode);
2711
+ }
2712
+ const triggerIcon = (_b = this.elements.trigger) == null ? void 0 : _b.querySelector(".jigts-trigger-icon");
2713
+ if (triggerIcon && langCode && langName) {
2714
+ triggerIcon.innerHTML = `<span class="jigts-lang-code">${langCode.toUpperCase()}</span><span class="jigts-lang-name">${langName}</span>`;
2715
+ }
2716
+ const triggerContent = trigger.querySelector(".jigts-trigger-content");
2717
+ if (langCode && langCode !== this.currentLanguage) {
2718
+ if (triggerContent) triggerContent.classList.add("jigts-has-translation");
2719
+ const triggerLoading = trigger.querySelector(".jigts-trigger-loading");
2720
+ if (triggerContent && triggerLoading) {
2721
+ triggerContent.style.display = "none";
2722
+ triggerLoading.style.display = "flex";
2723
+ }
2724
+ try {
2725
+ await this.translatePage(langCode);
2726
+ this.currentLanguage = langCode;
2727
+ } catch (error) {
2728
+ console.error("Translation error:", error);
2729
+ alert("An error occurred during translation. Please try again.");
2730
+ }
2731
+ } else if (triggerContent) {
2732
+ triggerContent.classList.remove("jigts-has-translation");
2733
+ }
2734
+ });
2735
+ });
2736
+ document.addEventListener("keydown", (e) => {
2737
+ if (!dropdown.classList.contains("jigts-open")) return;
2738
+ if (e.key === "Escape") {
2739
+ dropdown.classList.remove("jigts-open");
2740
+ trigger.setAttribute("aria-expanded", "false");
2741
+ trigger.focus();
2742
+ }
2743
+ });
2744
+ }
2745
+ scheduleTranslation() {
2746
+ if (this.translationScheduled) return;
2747
+ const currentUrl = window.location.href;
2748
+ const currentLang = this.currentLanguage;
2749
+ const nodes = DocumentNavigator.findTranslatableContent();
2750
+ const hash = generateHashForContent(nodes);
2751
+ if (this.lastTranslated && this.lastTranslated.url === currentUrl && this.lastTranslated.lang === currentLang && this.lastTranslated.hash === hash) {
2752
+ return;
2753
+ }
2754
+ this.translationScheduled = true;
2755
+ if (this.scheduleTimeout) clearTimeout(this.scheduleTimeout);
2756
+ this.scheduleTimeout = window.setTimeout(() => {
2757
+ var _a, _b;
2758
+ this.translationScheduled = false;
2759
+ if (this.currentLanguage !== this.config.pageLanguage) {
2760
+ this.lastTranslated = { url: currentUrl, lang: currentLang, hash };
2761
+ const triggerContent = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-content");
2762
+ const triggerLoading = (_b = this.elements.trigger) == null ? void 0 : _b.querySelector(".jigts-trigger-loading");
2763
+ if (triggerContent && triggerLoading) {
2764
+ triggerContent.style.display = "none";
2765
+ triggerLoading.style.display = "flex";
2766
+ }
2767
+ this.translatePage(this.currentLanguage).then(() => {
2768
+ const languageItems = this.widget.querySelectorAll(".jigts-language-item");
2769
+ languageItems.forEach((item) => {
2770
+ const isSelected = item.getAttribute("data-language-code") === this.currentLanguage;
2771
+ item.classList.toggle("jigts-selected", isSelected);
2772
+ item.setAttribute("aria-selected", isSelected.toString());
2773
+ });
2774
+ }).catch((error) => {
2775
+ console.error("Auto-translation error:", error);
2776
+ });
2777
+ }
2778
+ }, 200);
2779
+ }
2780
+ /**
2781
+ * Public method to translate the page to a specific language
2782
+ * @param langCode The language code to translate to
2783
+ * @returns Promise that resolves when translation is complete
2784
+ */
2785
+ async translateTo(langCode, onComplete, onError) {
2786
+ var _a;
2787
+ const startTime = Date.now();
2788
+ if (this.isTranslating) {
2789
+ console.warn("Translation already in progress");
2790
+ onError == null ? void 0 : onError(new Error("Translation already in progress"));
2791
+ return {
2792
+ success: false,
2793
+ targetLanguage: langCode,
2794
+ translatedNodes: 0,
2795
+ error: "Translation already in progress",
2796
+ duration: 0
2797
+ };
2798
+ }
2799
+ const supportedLang = this.targetLanguages.find((lang) => lang.code === langCode);
2800
+ if (!supportedLang) {
2801
+ onError == null ? void 0 : onError(new Error(`Unsupported language code: ${langCode}`));
2802
+ return {
2803
+ success: false,
2804
+ targetLanguage: langCode,
2805
+ translatedNodes: 0,
2806
+ error: `Unsupported language code: ${langCode}`,
2807
+ duration: 0
2808
+ };
2809
+ }
2810
+ if (langCode === this.currentLanguage) {
2811
+ onComplete == null ? void 0 : onComplete({
2812
+ success: true,
2813
+ targetLanguage: langCode,
2814
+ translatedNodes: 0,
2815
+ duration: 0
2816
+ });
2817
+ return {
2818
+ success: true,
2819
+ targetLanguage: langCode,
2820
+ translatedNodes: 0,
2821
+ duration: 0
2822
+ };
2823
+ }
2824
+ try {
2825
+ localStorage.setItem(LOCALSTORAGE_KEYS.PREFERRED_LANGUAGE, langCode);
2826
+ await this.translatePage(langCode);
2827
+ this.currentLanguage = langCode;
2828
+ const languageItems = this.widget.querySelectorAll(".jigts-language-item");
2829
+ for (const item of languageItems) {
2830
+ const isSelected = item.getAttribute("data-language-code") === langCode;
2831
+ item.classList.toggle("jigts-selected", isSelected);
2832
+ item.setAttribute("aria-selected", isSelected.toString());
2833
+ }
2834
+ const triggerContent = (_a = this.elements.trigger) == null ? void 0 : _a.querySelector(".jigts-trigger-content");
2835
+ if (triggerContent) {
2836
+ triggerContent.classList.add("jigts-has-translation");
2837
+ const triggerIcon = triggerContent.querySelector(".jigts-trigger-icon");
2838
+ if (triggerIcon && supportedLang) {
2839
+ triggerIcon.innerHTML = `<span class="jigts-lang-code">${supportedLang.code.toUpperCase()}</span><span class="jigts-lang-name">${supportedLang.name}</span>`;
2840
+ }
2841
+ }
2842
+ const endTime = Date.now();
2843
+ const translatedNodes = document.querySelectorAll(`[${ATTRIBUTES.TRANSLATED_LANG}]`).length;
2844
+ onComplete == null ? void 0 : onComplete({
2845
+ success: true,
2846
+ targetLanguage: langCode,
2847
+ translatedNodes,
2848
+ duration: endTime - startTime
2849
+ });
2850
+ return {
2851
+ success: true,
2852
+ targetLanguage: langCode,
2853
+ translatedNodes,
2854
+ duration: endTime - startTime
2855
+ };
2856
+ } catch (error) {
2857
+ onError == null ? void 0 : onError(error);
2858
+ return {
2859
+ success: false,
2860
+ targetLanguage: langCode,
2861
+ translatedNodes: 0,
2862
+ error: error instanceof Error ? error.message : "Unknown error occurred",
2863
+ duration: 0
2864
+ };
2865
+ }
2866
+ }
2867
+ /**
2868
+ * Get the current instance of TranslationWidget
2869
+ * @returns The current TranslationWidget instance or null if not initialized
2870
+ */
2871
+ static getInstance() {
2872
+ return _TranslationWidget.instance;
2873
+ }
2874
+ // Add this helper method to the class
2875
+ getLanguageSVG() {
2876
+ return `
2877
+ <svg class="jigts-languages-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2878
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2879
+ d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129">
2880
+ </path>
2881
+ </svg>
2882
+ `;
2883
+ }
2884
+ /**
2885
+ * Safely sets HTML content for translated text, handling nested HTML structures
2886
+ * @param element - The element to update
2887
+ * @param translatedText - The translated text that may contain HTML
2888
+ */
2889
+ setTranslatedContent(element, translatedText) {
2890
+ var _a;
2891
+ const hasHtmlTags = /<[^>]*>/g.test(translatedText);
2892
+ if (hasHtmlTags) {
2893
+ const tempContainer = document.createElement("div");
2894
+ tempContainer.innerHTML = translatedText;
2895
+ if (tempContainer.children.length === 1 && ((_a = tempContainer.firstElementChild) == null ? void 0 : _a.tagName.toLowerCase()) === element.tagName.toLowerCase()) {
2896
+ element.innerHTML = tempContainer.firstElementChild.innerHTML;
2897
+ } else {
2898
+ element.innerHTML = translatedText;
2899
+ }
2900
+ } else {
2901
+ element.textContent = translatedText;
2902
+ }
2903
+ }
2904
+ };
2905
+ __publicField(_TranslationWidget, "instance", null);
2906
+ let TranslationWidget = _TranslationWidget;
2907
+ if (typeof window !== "undefined") {
2908
+ window.resetTranslation = (defaultLang, onComplete, onError) => {
2909
+ const instance = TranslationWidget.getInstance();
2910
+ if (!instance) {
2911
+ return;
2912
+ }
2913
+ try {
2914
+ instance.resetToDefaultLanguage();
2915
+ onComplete == null ? void 0 : onComplete({
2916
+ success: true,
2917
+ targetLanguage: defaultLang
2918
+ });
2919
+ } catch (error) {
2920
+ onError == null ? void 0 : onError(error);
2921
+ }
2922
+ };
2923
+ window.translate = async (langCode, onComplete, onError) => {
2924
+ const instance = TranslationWidget.getInstance();
2925
+ if (!instance) {
2926
+ onError == null ? void 0 : onError(new Error("Translation widget not initialized"));
2927
+ return {
2928
+ success: false,
2929
+ targetLanguage: langCode,
2930
+ translatedNodes: 0,
2931
+ error: "Translation widget not initialized",
2932
+ duration: 0
2933
+ };
2934
+ }
2935
+ const startTime = Date.now();
2936
+ try {
2937
+ const result = await instance.translateTo(langCode, onComplete, onError);
2938
+ onComplete == null ? void 0 : onComplete(result);
2939
+ return result;
2940
+ } catch (error) {
2941
+ onError == null ? void 0 : onError(error);
2942
+ return {
2943
+ success: false,
2944
+ targetLanguage: langCode,
2945
+ translatedNodes: 0,
2946
+ error: error instanceof Error ? error.message : "Unknown error occurred",
2947
+ duration: Date.now() - startTime
2948
+ };
2949
+ }
2950
+ };
2951
+ window.clearCache = (lang_code = [], onComplete, onError) => {
2952
+ const localStorageWrapper = new LocalStorageWrapper(CACHE_PREFIX);
2953
+ try {
2954
+ localStorageWrapper.clear(lang_code);
2955
+ onComplete == null ? void 0 : onComplete();
2956
+ } catch (error) {
2957
+ onError == null ? void 0 : onError(error);
2958
+ }
2959
+ };
2960
+ }
2961
+ let widgetInstance;
2962
+ const initializeTranslationWidget = (publicKey, config) => {
2963
+ if (typeof window === "undefined") {
2964
+ throw new Error("Translation widget can only be used in browser environment");
2965
+ }
2966
+ const initWidget = () => {
2967
+ if (!widgetInstance) {
2968
+ if (!document.querySelector("style[data-translation-widget]")) {
2969
+ const style = document.createElement("style");
2970
+ style.setAttribute("data-translation-widget", "");
2971
+ style.textContent = styles;
2972
+ document.head.appendChild(style);
2973
+ }
2974
+ widgetInstance = new TranslationWidget(publicKey, config);
2975
+ }
2976
+ return widgetInstance;
2977
+ };
2978
+ if (document.readyState === "loading") {
2979
+ window.addEventListener("DOMContentLoaded", initWidget);
2980
+ return void 0;
2981
+ } else {
2982
+ return initWidget();
2983
+ }
2984
+ };
2985
+ (() => {
2986
+ const originalRemoveChild = Node.prototype.removeChild;
2987
+ Node.prototype.removeChild = function(child) {
2988
+ try {
2989
+ return originalRemoveChild.call(this, child);
2990
+ } catch (err) {
2991
+ if (err instanceof DOMException && err.name === "NotFoundError") {
2992
+ return child;
2993
+ }
2994
+ throw err;
2995
+ }
2996
+ };
2997
+ })();
2998
+ export {
2999
+ initializeTranslationWidget as default
3000
+ };