@lionrapid/core 0.3.0 → 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.
- package/dist/browser/attribute-extractor.d.ts +54 -0
- package/dist/browser/attribute-extractor.d.ts.map +1 -0
- package/dist/browser/attribute-extractor.js +155 -0
- package/dist/browser/attribute-extractor.js.map +1 -0
- package/dist/browser/auto-init.d.ts +4 -0
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +6 -2
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/auto-translator.d.ts +23 -0
- package/dist/browser/auto-translator.d.ts.map +1 -1
- package/dist/browser/auto-translator.js +90 -5
- package/dist/browser/auto-translator.js.map +1 -1
- package/dist/browser/block-extractor.d.ts +8 -0
- package/dist/browser/block-extractor.d.ts.map +1 -1
- package/dist/browser/block-extractor.js +77 -0
- package/dist/browser/block-extractor.js.map +1 -1
- package/dist/browser/editor/editor-api.d.ts +22 -1
- package/dist/browser/editor/editor-api.d.ts.map +1 -1
- package/dist/browser/editor/editor-api.js +54 -19
- package/dist/browser/editor/editor-api.js.map +1 -1
- package/dist/browser/editor/editor-init.js +72 -3
- package/dist/browser/editor/editor-init.js.map +1 -1
- package/dist/browser/editor/editor-styles.d.ts +1 -1
- package/dist/browser/editor/editor-styles.d.ts.map +1 -1
- package/dist/browser/editor/editor-styles.js +38 -1
- package/dist/browser/editor/editor-styles.js.map +1 -1
- package/dist/browser/editor/inspector.d.ts +8 -0
- package/dist/browser/editor/inspector.d.ts.map +1 -1
- package/dist/browser/editor/inspector.js +14 -0
- package/dist/browser/editor/inspector.js.map +1 -1
- package/dist/browser/editor/live-preview.d.ts +1 -1
- package/dist/browser/editor/live-preview.d.ts.map +1 -1
- package/dist/browser/editor/live-preview.js +27 -16
- package/dist/browser/editor/live-preview.js.map +1 -1
- package/dist/browser/editor/locale-url.d.ts +18 -0
- package/dist/browser/editor/locale-url.d.ts.map +1 -0
- package/dist/browser/editor/locale-url.js +37 -0
- package/dist/browser/editor/locale-url.js.map +1 -0
- package/dist/browser/editor/sidebar.d.ts +6 -0
- package/dist/browser/editor/sidebar.d.ts.map +1 -1
- package/dist/browser/editor/sidebar.js +133 -4
- package/dist/browser/editor/sidebar.js.map +1 -1
- package/dist/browser/editor/toolbar.d.ts +8 -1
- package/dist/browser/editor/toolbar.d.ts.map +1 -1
- package/dist/browser/editor/toolbar.js +49 -4
- package/dist/browser/editor/toolbar.js.map +1 -1
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +1 -0
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +324 -36
- package/dist/browser/lionrapid.auto.dev.js.map +4 -4
- package/dist/browser/lionrapid.auto.js +324 -36
- package/dist/browser/lionrapid.auto.js.map +4 -4
- package/dist/browser/lionrapid.auto.min.js +3 -3
- package/dist/browser/lionrapid.auto.min.js.map +4 -4
- package/dist/browser/lionrapid.editor.js +371 -36
- package/dist/browser/lionrapid.editor.js.map +3 -3
- package/dist/browser/lionrapid.editor.min.js +41 -4
- package/dist/browser/lionrapid.editor.min.js.map +4 -4
- package/dist/browser/lionrapid.esm.js +40 -26
- package/dist/browser/lionrapid.esm.js.map +2 -2
- package/dist/browser/lionrapid.esm.min.js +2 -2
- package/dist/browser/lionrapid.esm.min.js.map +3 -3
- package/dist/browser/lionrapid.iife.dev.js +40 -26
- package/dist/browser/lionrapid.iife.dev.js.map +2 -2
- package/dist/browser/lionrapid.iife.js +40 -26
- package/dist/browser/lionrapid.iife.js.map +2 -2
- package/dist/browser/lionrapid.iife.min.js +2 -2
- package/dist/browser/lionrapid.iife.min.js.map +3 -3
- package/dist/browser/lionrapid.umd.js +40 -26
- package/dist/browser/lionrapid.umd.js.map +2 -2
- package/dist/browser/lionrapid.umd.min.js +2 -2
- package/dist/browser/lionrapid.umd.min.js.map +3 -3
- package/dist/browser/md5.d.ts +3 -2
- package/dist/browser/md5.d.ts.map +1 -1
- package/dist/browser/md5.js +41 -6
- package/dist/browser/md5.js.map +1 -1
- package/dist/content/parser/MarkupParser.d.ts +0 -10
- package/dist/content/parser/MarkupParser.d.ts.map +1 -1
- package/dist/content/parser/MarkupParser.js +45 -3
- package/dist/content/parser/MarkupParser.js.map +1 -1
- package/dist/content/types.d.ts +30 -0
- package/dist/content/types.d.ts.map +1 -1
- package/dist/repositories/network-repository.d.ts +0 -14
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +4 -26
- package/dist/repositories/network-repository.js.map +1 -1
- 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);
|
|
@@ -3475,7 +3510,7 @@ var LionRapid = (() => {
|
|
|
3475
3510
|
logger.debug("API base URL is not set");
|
|
3476
3511
|
throw new NetworkError("API base URL is not set");
|
|
3477
3512
|
}
|
|
3478
|
-
const url = this.
|
|
3513
|
+
const url = this.buildUrl("getTranslation", { key });
|
|
3479
3514
|
logger.debug(`API GET: ${url}`);
|
|
3480
3515
|
try {
|
|
3481
3516
|
const response = await this.makeRequest(url, "GET");
|
|
@@ -3511,7 +3546,7 @@ var LionRapid = (() => {
|
|
|
3511
3546
|
logger.debug("API base URL is not set");
|
|
3512
3547
|
throw new NetworkError("API base URL is not set");
|
|
3513
3548
|
}
|
|
3514
|
-
const url = this.
|
|
3549
|
+
const url = this.buildUrl("getTranslation", { key });
|
|
3515
3550
|
logger.debug(`API GET ContentUnit: ${url}`);
|
|
3516
3551
|
try {
|
|
3517
3552
|
const response = await this.makeRequest(url, "GET");
|
|
@@ -3552,7 +3587,7 @@ var LionRapid = (() => {
|
|
|
3552
3587
|
logger.debug("API base URL is not set");
|
|
3553
3588
|
throw new NetworkError("API base URL is not set");
|
|
3554
3589
|
}
|
|
3555
|
-
const url = this.
|
|
3590
|
+
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3556
3591
|
logger.debug(`API GET Namespace: ${url}`);
|
|
3557
3592
|
const namespaceKey = `${locale}:${namespace}`;
|
|
3558
3593
|
try {
|
|
@@ -3666,7 +3701,7 @@ var LionRapid = (() => {
|
|
|
3666
3701
|
logger.debug("API base URL is not set");
|
|
3667
3702
|
throw new NetworkError("API base URL is not set");
|
|
3668
3703
|
}
|
|
3669
|
-
const url = this.
|
|
3704
|
+
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3670
3705
|
logger.debug(`API GET Namespace ContentUnits: ${url}`);
|
|
3671
3706
|
const namespaceKey = `${locale}:${namespace}`;
|
|
3672
3707
|
try {
|
|
@@ -3878,26 +3913,6 @@ var LionRapid = (() => {
|
|
|
3878
3913
|
}
|
|
3879
3914
|
return `${baseUrl}${path}`;
|
|
3880
3915
|
}
|
|
3881
|
-
/**
|
|
3882
|
-
* Check whether the current page is running inside an active visual-editor
|
|
3883
|
-
* session. The editor JS sets `__lr_editor_active=1` as a session cookie
|
|
3884
|
-
* when its JWT is established; we read it here so client-side translation
|
|
3885
|
-
* fetches request draft content too, matching what the edge proxy does
|
|
3886
|
-
* server-side. Outside a browser (Node/SSR) this is always false.
|
|
3887
|
-
*/
|
|
3888
|
-
isEditorPreviewSession() {
|
|
3889
|
-
if (typeof document === "undefined") return false;
|
|
3890
|
-
return document.cookie.split("; ").some((c) => c === "__lr_editor_active=1");
|
|
3891
|
-
}
|
|
3892
|
-
/**
|
|
3893
|
-
* Append `include_drafts=true` to a built URL when an editor session is
|
|
3894
|
-
* active. Leaves the URL unchanged for normal visitors so the delivery
|
|
3895
|
-
* gate continues to apply in production.
|
|
3896
|
-
*/
|
|
3897
|
-
withDraftsParam(url) {
|
|
3898
|
-
if (!this.isEditorPreviewSession()) return url;
|
|
3899
|
-
return url + (url.includes("?") ? "&" : "?") + "include_drafts=true";
|
|
3900
|
-
}
|
|
3901
3916
|
/**
|
|
3902
3917
|
* Make HTTP request with timeout and headers
|
|
3903
3918
|
*/
|
|
@@ -4224,15 +4239,46 @@ var LionRapid = (() => {
|
|
|
4224
4239
|
}
|
|
4225
4240
|
return str;
|
|
4226
4241
|
}
|
|
4227
|
-
function
|
|
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) {
|
|
4228
4273
|
const bin = [];
|
|
4229
|
-
for (let i = 0; i <
|
|
4230
|
-
bin[i >>
|
|
4274
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
4275
|
+
bin[i >> 2] |= bytes[i] << i % 4 * 8;
|
|
4231
4276
|
}
|
|
4232
4277
|
return bin;
|
|
4233
4278
|
}
|
|
4234
4279
|
function md5(str) {
|
|
4235
|
-
|
|
4280
|
+
const bytes = utf8Bytes(str);
|
|
4281
|
+
return binl2hex(binlMD5(bytes2binl(bytes), bytes.length * 8));
|
|
4236
4282
|
}
|
|
4237
4283
|
function generateAutoKey(text) {
|
|
4238
4284
|
const trimmed = text.trim();
|
|
@@ -4241,6 +4287,7 @@ var LionRapid = (() => {
|
|
|
4241
4287
|
|
|
4242
4288
|
// src/browser/block-extractor.ts
|
|
4243
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";
|
|
4244
4291
|
var SKIP_TAGS = /* @__PURE__ */ new Set([
|
|
4245
4292
|
"SCRIPT",
|
|
4246
4293
|
"STYLE",
|
|
@@ -4329,8 +4376,60 @@ var LionRapid = (() => {
|
|
|
4329
4376
|
}
|
|
4330
4377
|
results.push({ element: el, unit, key });
|
|
4331
4378
|
}
|
|
4379
|
+
this.extractOrphanInline(root, results, includeTranslated);
|
|
4332
4380
|
return results;
|
|
4333
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
|
+
}
|
|
4334
4433
|
isInsideSkipTag(element) {
|
|
4335
4434
|
let current = element.parentElement;
|
|
4336
4435
|
while (current) {
|
|
@@ -4366,6 +4465,116 @@ var LionRapid = (() => {
|
|
|
4366
4465
|
}
|
|
4367
4466
|
};
|
|
4368
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
|
+
|
|
4369
4578
|
// src/browser/dom-observer.ts
|
|
4370
4579
|
var DOMObserver = class {
|
|
4371
4580
|
constructor(callbacks, debounceMs = 50) {
|
|
@@ -4470,6 +4679,7 @@ var LionRapid = (() => {
|
|
|
4470
4679
|
constructor(config = {}) {
|
|
4471
4680
|
__publicField(this, "core", null);
|
|
4472
4681
|
__publicField(this, "blockExtractor");
|
|
4682
|
+
__publicField(this, "attributeExtractor");
|
|
4473
4683
|
__publicField(this, "domObserver", null);
|
|
4474
4684
|
__publicField(this, "locale", "");
|
|
4475
4685
|
__publicField(this, "sourceLocale", "");
|
|
@@ -4483,6 +4693,11 @@ var LionRapid = (() => {
|
|
|
4483
4693
|
config.excludeSelectors,
|
|
4484
4694
|
config.minTextLength
|
|
4485
4695
|
);
|
|
4696
|
+
this.attributeExtractor = new AttributeExtractor(
|
|
4697
|
+
config.excludePatterns,
|
|
4698
|
+
config.excludeSelectors,
|
|
4699
|
+
config.minTextLength
|
|
4700
|
+
);
|
|
4486
4701
|
this.debounceMs = config.debounceMs ?? 100;
|
|
4487
4702
|
}
|
|
4488
4703
|
/**
|
|
@@ -4531,8 +4746,9 @@ var LionRapid = (() => {
|
|
|
4531
4746
|
if (!this.core) {
|
|
4532
4747
|
return 0;
|
|
4533
4748
|
}
|
|
4534
|
-
const
|
|
4535
|
-
|
|
4749
|
+
const blockCount = this.translateBlocks(this.blockExtractor.extract(element));
|
|
4750
|
+
const attrCount = this.translateAttributesIn(this.attributeExtractor.extract(element));
|
|
4751
|
+
return blockCount + attrCount;
|
|
4536
4752
|
}
|
|
4537
4753
|
/**
|
|
4538
4754
|
* Translate the entire page
|
|
@@ -4630,12 +4846,79 @@ var LionRapid = (() => {
|
|
|
4630
4846
|
}
|
|
4631
4847
|
return count;
|
|
4632
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
|
+
}
|
|
4633
4897
|
/**
|
|
4634
4898
|
* Get translated count
|
|
4635
4899
|
*/
|
|
4636
4900
|
getTranslatedCount() {
|
|
4637
4901
|
return this.translatedCount;
|
|
4638
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
|
+
}
|
|
4639
4922
|
/**
|
|
4640
4923
|
* Re-translate a single element (for editor live preview reset)
|
|
4641
4924
|
*/
|
|
@@ -4644,14 +4927,15 @@ var LionRapid = (() => {
|
|
|
4644
4927
|
if (original !== null) element.innerHTML = original;
|
|
4645
4928
|
element.removeAttribute("data-lionrapid-client");
|
|
4646
4929
|
element.removeAttribute("data-lr-block");
|
|
4930
|
+
this.restoreClientAttributes(element);
|
|
4647
4931
|
this.translateElement(element);
|
|
4648
4932
|
}
|
|
4649
4933
|
/**
|
|
4650
4934
|
* Re-translate the page (after language change)
|
|
4651
4935
|
*/
|
|
4652
4936
|
retranslate() {
|
|
4653
|
-
const
|
|
4654
|
-
|
|
4937
|
+
const translatedBlocks = document.querySelectorAll("[data-lionrapid-client]");
|
|
4938
|
+
translatedBlocks.forEach((el) => {
|
|
4655
4939
|
const original = el.getAttribute("data-lr-original");
|
|
4656
4940
|
if (original !== null) {
|
|
4657
4941
|
el.innerHTML = original;
|
|
@@ -4659,6 +4943,8 @@ var LionRapid = (() => {
|
|
|
4659
4943
|
el.removeAttribute("data-lionrapid-client");
|
|
4660
4944
|
el.removeAttribute("data-lr-block");
|
|
4661
4945
|
});
|
|
4946
|
+
const translatedAttrs = document.querySelectorAll("[data-lionrapid-client-attrs]");
|
|
4947
|
+
translatedAttrs.forEach((el) => this.restoreClientAttributes(el));
|
|
4662
4948
|
this.translatedCount = 0;
|
|
4663
4949
|
if (this.locale !== this.sourceLocale) {
|
|
4664
4950
|
this.translatePage();
|
|
@@ -5388,6 +5674,7 @@ var LionRapid = (() => {
|
|
|
5388
5674
|
const switcherShowFlags = script.dataset.switcherShowFlags !== "false";
|
|
5389
5675
|
const switcherShowNames = script.dataset.switcherShowNames === "true";
|
|
5390
5676
|
const switcherCustomCss = script.dataset.switcherCustomCss || void 0;
|
|
5677
|
+
const editorCdnUrl = script.dataset.editorCdn || void 0;
|
|
5391
5678
|
return {
|
|
5392
5679
|
apiKey,
|
|
5393
5680
|
baseUrl,
|
|
@@ -5401,7 +5688,8 @@ var LionRapid = (() => {
|
|
|
5401
5688
|
switcherPosition,
|
|
5402
5689
|
switcherShowFlags,
|
|
5403
5690
|
switcherShowNames,
|
|
5404
|
-
switcherCustomCss
|
|
5691
|
+
switcherCustomCss,
|
|
5692
|
+
editorCdnUrl
|
|
5405
5693
|
};
|
|
5406
5694
|
}
|
|
5407
5695
|
function readLocaleCookie(availableLanguages) {
|
|
@@ -5583,7 +5871,7 @@ var LionRapid = (() => {
|
|
|
5583
5871
|
};
|
|
5584
5872
|
window.LionRapid = instance;
|
|
5585
5873
|
const editorParam = new URLSearchParams(window.location.search).get("__lr_editor");
|
|
5586
|
-
if (editorParam && !document.querySelector("script[data-grant]")) {
|
|
5874
|
+
if (editorParam && !document.querySelector("script[data-grant], script[data-lr-editor]")) {
|
|
5587
5875
|
const editorScript = document.createElement("script");
|
|
5588
5876
|
editorScript.src = config.editorCdnUrl || "https://cdn.lionrapid.com/lionrapid.editor.min.js";
|
|
5589
5877
|
editorScript.dataset.grant = editorParam;
|