@lionrapid/core 0.3.1 → 0.4.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.
Files changed (85) hide show
  1. package/dist/browser/attribute-extractor.d.ts +54 -0
  2. package/dist/browser/attribute-extractor.d.ts.map +1 -0
  3. package/dist/browser/attribute-extractor.js +155 -0
  4. package/dist/browser/attribute-extractor.js.map +1 -0
  5. package/dist/browser/auto-init.d.ts +4 -0
  6. package/dist/browser/auto-init.d.ts.map +1 -1
  7. package/dist/browser/auto-init.js +6 -2
  8. package/dist/browser/auto-init.js.map +1 -1
  9. package/dist/browser/auto-translator.d.ts +23 -0
  10. package/dist/browser/auto-translator.d.ts.map +1 -1
  11. package/dist/browser/auto-translator.js +90 -5
  12. package/dist/browser/auto-translator.js.map +1 -1
  13. package/dist/browser/block-extractor.d.ts +8 -0
  14. package/dist/browser/block-extractor.d.ts.map +1 -1
  15. package/dist/browser/block-extractor.js +77 -0
  16. package/dist/browser/block-extractor.js.map +1 -1
  17. package/dist/browser/editor/editor-api.d.ts +22 -1
  18. package/dist/browser/editor/editor-api.d.ts.map +1 -1
  19. package/dist/browser/editor/editor-api.js +54 -19
  20. package/dist/browser/editor/editor-api.js.map +1 -1
  21. package/dist/browser/editor/editor-init.js +72 -3
  22. package/dist/browser/editor/editor-init.js.map +1 -1
  23. package/dist/browser/editor/editor-styles.d.ts +1 -1
  24. package/dist/browser/editor/editor-styles.d.ts.map +1 -1
  25. package/dist/browser/editor/editor-styles.js +38 -1
  26. package/dist/browser/editor/editor-styles.js.map +1 -1
  27. package/dist/browser/editor/inspector.d.ts +8 -0
  28. package/dist/browser/editor/inspector.d.ts.map +1 -1
  29. package/dist/browser/editor/inspector.js +14 -0
  30. package/dist/browser/editor/inspector.js.map +1 -1
  31. package/dist/browser/editor/live-preview.d.ts +1 -1
  32. package/dist/browser/editor/live-preview.d.ts.map +1 -1
  33. package/dist/browser/editor/live-preview.js +27 -16
  34. package/dist/browser/editor/live-preview.js.map +1 -1
  35. package/dist/browser/editor/locale-url.d.ts +18 -0
  36. package/dist/browser/editor/locale-url.d.ts.map +1 -0
  37. package/dist/browser/editor/locale-url.js +37 -0
  38. package/dist/browser/editor/locale-url.js.map +1 -0
  39. package/dist/browser/editor/sidebar.d.ts +6 -0
  40. package/dist/browser/editor/sidebar.d.ts.map +1 -1
  41. package/dist/browser/editor/sidebar.js +133 -4
  42. package/dist/browser/editor/sidebar.js.map +1 -1
  43. package/dist/browser/editor/toolbar.d.ts +8 -1
  44. package/dist/browser/editor/toolbar.d.ts.map +1 -1
  45. package/dist/browser/editor/toolbar.js +49 -4
  46. package/dist/browser/editor/toolbar.js.map +1 -1
  47. package/dist/browser/index.d.ts +2 -0
  48. package/dist/browser/index.d.ts.map +1 -1
  49. package/dist/browser/index.js +1 -0
  50. package/dist/browser/index.js.map +1 -1
  51. package/dist/browser/lionrapid.auto.dev.js +320 -12
  52. package/dist/browser/lionrapid.auto.dev.js.map +4 -4
  53. package/dist/browser/lionrapid.auto.js +320 -12
  54. package/dist/browser/lionrapid.auto.js.map +4 -4
  55. package/dist/browser/lionrapid.auto.min.js +3 -3
  56. package/dist/browser/lionrapid.auto.min.js.map +4 -4
  57. package/dist/browser/lionrapid.editor.js +371 -36
  58. package/dist/browser/lionrapid.editor.js.map +3 -3
  59. package/dist/browser/lionrapid.editor.min.js +41 -4
  60. package/dist/browser/lionrapid.editor.min.js.map +4 -4
  61. package/dist/browser/lionrapid.esm.js +36 -2
  62. package/dist/browser/lionrapid.esm.js.map +2 -2
  63. package/dist/browser/lionrapid.esm.min.js +2 -2
  64. package/dist/browser/lionrapid.esm.min.js.map +3 -3
  65. package/dist/browser/lionrapid.iife.dev.js +36 -2
  66. package/dist/browser/lionrapid.iife.dev.js.map +2 -2
  67. package/dist/browser/lionrapid.iife.js +36 -2
  68. package/dist/browser/lionrapid.iife.js.map +2 -2
  69. package/dist/browser/lionrapid.iife.min.js +2 -2
  70. package/dist/browser/lionrapid.iife.min.js.map +3 -3
  71. package/dist/browser/lionrapid.umd.js +36 -2
  72. package/dist/browser/lionrapid.umd.js.map +2 -2
  73. package/dist/browser/lionrapid.umd.min.js +2 -2
  74. package/dist/browser/lionrapid.umd.min.js.map +3 -3
  75. package/dist/browser/md5.d.ts +3 -2
  76. package/dist/browser/md5.d.ts.map +1 -1
  77. package/dist/browser/md5.js +41 -6
  78. package/dist/browser/md5.js.map +1 -1
  79. package/dist/content/parser/MarkupParser.d.ts +0 -10
  80. package/dist/content/parser/MarkupParser.d.ts.map +1 -1
  81. package/dist/content/parser/MarkupParser.js +45 -3
  82. package/dist/content/parser/MarkupParser.js.map +1 -1
  83. package/dist/content/types.d.ts +30 -0
  84. package/dist/content/types.d.ts.map +1 -1
  85. package/package.json +1 -1
