@ecomconsult/consentkit 0.5.13 → 0.5.15
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/README.md +203 -9
- package/npm/index.d.ts +35 -1
- package/npm/internal-stub.mjs +1 -1
- package/package.json +1 -1
- package/src/ck-core.js +24 -1
- package/src/ck-debug.js +74 -6
- package/src/ck-locales.js +36 -1
- package/src/ck-saas.js +154 -6
- package/src/ck-ui.js +554 -6
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Vanilla ES2020, zero dependencies, no build step.
|
|
|
29
29
|
- **Equal-weight buttons, no pre-ticked boxes** — the consent invariants are
|
|
30
30
|
fixed by design, see [CONTRIBUTING.md](https://github.com/vermoh/ConsentKit/blob/main/CONTRIBUTING.md)
|
|
31
31
|
|
|
32
|
-
> **Status: prototype (v0.5.
|
|
32
|
+
> **Status: prototype (v0.5.15).** The core, the UI and the demo are verified in
|
|
33
33
|
> a browser and covered by an automated suite (`npm test`); several distribution
|
|
34
34
|
> paths are not yet tested against live systems. See
|
|
35
35
|
> [Project status](#project-status) before shipping this to production.
|
|
@@ -193,6 +193,8 @@ Pass any subset to `init()`. Nested objects merge with the defaults.
|
|
|
193
193
|
| `texts.policyUrl` | `string` | — | v0.5.0. Cookie policy address. `http(s)` only; anything else is ignored |
|
|
194
194
|
| `texts.detailsAction` | `"policy" \| "settings" \| "hide" \| "declaration"` | see notes | v0.5.0, `declaration` in v0.5.7. What «Learn more» does. Defaults to `policy` when `policyUrl` is set, `settings` when it is not. `policy` or `declaration` without a usable URL falls back to `settings` rather than rendering a dead link |
|
|
195
195
|
| `texts.declarationUrl` | `string` | — | v0.5.7. Address of the cookie declaration page, used by `detailsAction: "declaration"`. `http(s)` only. Filled by the hosted service; the client only reads it |
|
|
196
|
+
| `texts.<lang>` | `object` | — | v0.5.15. Per-language dictionary overrides, keyed by a language tag (`ru`, `ro`, `en`, `pt-br`, …). Overridable keys: `bannerTitle`, `bannerText`, `panelTitle`, `panelIntro`, `extraTitle`, `extraText`, and `cat.<necessary\|functional\|analytics\|marketing>.title` / `.desc`. An empty string falls through to the standard text; every other key is ignored — see [Custom texts and links](#custom-texts-and-links) |
|
|
197
|
+
| `texts.links` | `object[]` | `[]` | v0.5.15. Up to 3 links under the banner buttons and at the foot of the settings panel: `{ id, url, label: { ru, ro, en, … } }`. `url` is `http(s)` only; a row with no resolvable label or an unusable address is skipped. Does not affect `detailsAction`, except that «Learn more» is hidden when its `policy` / `declaration` URL repeats one of these links — see [Custom texts and links](#custom-texts-and-links) |
|
|
196
198
|
| `categories.*.enabled` | `boolean` | `true` | Per category: `functional`, `analytics`, `marketing`. Hides the toggle when `false` |
|
|
197
199
|
| `consentTtlDays` | `number` | `365` | Lifetime of the stored decision |
|
|
198
200
|
| `integrations.gcm` | `boolean` | `true` | Google Consent Mode v2 signals |
|
|
@@ -363,10 +365,161 @@ Both link forms accept `http(s)` addresses only. A `javascript:` or `data:` URL
|
|
|
363
365
|
in a control the visitor is invited to click is an XSS vector, so anything else
|
|
364
366
|
is refused and the link degrades to `settings`.
|
|
365
367
|
|
|
368
|
+
Since 0.5.15 both link forms are also dropped for a render whose `texts.links`
|
|
369
|
+
already show the same address, so the banner never links one page twice — see
|
|
370
|
+
[`texts.links`](#textslinks--your-own-links).
|
|
371
|
+
|
|
366
372
|
> Before 0.5.0 this control was rendered as `<a href="#">` with no handler at
|
|
367
373
|
> all: clicking it jumped to the top of the page and nothing else. Any site
|
|
368
374
|
> running 0.4.x or earlier has a dead «Learn more» link.
|
|
369
375
|
|
|
376
|
+
### Custom texts and links
|
|
377
|
+
|
|
378
|
+
Since 0.5.15 the banner and the panel can carry your own words, per language,
|
|
379
|
+
and up to three of your own links. This exists because a cookie banner in some
|
|
380
|
+
jurisdictions has to name the operator and say where a data subject may
|
|
381
|
+
complain — that is a legal requirement, not decoration, and it does not fit in
|
|
382
|
+
any of the built-in sentences.
|
|
383
|
+
|
|
384
|
+
#### `texts.<lang>` — dictionary overrides
|
|
385
|
+
|
|
386
|
+
A key under `texts` is treated as a language dictionary when — and only when —
|
|
387
|
+
it looks like a language tag, matching `/^[a-z]{2}(-[a-z]{2})?$/`. That is what
|
|
388
|
+
keeps `policyUrl`, `detailsAction`, `declarationUrl` and `links` out of it; no
|
|
389
|
+
scalar setting under `texts` is ever two letters.
|
|
390
|
+
|
|
391
|
+
Overridable keys, and nothing else:
|
|
392
|
+
|
|
393
|
+
| Key | Where it shows |
|
|
394
|
+
|---|---|
|
|
395
|
+
| `bannerTitle` | The banner's heading |
|
|
396
|
+
| `bannerText` | The banner's paragraph |
|
|
397
|
+
| `panelTitle` | The settings panel's heading |
|
|
398
|
+
| `panelIntro` | The line under it |
|
|
399
|
+
| `extraTitle` | Heading of the «Additional information» block (defaults to a translated «Additional information» in all 32 languages) |
|
|
400
|
+
| `extraText` | Body of that block. **Empty in every dictionary** — the block renders only when you supply text |
|
|
401
|
+
| `cat.<name>.title` / `.desc` | One category's name and description, for `necessary`, `functional`, `analytics`, `marketing` |
|
|
402
|
+
|
|
403
|
+
Anything else — `acceptAll`, `more`, `save`, an unknown key — is ignored. The
|
|
404
|
+
button labels are what a visitor recognises across sites, and the plural tables
|
|
405
|
+
are arrays that a string override would break.
|
|
406
|
+
|
|
407
|
+
Values are merged in three layers, in this order, each winning over the one
|
|
408
|
+
before it:
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
built-in dictionary ← window.__ckLocales ← config.texts[lang]
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
For the last layer the exact resolved code is tried first, then its two-letter
|
|
415
|
+
base: a banner that resolved to `pt-br` reads `texts['pt-br']`, then
|
|
416
|
+
`texts['pt']`. **A non-empty string wins; an empty string, a missing key or a
|
|
417
|
+
non-string falls through to the layer below.** That is what makes an empty field
|
|
418
|
+
in an editor mean «use the standard text» rather than «show nothing».
|
|
419
|
+
|
|
420
|
+
Every value is treated as **text, never as HTML**, everywhere.
|
|
421
|
+
|
|
422
|
+
#### The markup subset for `extraText`
|
|
423
|
+
|
|
424
|
+
`extraText` is the one field with structure, because an operator block genuinely
|
|
425
|
+
is two or three paragraphs with an address and a link in them. The rules below
|
|
426
|
+
are the whole contract — the hosted service's validator mirrors them exactly,
|
|
427
|
+
so what the cabinet previews is what the banner paints:
|
|
428
|
+
|
|
429
|
+
1. **Paragraphs.** A blank line (two newlines) starts a new paragraph. Each
|
|
430
|
+
paragraph becomes one `<p>`.
|
|
431
|
+
2. **Line breaks.** A single newline inside a paragraph becomes a `<br>`.
|
|
432
|
+
3. **Three passes, in this exact order.** The order is part of the contract,
|
|
433
|
+
not an implementation detail:
|
|
434
|
+
1. **`[label](url)` links.** Tokenised first, and their pieces are never
|
|
435
|
+
seen by the later passes. This is what stops the bare-URL rule from
|
|
436
|
+
eating the address inside `[label](https://…)`, stops the bare-e-mail
|
|
437
|
+
rule from firing inside a `mailto:` label, and leaves a `**` inside a
|
|
438
|
+
link label literal.
|
|
439
|
+
2. **`**bold**`**, over the text between those links.
|
|
440
|
+
3. **Bare addresses**, inside each bold and each plain span: a bare
|
|
441
|
+
`https://` or `http://` URL, and a bare e-mail address, each becoming its
|
|
442
|
+
own label.
|
|
443
|
+
Bold must come *before* the bare addresses, not after: `**mail@example.md**`
|
|
444
|
+
is one bold run that happens to contain an address, and linking the address
|
|
445
|
+
first would split the run and leave the asterisks visible on screen.
|
|
446
|
+
4. **What the passes produce.** Every link — `[label](https://…)`,
|
|
447
|
+
`[label](mailto:…)`, a bare URL, a bare e-mail — is rendered with
|
|
448
|
+
`target="_blank" rel="noopener"`. `**bold**` becomes `<strong>`, and an
|
|
449
|
+
address inside it is bold *and* clickable. There is no nesting the other
|
|
450
|
+
way: `**` inside a `[label](…)` stays literal, because pass 1 removed the
|
|
451
|
+
whole link before pass 2 ran. An unclosed `**` is two literal asterisks,
|
|
452
|
+
never a bold tail that swallows the paragraph.
|
|
453
|
+
5. **Any other scheme is not a link.** `[x](javascript:…)`, `data:`, `file:` —
|
|
454
|
+
the *whole literal* `[x](javascript:…)` is rendered as plain text, so a
|
|
455
|
+
mistake is visible to whoever wrote it rather than silently swallowed.
|
|
456
|
+
6. **No HTML.** `<b>` is four characters of text. Nothing in this path goes
|
|
457
|
+
through `innerHTML`; the block is built with `createElement` and
|
|
458
|
+
`createTextNode` only. The hosted service refuses `<` in these fields
|
|
459
|
+
outright, at validation time.
|
|
460
|
+
|
|
461
|
+
The block is drawn after the categories and their service lists, before the
|
|
462
|
+
panel's buttons, and only when `extraText` resolves to a non-empty string.
|
|
463
|
+
|
|
464
|
+
#### `texts.links` — your own links
|
|
465
|
+
|
|
466
|
+
Up to three, rendered as a row under the banner's buttons (in all three
|
|
467
|
+
layouts) and at the foot of the settings panel:
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
texts: {
|
|
471
|
+
links: [
|
|
472
|
+
{ id: 'privacy', url: 'https://shop.md/privacy',
|
|
473
|
+
label: { ru: 'Политика конфиденциальности', ro: 'Politica de confidențialitate', en: 'Privacy policy' } },
|
|
474
|
+
{ id: 'cookies', url: 'https://shop.md/cookies',
|
|
475
|
+
label: { ru: 'Политика cookie', ro: 'Politica cookie', en: 'Cookie policy' } }
|
|
476
|
+
]
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
`url` must be `http(s)`; anything else is skipped. `label` is resolved with the
|
|
481
|
+
same fallback chain as `branding.poweredBy.texts` — exact code, then the
|
|
482
|
+
two-letter base, then `en` — and a row whose label resolves to nothing is
|
|
483
|
+
skipped rather than rendered blank. The cap of three is applied to the rows that
|
|
484
|
+
*survive* those checks, so one malformed entry never costs a good one its place.
|
|
485
|
+
|
|
486
|
+
This does not touch `detailsAction`, with one exception that keeps the banner
|
|
487
|
+
from printing the same address twice: when `detailsAction` resolves to `policy`
|
|
488
|
+
or `declaration` and its URL matches one of the links on screen — compared
|
|
489
|
+
trimmed, with a case-insensitive host and any trailing slash ignored — the
|
|
490
|
+
in-text «Learn more» link is not rendered and the banner behaves as
|
|
491
|
+
`detailsAction: "hide"` for that render, leaving the address to the link row,
|
|
492
|
+
which also carries your own label for it. An explicit `detailsAction:
|
|
493
|
+
"settings"` is unaffected: it opens the panel and has no address to duplicate.
|
|
494
|
+
|
|
495
|
+
#### A worked example
|
|
496
|
+
|
|
497
|
+
```json
|
|
498
|
+
{
|
|
499
|
+
"language": "auto",
|
|
500
|
+
"texts": {
|
|
501
|
+
"policyUrl": "https://shop.md/privacy",
|
|
502
|
+
"ru": {
|
|
503
|
+
"bannerTitle": "Cookie на сайте INTERSTEPCOM",
|
|
504
|
+
"extraText": "Оператор: «FIRM» SRL, IDNO 1234567890123, мун. Кишинёв, ул. Примерная 1.\n\nПо вопросам обработки персональных данных пишите на **office@firm.md** — отвечаем не позднее одного месяца (ст. 12 ч. (3) Закона № 195/2024).\n\nВы вправе подать жалобу в [Национальный центр по защите персональных данных](https://datepersonale.md)."
|
|
505
|
+
},
|
|
506
|
+
"links": [
|
|
507
|
+
{ "id": "privacy", "url": "https://shop.md/privacy",
|
|
508
|
+
"label": { "ru": "Политика конфиденциальности", "ro": "Politica de confidențialitate" } },
|
|
509
|
+
{ "id": "cookies", "url": "https://shop.md/cookies",
|
|
510
|
+
"label": { "ru": "Политика cookie", "ro": "Politica cookie" } }
|
|
511
|
+
]
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
A Russian visitor sees the custom banner title, and a panel whose «Дополнительно»
|
|
517
|
+
card carries three paragraphs: the operator's details, a bold auto-linked
|
|
518
|
+
address with the statutory answering period, and a link to the supervisory
|
|
519
|
+
authority. A Romanian visitor sees the standard Romanian banner title (nothing
|
|
520
|
+
was overridden for `ro`), no «Informații suplimentare» card (no `ro.extraText`),
|
|
521
|
+
and both links under the buttons in Romanian.
|
|
522
|
+
|
|
370
523
|
### Reopening the settings
|
|
371
524
|
|
|
372
525
|
`ConsentKit.openSettings()` opens the preferences panel from anywhere on the
|
|
@@ -819,19 +972,19 @@ external requests. Rebuild them with `tools/build-inline.mjs` (see
|
|
|
819
972
|
[`tools/README.md`](https://github.com/vermoh/ConsentKit/blob/main/tools/README.md)); each block's header records the exact
|
|
820
973
|
command that produced it.
|
|
821
974
|
|
|
822
|
-
ConsentKit 0.5.
|
|
975
|
+
ConsentKit 0.5.15, rebuilt 2026-09-07, uncompressed — gzip on the server cuts
|
|
823
976
|
this roughly threefold. Every block includes the branding extension and the
|
|
824
977
|
attribution line; `--no-branding` drops both the code and the config and takes
|
|
825
978
|
**~26 KB** back off:
|
|
826
979
|
|
|
827
980
|
| Block | Languages | Bytes | gzip | `--no-branding` |
|
|
828
981
|
|---|---|---|---|---|
|
|
829
|
-
| `ready/en-bar.txt` | en |
|
|
830
|
-
| `ready/ru-bar.txt` | ru, ro, en |
|
|
831
|
-
| `ready/ru-box.txt` | ru, ro, en |
|
|
832
|
-
| `ready/ru-box-right.txt` | ru, ro, en |
|
|
833
|
-
| `ready/ru-modal.txt` | ru, ro, en |
|
|
834
|
-
| `ready/eu-bar.txt` | 34 languages |
|
|
982
|
+
| `ready/en-bar.txt` | en | 275,509 | 86,249 | 249,096 |
|
|
983
|
+
| `ready/ru-bar.txt` | ru, ro, en | 277,578 | 87,146 | 250,979 |
|
|
984
|
+
| `ready/ru-box.txt` | ru, ro, en | 277,593 | 87,156 | 250,994 |
|
|
985
|
+
| `ready/ru-box-right.txt` | ru, ro, en | 277,602 | 87,161 | 250,997 |
|
|
986
|
+
| `ready/ru-modal.txt` | ru, ro, en | 277,586 | 87,153 | 250,985 |
|
|
987
|
+
| `ready/eu-bar.txt` | 34 languages | 326,232 | 105,534 | 299,653 |
|
|
835
988
|
|
|
836
989
|
The blocks are dominated by the core and the UI (roughly 97 KB and 129 KB of
|
|
837
990
|
source respectively, comments included — the builder concatenates the sources
|
|
@@ -1023,6 +1176,47 @@ Client versions. The WordPress plugin tracks the same numbers and keeps its own
|
|
|
1023
1176
|
notes in
|
|
1024
1177
|
[`plugins/wordpress/consentkit/readme.txt`](https://github.com/vermoh/ConsentKit/blob/main/plugins/wordpress/consentkit/readme.txt).
|
|
1025
1178
|
|
|
1179
|
+
### 0.5.15
|
|
1180
|
+
|
|
1181
|
+
- **Your own texts, per language.** `texts.<lang>` overrides the banner title
|
|
1182
|
+
and copy, the panel title and intro, and any category's title or description,
|
|
1183
|
+
for one language at a time. An empty field means «take the standard text», so
|
|
1184
|
+
a partly filled form is not a partly blank banner — see
|
|
1185
|
+
[Custom texts and links](#custom-texts-and-links).
|
|
1186
|
+
- **An «Additional information» block in the settings panel.** `texts.<lang>.extraText`
|
|
1187
|
+
renders as a card under the categories: who the operator is, where to write,
|
|
1188
|
+
how long an answer takes, where to complain. It accepts a small, precisely
|
|
1189
|
+
defined markup subset — paragraphs, `**bold**`, links and auto-linked e-mail
|
|
1190
|
+
addresses — and no HTML at all: the block is built with `createElement` and
|
|
1191
|
+
`createTextNode`, never `innerHTML`.
|
|
1192
|
+
- **Up to three of your own links under the banner buttons** and at the foot of
|
|
1193
|
+
the settings panel, via `texts.links`. Each carries a per-language label, is
|
|
1194
|
+
`http(s)`-only, and opens with `target="_blank" rel="noopener"`. «Learn more»
|
|
1195
|
+
is unaffected: a site that adds links keeps whatever `detailsAction` already
|
|
1196
|
+
did — except that the in-text link is dropped when it would repeat an address
|
|
1197
|
+
already on screen in the link row, so the same page is never linked twice.
|
|
1198
|
+
- **The debug panel reports which language got overrides**, so «I filled in the
|
|
1199
|
+
text and the banner still shows the standard one» has an answer on screen —
|
|
1200
|
+
usually that the override is filed under a language code the banner did not
|
|
1201
|
+
resolve to.
|
|
1202
|
+
|
|
1203
|
+
### 0.5.14
|
|
1204
|
+
|
|
1205
|
+
- **The loader tolerates a second snippet with a dead site id.** A page that
|
|
1206
|
+
carries two `data-ck-id` blocks — a migration that left the old one in place —
|
|
1207
|
+
is driven by the snippet whose config loads; the failing one only warns. The
|
|
1208
|
+
strict fallback is raised solely when *every* snippet fails, never while
|
|
1209
|
+
another is still in flight. The same id twice (Tilda duplicates the head
|
|
1210
|
+
block) initialises once and warns about nothing.
|
|
1211
|
+
- **A second copy of the script on the same page no longer replaces the first.**
|
|
1212
|
+
Each snippet loads the whole bundle, so a two-snippet page ran the client
|
|
1213
|
+
twice: the second core published a fresh, uninitialised engine over
|
|
1214
|
+
`window.ConsentKit` and the second UI layer mounted its own banner, leaving
|
|
1215
|
+
the real config applied to an engine nothing pointed at any more — a banner
|
|
1216
|
+
drawn from the built-in defaults. The second copy now stands down: the first
|
|
1217
|
+
engine keeps its state, its DOM patches and its observer, and the page mounts
|
|
1218
|
+
exactly one banner.
|
|
1219
|
+
|
|
1026
1220
|
### 0.5.13
|
|
1027
1221
|
|
|
1028
1222
|
- Host database: `fbcdn.net` → marketing (Facebook SDK chunks and plugin images); `aichat.md`, `bubble.aichat.md` → functional (chat widget); `challenges.cloudflare.com` (Cloudflare challenge / Turnstile) and `i.imgur.com` → infrastructure, never held.
|
|
@@ -1143,7 +1337,7 @@ notes in
|
|
|
1143
1337
|
|
|
1144
1338
|
## Project status
|
|
1145
1339
|
|
|
1146
|
-
**This is a prototype (v0.5.
|
|
1340
|
+
**This is a prototype (v0.5.15), not a released product.** It is honest about
|
|
1147
1341
|
what has been verified and what has not.
|
|
1148
1342
|
|
|
1149
1343
|
### Verified
|
package/npm/index.d.ts
CHANGED
|
@@ -168,6 +168,40 @@ export interface CkTextsConfig {
|
|
|
168
168
|
* service; the client only reads it.
|
|
169
169
|
*/
|
|
170
170
|
declarationUrl?: string;
|
|
171
|
+
/**
|
|
172
|
+
* v0.5.15. Up to three links rendered under the banner buttons and at the
|
|
173
|
+
* bottom of the preferences panel. `url` is `http(s)` only; `label` is
|
|
174
|
+
* resolved per language the way `branding.poweredBy.texts` is. A link whose
|
|
175
|
+
* address equals the «Learn more» target hides the in-text link.
|
|
176
|
+
*/
|
|
177
|
+
links?: CkTextLink[];
|
|
178
|
+
/**
|
|
179
|
+
* v0.5.15. Per-language overrides of the built-in dictionary, keyed by a
|
|
180
|
+
* language code (`ru`, `ro`, `en`, `pt-br`, …). An empty string means «use
|
|
181
|
+
* the dictionary». `extraText` accepts the rich-text subset documented in
|
|
182
|
+
* the README («Custom texts and links»); it is never interpreted as HTML.
|
|
183
|
+
*/
|
|
184
|
+
[lang: string]: CkTextOverrides | CkTextLink[] | string | undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** v0.5.15. One link under the banner buttons. */
|
|
188
|
+
export interface CkTextLink {
|
|
189
|
+
id: string;
|
|
190
|
+
url: string;
|
|
191
|
+
label: Record<string, string>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** v0.5.15. The dictionary keys a site may override for one language. */
|
|
195
|
+
export interface CkTextOverrides {
|
|
196
|
+
bannerTitle?: string;
|
|
197
|
+
bannerText?: string;
|
|
198
|
+
panelTitle?: string;
|
|
199
|
+
panelIntro?: string;
|
|
200
|
+
/** Heading of the «Additional information» block in the preferences panel. */
|
|
201
|
+
extraTitle?: string;
|
|
202
|
+
/** Body of that block; rendered only when non-empty. Rich-text subset. */
|
|
203
|
+
extraText?: string;
|
|
204
|
+
cat?: Partial<Record<'necessary' | 'functional' | 'analytics' | 'marketing', { title?: string; desc?: string }>>;
|
|
171
205
|
}
|
|
172
206
|
|
|
173
207
|
/** Whether a category is offered in the preferences panel at all. */
|
|
@@ -421,7 +455,7 @@ export interface CkContrastApi {
|
|
|
421
455
|
resolveFont(theme: CkThemeConfig): string;
|
|
422
456
|
/** Takes the whole config, not just `texts`. */
|
|
423
457
|
resolveDetails(config: CkConfig):
|
|
424
|
-
{ kind: 'policy' | 'settings' | 'hide'; href: string | null };
|
|
458
|
+
{ kind: 'policy' | 'settings' | 'hide' | 'declaration'; href: string | null };
|
|
425
459
|
/** Takes the whole config. Returns the generated stylesheet and both palettes. */
|
|
426
460
|
buildThemeCss(config: CkConfig): {
|
|
427
461
|
css: string;
|
package/npm/internal-stub.mjs
CHANGED
|
@@ -28,7 +28,7 @@ export function undecidedState() {
|
|
|
28
28
|
*/
|
|
29
29
|
export function createStub() {
|
|
30
30
|
const stub = {
|
|
31
|
-
version: '0.5.
|
|
31
|
+
version: '0.5.15',
|
|
32
32
|
config: {},
|
|
33
33
|
init: function () { return undecidedState(); },
|
|
34
34
|
allowed: function (cat) { return cat === 'necessary'; },
|
package/package.json
CHANGED
package/src/ck-core.js
CHANGED
|
@@ -9,6 +9,29 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
if (!global) { return; }
|
|
12
|
+
|
|
13
|
+
/* SPEC §1.9 — a second copy of ck.js on the page stands down.
|
|
14
|
+
A page can carry two snippets: an old page-level block with a dead site id
|
|
15
|
+
that nobody ever removed, plus the site-wide one. Each <script> loads the
|
|
16
|
+
WHOLE bundle, so this file runs twice — and without this guard the second
|
|
17
|
+
run would publish a fresh, uninitialised engine over `window.ConsentKit`.
|
|
18
|
+
Everything the first engine did stays behind on an object nothing points at
|
|
19
|
+
any more: its consent state, and the loader that is mid-fetch holding a
|
|
20
|
+
`CK` reference to it. The page would then end on the default config with a
|
|
21
|
+
banner drawn from default texts, while the real config was applied to the
|
|
22
|
+
orphan.
|
|
23
|
+
Standing down here is also what the two loaders' arbitration in ck-saas.js
|
|
24
|
+
assumes: `__ckSaas` coordinates them on the premise that both are talking
|
|
25
|
+
to the SAME core, so `shared.done` genuinely means "this page is
|
|
26
|
+
initialised" rather than "some object was initialised".
|
|
27
|
+
The patches, the MutationObserver and the initial scan are already live
|
|
28
|
+
from the first run; re-installing them would double every interception.
|
|
29
|
+
`init` is the test rather than mere presence, because ck-ui-branding.js
|
|
30
|
+
pre-creates a bare `window.ConsentKit = {}` when it happens to load first.
|
|
31
|
+
That object has no `init`, so we fall through and publish over it exactly
|
|
32
|
+
as before. */
|
|
33
|
+
if (global.ConsentKit && typeof global.ConsentKit.init === 'function') { return; }
|
|
34
|
+
|
|
12
35
|
var doc = global.document;
|
|
13
36
|
|
|
14
37
|
// ---------------------------------------------------------------------------
|
|
@@ -1978,7 +2001,7 @@
|
|
|
1978
2001
|
// Public API
|
|
1979
2002
|
// ---------------------------------------------------------------------------
|
|
1980
2003
|
var ConsentKit = {
|
|
1981
|
-
version: '0.5.
|
|
2004
|
+
version: '0.5.15',
|
|
1982
2005
|
config: config,
|
|
1983
2006
|
|
|
1984
2007
|
init: function (userConfig) {
|
package/src/ck-debug.js
CHANGED
|
@@ -272,6 +272,7 @@
|
|
|
272
272
|
source: 'источник',
|
|
273
273
|
srcSaas: 'SaaS',
|
|
274
274
|
srcInline: 'инлайн',
|
|
275
|
+
twoSnippets: 'два снипета',
|
|
275
276
|
secConsent: 'Согласие',
|
|
276
277
|
decidedAt: 'решение',
|
|
277
278
|
method: 'способ',
|
|
@@ -311,6 +312,12 @@
|
|
|
311
312
|
themeCard: 'карточка',
|
|
312
313
|
themeBtn: 'кнопки',
|
|
313
314
|
themeLink: 'Ссылки',
|
|
315
|
+
// SPEC V1.16 §1.4 — какой язык получил свои тексты из конфига. Отвечает
|
|
316
|
+
// на вопрос «почему на баннере не тот текст, который я вписал»: чаще
|
|
317
|
+
// всего потому, что переопределение записано под другим кодом языка.
|
|
318
|
+
textsOverride: 'Тексты',
|
|
319
|
+
textsFor: 'переопределения для ',
|
|
320
|
+
textsNone: 'нет',
|
|
314
321
|
btnAccept: 'Принять всё',
|
|
315
322
|
btnReject: 'Отклонить всё',
|
|
316
323
|
btnSettings: 'Настроить',
|
|
@@ -348,6 +355,7 @@
|
|
|
348
355
|
source: 'source',
|
|
349
356
|
srcSaas: 'SaaS',
|
|
350
357
|
srcInline: 'inline',
|
|
358
|
+
twoSnippets: 'two snippets',
|
|
351
359
|
secConsent: 'Consent',
|
|
352
360
|
decidedAt: 'decided',
|
|
353
361
|
method: 'method',
|
|
@@ -385,6 +393,9 @@
|
|
|
385
393
|
themeCard: 'card',
|
|
386
394
|
themeBtn: 'buttons',
|
|
387
395
|
themeLink: 'Links',
|
|
396
|
+
textsOverride: 'Texts',
|
|
397
|
+
textsFor: 'overrides for ',
|
|
398
|
+
textsNone: 'none',
|
|
388
399
|
btnAccept: 'Accept all',
|
|
389
400
|
btnReject: 'Reject all',
|
|
390
401
|
btnSettings: 'Customize',
|
|
@@ -423,6 +434,7 @@
|
|
|
423
434
|
source: 'sursă',
|
|
424
435
|
srcSaas: 'SaaS',
|
|
425
436
|
srcInline: 'inline',
|
|
437
|
+
twoSnippets: 'două fragmente',
|
|
426
438
|
secConsent: 'Consimțământ',
|
|
427
439
|
decidedAt: 'decizie',
|
|
428
440
|
method: 'mod',
|
|
@@ -460,6 +472,9 @@
|
|
|
460
472
|
themeCard: 'card',
|
|
461
473
|
themeBtn: 'butoane',
|
|
462
474
|
themeLink: 'Linkuri',
|
|
475
|
+
textsOverride: 'Texte',
|
|
476
|
+
textsFor: 'suprascrieri pentru ',
|
|
477
|
+
textsNone: 'niciuna',
|
|
463
478
|
btnAccept: 'Acceptă tot',
|
|
464
479
|
btnReject: 'Respinge tot',
|
|
465
480
|
btnSettings: 'Personalizează',
|
|
@@ -850,6 +865,45 @@
|
|
|
850
865
|
: 'inherit (' + T.themeFontInherit + tries + ')';
|
|
851
866
|
}
|
|
852
867
|
|
|
868
|
+
/* SPEC V1.16 §1.4 — «Тексты: переопределения для ru» / «нет».
|
|
869
|
+
|
|
870
|
+
The question this answers is «I filled the banner text in the cabinet and
|
|
871
|
+
the banner still shows the standard one». Nine times in ten the answer is
|
|
872
|
+
that the override is filed under a language code the banner did not resolve
|
|
873
|
+
to — so the row reports the RESOLVED code, not the configured one, and says
|
|
874
|
+
which of the two lookups (exact, then two-letter base) actually hit.
|
|
875
|
+
|
|
876
|
+
The resolution is asked of ck-ui through _contrast rather than repeated
|
|
877
|
+
here: «один код — одни числа», the same rule the Appearance section above
|
|
878
|
+
already follows. With ck-ui absent (a core-only page) there is no banner
|
|
879
|
+
language to report and the row reads «нет». */
|
|
880
|
+
function textsRow(T) {
|
|
881
|
+
var cfg = (CK && CK.config) || {};
|
|
882
|
+
var texts = (cfg.texts && typeof cfg.texts === 'object' && !Array.isArray(cfg.texts))
|
|
883
|
+
? cfg.texts : null;
|
|
884
|
+
if (!texts) return T.textsNone;
|
|
885
|
+
var lang = '';
|
|
886
|
+
try {
|
|
887
|
+
var C = CK && CK._contrast;
|
|
888
|
+
if (C && typeof C.resolveLang === 'function' && typeof C.localeTable === 'function') {
|
|
889
|
+
lang = String(C.resolveLang(cfg.language, C.localeTable()) || '').toLowerCase();
|
|
890
|
+
}
|
|
891
|
+
} catch (e) { lang = ''; }
|
|
892
|
+
if (!lang) return T.textsNone;
|
|
893
|
+
|
|
894
|
+
function has(k) {
|
|
895
|
+
return !!(k && /^[a-z]{2}(-[a-z]{2})?$/.test(k) &&
|
|
896
|
+
Object.prototype.hasOwnProperty.call(texts, k) &&
|
|
897
|
+
texts[k] && typeof texts[k] === 'object' && !Array.isArray(texts[k]));
|
|
898
|
+
}
|
|
899
|
+
// The exact code first, then its two-letter base — the same order
|
|
900
|
+
// buildStrings() merges them in, so the row names the key that actually won.
|
|
901
|
+
if (has(lang)) return T.textsFor + lang;
|
|
902
|
+
var base = lang.slice(0, 2);
|
|
903
|
+
if (base !== lang && has(base)) return T.textsFor + base;
|
|
904
|
+
return T.textsNone;
|
|
905
|
+
}
|
|
906
|
+
|
|
853
907
|
// Resolved lazily, not at parse time: this file runs before ConsentKit.init()
|
|
854
908
|
// has merged the site's config, so asking for the language now would always
|
|
855
909
|
// read the built-in default. Re-resolved on every render so a page that
|
|
@@ -910,14 +964,26 @@
|
|
|
910
964
|
body.textContent = '';
|
|
911
965
|
|
|
912
966
|
// 1. Client
|
|
967
|
+
/* Read straight off _saas rather than through buildReport(): more than one
|
|
968
|
+
snippet on a page is a fact about the page, not about the report, and it
|
|
969
|
+
is worth surfacing because the symptom it explains — a banner configured
|
|
970
|
+
by an id the owner did not expect — is otherwise invisible. */
|
|
971
|
+
var siteIds = null;
|
|
972
|
+
try {
|
|
973
|
+
var ids = CK && CK._saas && CK._saas.siteIds;
|
|
974
|
+
if (ids && ids.length > 1) { siteIds = ids.join(', '); }
|
|
975
|
+
} catch (e) { siteIds = null; }
|
|
976
|
+
|
|
913
977
|
var s1 = section(T.secClient);
|
|
914
|
-
|
|
978
|
+
var rows1 = [
|
|
915
979
|
[T.version, r.client.version],
|
|
916
980
|
[T.source, r.client.source === 'saas' ? T.srcSaas : T.srcInline],
|
|
917
|
-
['siteId', r.client.siteId]
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
])
|
|
981
|
+
['siteId', r.client.siteId]
|
|
982
|
+
];
|
|
983
|
+
if (siteIds) { rows1.push([T.twoSnippets, siteIds]); }
|
|
984
|
+
rows1.push(['policyVersion', r.client.policyVersion]);
|
|
985
|
+
rows1.push(['ETag', r.client.etag]);
|
|
986
|
+
s1.appendChild(defs(rows1));
|
|
921
987
|
body.appendChild(s1);
|
|
922
988
|
|
|
923
989
|
// 2. Consent
|
|
@@ -1061,7 +1127,9 @@
|
|
|
1061
1127
|
[T.themeFont, fontRow(built, T)],
|
|
1062
1128
|
[T.themeRadius, T.themeCard + ' ' + built.radius.card + 'px · ' +
|
|
1063
1129
|
T.themeBtn + ' ' + built.radius.button + 'px'],
|
|
1064
|
-
[T.themeLink, lkTxt]
|
|
1130
|
+
[T.themeLink, lkTxt],
|
|
1131
|
+
// SPEC V1.16 §1.4 — «Тексты: переопределения для ru» / «нет».
|
|
1132
|
+
[T.textsOverride, textsRow(T)]
|
|
1065
1133
|
]));
|
|
1066
1134
|
|
|
1067
1135
|
var labels = { accept: T.btnAccept, reject: T.btnReject, settings: T.btnSettings };
|