@@ -23,6 +23,7 @@ var LionRapid = (() => {
23
23
  // src/browser/index.ts
24
24
  var index_exports = {};
25
25
  __export(index_exports, {
26
+ AttributeExtractor: () => AttributeExtractor,
26
27
  AutoTranslator: () => AutoTranslator,
27
28
  BlockExtractor: () => BlockExtractor,
28
29
  CacheStorageRepository: () => CacheStorageRepository,
@@ -2179,10 +2180,44 @@ var LionRapid = (() => {
2179
2180
  }
2180
2181
 
2181
2182
  // src/content/parser/MarkupParser.ts
2183
+ function decodeEntities(text) {
2184
+ const named = {
2185
+ amp: "&",
2186
+ lt: "<",
2187
+ gt: ">",
2188
+ quot: '"',
2189
+ apos: "'",
2190
+ nbsp: " ",
2191
+ rsquo: "\u2019",
2192
+ lsquo: "\u2018",
2193
+ rdquo: "\u201D",
2194
+ ldquo: "\u201C",
2195
+ hellip: "\u2026",
2196
+ mdash: "\u2014",
2197
+ ndash: "\u2013",
2198
+ copy: "\xA9",
2199
+ reg: "\xAE",
2200
+ trade: "\u2122"
2201
+ };
2202
+ return text.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z][a-z0-9]*);/gi, (m, body) => {
2203
+ if (body[0] === "#") {
2204
+ const cp = body[1] === "x" || body[1] === "X" ? parseInt(body.slice(2), 16) : parseInt(body.slice(1), 10);
2205
+ if (Number.isFinite(cp) && cp > 0) {
2206
+ try {
2207
+ return String.fromCodePoint(cp);
2208
+ } catch {
2209
+ return m;
2210
+ }
2211
+ }
2212
+ return m;
2213
+ }
2214
+ return named[body.toLowerCase()] ?? m;
2215
+ });
2216
+ }
2182
2217
  var MarkupParser = class {
2183
2218
  parse(input) {
2184
2219
  if (/<\/?[0-9]+>/.test(input)) {
2185
- return { text: input, styles: {} };
2220
+ return { text: decodeEntities(input), styles: {} };
2186
2221
  }
2187
2222
  const styles = {};
2188
2223
  let tagIndex = 0;
@@ -2227,7 +2262,7 @@ var LionRapid = (() => {
2227
2262
  const unclosed = tagStack.pop();
2228
2263
  result += `</${unclosed.index}>`;
2229
2264
  }
2230
- return { text: result, styles };
2265
+ return { text: decodeEntities(result), styles };
2231
2266
  }
2232
2267
  matchTag(input, startPos) {
2233
2268
  const tagStart = input.indexOf("<", startPos);
@@ -4204,15 +4239,46 @@ var LionRapid = (() => {
4204
4239
  }
4205
4240
  return str;
4206
4241
  }
4207
- function str2binl(str) {
4242
+ function utf8Bytes(str) {
4243
+ if (typeof TextEncoder !== "undefined") {
4244
+ return new TextEncoder().encode(str);
4245
+ }
4246
+ const out = [];
4247
+ for (let i = 0; i < str.length; i++) {
4248
+ const c = str.charCodeAt(i);
4249
+ if (c < 128) {
4250
+ out.push(c);
4251
+ } else if (c < 2048) {
4252
+ out.push(192 | c >> 6, 128 | c & 63);
4253
+ } else if (c >= 55296 && c < 56320 && i + 1 < str.length) {
4254
+ const c2 = str.charCodeAt(++i);
4255
+ const cp = 65536 + (c - 55296 << 10) + (c2 - 56320);
4256
+ out.push(
4257
+ 240 | cp >> 18,
4258
+ 128 | cp >> 12 & 63,
4259
+ 128 | cp >> 6 & 63,
4260
+ 128 | cp & 63
4261
+ );
4262
+ } else {
4263
+ out.push(
4264
+ 224 | c >> 12,
4265
+ 128 | c >> 6 & 63,
4266
+ 128 | c & 63
4267
+ );
4268
+ }
4269
+ }
4270
+ return new Uint8Array(out);
4271
+ }
4272
+ function bytes2binl(bytes) {
4208
4273
  const bin = [];
4209
- for (let i = 0; i < str.length * 8; i += 8) {
4210
- bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32;
4274
+ for (let i = 0; i < bytes.length; i++) {
4275
+ bin[i >> 2] |= bytes[i] << i % 4 * 8;
4211
4276
  }
4212
4277
  return bin;
4213
4278
  }
4214
4279
  function md5(str) {
4215
- return binl2hex(binlMD5(str2binl(str), str.length * 8));
4280
+ const bytes = utf8Bytes(str);
4281
+ return binl2hex(binlMD5(bytes2binl(bytes), bytes.length * 8));
4216
4282
  }
4217
4283
  function generateAutoKey(text) {
4218
4284
  const trimmed = text.trim();
@@ -4221,6 +4287,7 @@ var LionRapid = (() => {
4221
4287
 
4222
4288
  // src/browser/block-extractor.ts
4223
4289
  var BLOCK_SELECTOR = "p,h1,h2,h3,h4,h5,h6,li,td,th,label,button,figcaption,dt,dd,summary,caption";
4290
+ var INLINE_BLOCK_SELECTOR = "a";
4224
4291
  var SKIP_TAGS = /* @__PURE__ */ new Set([
4225
4292
  "SCRIPT",
4226
4293
  "STYLE",
@@ -4309,8 +4376,60 @@ var LionRapid = (() => {
4309
4376
  }
4310
4377
  results.push({ element: el, unit, key });
4311
4378
  }
4379
+ this.extractOrphanInline(root, results, includeTranslated);
4312
4380
  return results;
4313
4381
  }
4382
+ /**
4383
+ * Second pass: extract inline elements (currently <a>) that have no block
4384
+ * ancestor. These would otherwise slip through both the block pass (not in
4385
+ * BLOCK_SELECTOR) and the inline-style capture (no parent block to host
4386
+ * them as `<1>...</1>`). Common shape: a `<div>` action group containing
4387
+ * only links.
4388
+ */
4389
+ extractOrphanInline(root, results, includeTranslated) {
4390
+ const candidates = root.querySelectorAll(INLINE_BLOCK_SELECTOR);
4391
+ const elements = [];
4392
+ if (root instanceof Element && root.matches(INLINE_BLOCK_SELECTOR)) {
4393
+ elements.push(root);
4394
+ }
4395
+ for (let i = 0; i < candidates.length; i++) {
4396
+ elements.push(candidates[i]);
4397
+ }
4398
+ for (let i = 0; i < elements.length; i++) {
4399
+ const el = elements[i];
4400
+ if (this.isInsideSkipTag(el)) continue;
4401
+ if (!includeTranslated && this.hasSkipAttribute(el)) continue;
4402
+ if (this.matchesExcludeSelector(el)) continue;
4403
+ if (el.getAttribute("translate") === "no") continue;
4404
+ if (el.parentElement?.closest(BLOCK_SELECTOR)) continue;
4405
+ if (el.parentElement?.closest(INLINE_BLOCK_SELECTOR)) continue;
4406
+ if (el.querySelector(BLOCK_SELECTOR)) continue;
4407
+ const lrOriginal = el.getAttribute("data-lr-original");
4408
+ let unit;
4409
+ if (lrOriginal) {
4410
+ const temp = document.createElement(el.tagName);
4411
+ temp.innerHTML = lrOriginal;
4412
+ unit = parseBlockElement(temp);
4413
+ } else {
4414
+ unit = parseBlockElement(el);
4415
+ }
4416
+ const strippedText = unit.text.replace(/<\/?[0-9]+\/?>/g, "");
4417
+ if (this.excludeFilter.shouldExclude(strippedText)) continue;
4418
+ const lrBlock = el.getAttribute("data-lr-block");
4419
+ let key;
4420
+ if (lrBlock) {
4421
+ key = lrBlock;
4422
+ } else {
4423
+ const dataI18n = el.getAttribute("data-i18n");
4424
+ if (dataI18n) {
4425
+ key = dataI18n.includes(":") ? dataI18n : `common:${dataI18n}`;
4426
+ } else {
4427
+ key = generateAutoKey(unit.text);
4428
+ }
4429
+ }
4430
+ results.push({ element: el, unit, key });
4431
+ }
4432
+ }
4314
4433
  isInsideSkipTag(element) {
4315
4434
  let current = element.parentElement;
4316
4435
  while (current) {
@@ -4346,6 +4465,116 @@ var LionRapid = (() => {
4346
4465
  }
4347
4466
  };
4348
4467
 
4468
+ // src/browser/attribute-extractor.ts
4469
+ var TARGETS = [
4470
+ { selector: 'img[alt], area[alt], input[type="image"][alt]', attr: "alt", type: "alt" },
4471
+ { selector: "[title]", attr: "title", type: "title" },
4472
+ { selector: "input[placeholder], textarea[placeholder]", attr: "placeholder", type: "placeholder" },
4473
+ { selector: 'meta[name="description"][content]', attr: "content", type: "meta-description" },
4474
+ {
4475
+ selector: 'input[type="submit"][value], input[type="button"][value], input[type="reset"][value]',
4476
+ attr: "value",
4477
+ type: "input-value"
4478
+ }
4479
+ ];
4480
+ var SKIP_TAGS2 = /* @__PURE__ */ new Set([
4481
+ "SCRIPT",
4482
+ "STYLE",
4483
+ "CODE",
4484
+ "PRE",
4485
+ "NOSCRIPT",
4486
+ "SVG"
4487
+ ]);
4488
+ var SKIP_ATTRIBUTES2 = [
4489
+ "data-lionrapid-translated",
4490
+ "data-lionrapid-client",
4491
+ "data-no-translate"
4492
+ ];
4493
+ function stampAttrName(attrName) {
4494
+ return `data-lr-attr-${attrName.toLowerCase()}`;
4495
+ }
4496
+ var AttributeExtractor = class {
4497
+ constructor(excludePatterns = {}, excludeSelectors = [], minTextLength = 2) {
4498
+ __publicField(this, "excludeFilter");
4499
+ __publicField(this, "excludeSelectors");
4500
+ this.excludeFilter = new ExcludeFilter(excludePatterns, minTextLength);
4501
+ this.excludeSelectors = excludeSelectors;
4502
+ }
4503
+ /**
4504
+ * Walk the DOM under `root` and return every translatable attribute.
4505
+ *
4506
+ * @param root Element or Document to scan
4507
+ * @param includeStamped When true, elements already carrying
4508
+ * `data-lr-attr-{name}` are returned anyway —
4509
+ * the editor uses this to surface server-handled
4510
+ * attributes for inspection. The runtime keeps
4511
+ * the default `false` so it doesn't re-extract
4512
+ * what the server already keyed.
4513
+ */
4514
+ extract(root, includeStamped = false) {
4515
+ const results = [];
4516
+ for (const target of TARGETS) {
4517
+ const candidates = root.querySelectorAll(target.selector);
4518
+ const all = [];
4519
+ if (root instanceof Element && root.matches(target.selector)) {
4520
+ all.push(root);
4521
+ }
4522
+ for (let i = 0; i < candidates.length; i++) {
4523
+ all.push(candidates[i]);
4524
+ }
4525
+ for (let i = 0; i < all.length; i++) {
4526
+ const el = all[i];
4527
+ if (this.isInsideSkipTag(el)) continue;
4528
+ if (this.hasSkipAncestor(el)) continue;
4529
+ if (this.matchesExcludeSelector(el)) continue;
4530
+ if (el.getAttribute("translate") === "no") continue;
4531
+ const rawValue = el.getAttribute(target.attr);
4532
+ if (rawValue === null) continue;
4533
+ const trimmed = rawValue.trim();
4534
+ if (this.excludeFilter.shouldExclude(trimmed)) continue;
4535
+ const stampName = stampAttrName(target.attr);
4536
+ const stamped = el.getAttribute(stampName);
4537
+ if (!includeStamped && stamped) {
4538
+ continue;
4539
+ }
4540
+ const unit = { text: rawValue, type: target.type };
4541
+ const key = stamped ?? generateAutoKey(rawValue);
4542
+ results.push({ element: el, attrName: target.attr, unit, key });
4543
+ }
4544
+ }
4545
+ return results;
4546
+ }
4547
+ isInsideSkipTag(element) {
4548
+ let current = element.parentElement;
4549
+ while (current) {
4550
+ if (SKIP_TAGS2.has(current.tagName)) return true;
4551
+ current = current.parentElement;
4552
+ }
4553
+ return false;
4554
+ }
4555
+ hasSkipAncestor(element) {
4556
+ let current = element;
4557
+ while (current) {
4558
+ for (const attr of SKIP_ATTRIBUTES2) {
4559
+ if (current.hasAttribute(attr)) return true;
4560
+ }
4561
+ current = current.parentElement;
4562
+ }
4563
+ return false;
4564
+ }
4565
+ matchesExcludeSelector(element) {
4566
+ for (const selector of this.excludeSelectors) {
4567
+ try {
4568
+ if (element.matches(selector) || element.closest(selector)) {
4569
+ return true;
4570
+ }
4571
+ } catch {
4572
+ }
4573
+ }
4574
+ return false;
4575
+ }
4576
+ };
4577
+
4349
4578
  // src/browser/dom-observer.ts
4350
4579
  var DOMObserver = class {
4351
4580
  constructor(callbacks, debounceMs = 50) {
@@ -4450,6 +4679,7 @@ var LionRapid = (() => {
4450
4679
  constructor(config = {}) {
4451
4680
  __publicField(this, "core", null);
4452
4681
  __publicField(this, "blockExtractor");
4682
+ __publicField(this, "attributeExtractor");
4453
4683
  __publicField(this, "domObserver", null);
4454
4684
  __publicField(this, "locale", "");
4455
4685
  __publicField(this, "sourceLocale", "");
@@ -4463,6 +4693,11 @@ var LionRapid = (() => {
4463
4693
  config.excludeSelectors,
4464
4694
  config.minTextLength
4465
4695
  );
4696
+ this.attributeExtractor = new AttributeExtractor(
4697
+ config.excludePatterns,
4698
+ config.excludeSelectors,
4699
+ config.minTextLength
4700
+ );
4466
4701
  this.debounceMs = config.debounceMs ?? 100;
4467
4702
  }
4468
4703
  /**
@@ -4511,8 +4746,9 @@ var LionRapid = (() => {
4511
4746
  if (!this.core) {
4512
4747
  return 0;
4513
4748
  }
4514
- const extracted = this.blockExtractor.extract(element);
4515
- return this.translateBlocks(extracted);
4749
+ const blockCount = this.translateBlocks(this.blockExtractor.extract(element));
4750
+ const attrCount = this.translateAttributesIn(this.attributeExtractor.extract(element));
4751
+ return blockCount + attrCount;
4516
4752
  }
4517
4753
  /**
4518
4754
  * Translate the entire page
@@ -4610,12 +4846,79 @@ var LionRapid = (() => {
4610
4846
  }
4611
4847
  return count;
4612
4848
  }
4849
+ /**
4850
+ * Translate extracted attribute units against the cache.
4851
+ *
4852
+ * Mirrors translateBlocks but for attribute values. For each hit, the
4853
+ * original attribute value is preserved in `data-lr-orig-{name}` so
4854
+ * retranslate() can restore source. The element accumulates a comma-
4855
+ * separated `data-lionrapid-client-attrs` list naming every attr the
4856
+ * client touched — so we can find them all on retranslate without
4857
+ * iterating a fixed selector set.
4858
+ *
4859
+ * Cache misses emit `translation:not-found` with empty styles, so the
4860
+ * existing MissingTranslationQueue + NetworkRepository sync queue
4861
+ * picks them up unchanged.
4862
+ */
4863
+ translateAttributesIn(extracted) {
4864
+ if (!this.core) return 0;
4865
+ let count = 0;
4866
+ for (const { element, attrName, unit, key } of extracted) {
4867
+ const fullKey = `${this.locale}:${this.namespace}:${key}`;
4868
+ const cached = this.core.cache.get(fullKey);
4869
+ if (cached && cached.value !== unit.text && cached.value !== key) {
4870
+ const lower = attrName.toLowerCase();
4871
+ const origAttr = `data-lr-orig-${lower}`;
4872
+ if (!element.hasAttribute(origAttr)) {
4873
+ element.setAttribute(origAttr, unit.text);
4874
+ }
4875
+ element.setAttribute(attrName, cached.value);
4876
+ element.setAttribute(`data-lr-attr-${lower}`, key);
4877
+ const existing = element.getAttribute("data-lionrapid-client-attrs") ?? "";
4878
+ const set = new Set(existing.split(",").filter(Boolean));
4879
+ set.add(lower);
4880
+ element.setAttribute("data-lionrapid-client-attrs", Array.from(set).join(","));
4881
+ count++;
4882
+ } else {
4883
+ this.core.bus.emit("translation:not-found", {
4884
+ payload: {
4885
+ key,
4886
+ fallbackValue: unit.text,
4887
+ locale: this.sourceLocale,
4888
+ namespace: this.namespace,
4889
+ status: 404,
4890
+ styles: {}
4891
+ }
4892
+ });
4893
+ }
4894
+ }
4895
+ return count;
4896
+ }
4613
4897
  /**
4614
4898
  * Get translated count
4615
4899
  */
4616
4900
  getTranslatedCount() {
4617
4901
  return this.translatedCount;
4618
4902
  }
4903
+ /**
4904
+ * Restore every client-translated attribute on `el` from its
4905
+ * `data-lr-orig-*` backup, then strip the stamps. Mirrors the block-side
4906
+ * restoration in retranslate() so language switch / editor reset clears
4907
+ * attributes too.
4908
+ */
4909
+ restoreClientAttributes(el) {
4910
+ const list = el.getAttribute("data-lionrapid-client-attrs");
4911
+ if (list === null) return;
4912
+ for (const name of list.split(",").filter(Boolean)) {
4913
+ const orig = el.getAttribute(`data-lr-orig-${name}`);
4914
+ if (orig !== null) {
4915
+ el.setAttribute(name, orig);
4916
+ el.removeAttribute(`data-lr-orig-${name}`);
4917
+ }
4918
+ el.removeAttribute(`data-lr-attr-${name}`);
4919
+ }
4920
+ el.removeAttribute("data-lionrapid-client-attrs");
4921
+ }
4619
4922
  /**
4620
4923
  * Re-translate a single element (for editor live preview reset)
4621
4924
  */
@@ -4624,14 +4927,15 @@ var LionRapid = (() => {
4624
4927
  if (original !== null) element.innerHTML = original;
4625
4928
  element.removeAttribute("data-lionrapid-client");
4626
4929
  element.removeAttribute("data-lr-block");
4930
+ this.restoreClientAttributes(element);
4627
4931
  this.translateElement(element);
4628
4932
  }
4629
4933
  /**
4630
4934
  * Re-translate the page (after language change)
4631
4935
  */
4632
4936
  retranslate() {
4633
- const translated = document.querySelectorAll("[data-lionrapid-client]");
4634
- translated.forEach((el) => {
4937
+ const translatedBlocks = document.querySelectorAll("[data-lionrapid-client]");
4938
+ translatedBlocks.forEach((el) => {
4635
4939
  const original = el.getAttribute("data-lr-original");
4636
4940
  if (original !== null) {
4637
4941
  el.innerHTML = original;
@@ -4639,6 +4943,8 @@ var LionRapid = (() => {
4639
4943
  el.removeAttribute("data-lionrapid-client");
4640
4944
  el.removeAttribute("data-lr-block");
4641
4945
  });
4946
+ const translatedAttrs = document.querySelectorAll("[data-lionrapid-client-attrs]");
4947
+ translatedAttrs.forEach((el) => this.restoreClientAttributes(el));
4642
4948
  this.translatedCount = 0;
4643
4949
  if (this.locale !== this.sourceLocale) {
4644
4950
  this.translatePage();
@@ -5368,6 +5674,7 @@ var LionRapid = (() => {
5368
5674
  const switcherShowFlags = script.dataset.switcherShowFlags !== "false";
5369
5675
  const switcherShowNames = script.dataset.switcherShowNames === "true";
5370
5676
  const switcherCustomCss = script.dataset.switcherCustomCss || void 0;
5677
+ const editorCdnUrl = script.dataset.editorCdn || void 0;
5371
5678
  return {
5372
5679
  apiKey,
5373
5680
  baseUrl,
@@ -5381,7 +5688,8 @@ var LionRapid = (() => {
5381
5688
  switcherPosition,
5382
5689
  switcherShowFlags,
5383
5690
  switcherShowNames,
5384
- switcherCustomCss
5691
+ switcherCustomCss,
5692
+ editorCdnUrl
5385
5693
  };
5386
5694
  }
5387
5695
  function readLocaleCookie(availableLanguages) {
@@ -5563,7 +5871,7 @@ var LionRapid = (() => {
5563
5871
  };
5564
5872
  window.LionRapid = instance;
5565
5873
  const editorParam = new URLSearchParams(window.location.search).get("__lr_editor");
5566
- if (editorParam && !document.querySelector("script[data-grant]")) {
5874
+ if (editorParam && !document.querySelector("script[data-grant], script[data-lr-editor]")) {
5567
5875
  const editorScript = document.createElement("script");
5568
5876
  editorScript.src = config.editorCdnUrl || "https://cdn.lionrapid.com/lionrapid.editor.min.js";
5569
5877
  editorScript.dataset.grant = editorParam;