umberto 10.5.0 → 10.6.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/CHANGELOG.md CHANGED
@@ -1,51 +1,47 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- ## [10.5.0](https://github.com/cksource/umberto/compare/v10.4.1...v10.5.0) (April 14, 2026)
4
+ ## [10.6.0](https://github.com/cksource/umberto/compare/v10.5.1...v10.6.0) (June 8, 2026)
5
5
 
6
6
  ### Features
7
7
 
8
- * Added the Call for feedback component for documentation pages.
8
+ * Improved rendering of complex TypeScript types in API docs.
9
9
 
10
+ Intersection (`A & B`), tuple (`[ key: T, value: U ]`), and conditional (`T extends U ? X : Y`) types now render with their full structure preserved and each reference linkable, instead of collapsing to a generic `object` or `tuple` placeholder. Optional tuple elements display the trailing `?` from the source. The `infer X` keyword inside conditionals renders as such and no longer triggers "unsupported type" warnings during the docs build.
10
11
 
11
- ## [10.4.1](https://github.com/cksource/umberto/compare/v10.4.0...v10.4.1) (April 13, 2026)
12
+ Properties tables on intersection type aliases now list the fields from the inline-literal half — including nested cases like `abc: A & { b: T }`, which now exposes `abc.b` as a sub-row, and cases where the inline literal is wrapped in `Partial<...>`, `Readonly<...>`, or `Required<...>`, where the wrapper's modifier is applied to each field. Per-field inline TSDoc comments (`/** ... */`) above each field render their descriptions in the Properties table, where they were previously dropped.
12
13
 
13
- ### Bug fixes
14
+ ### Other changes
14
15
 
15
- * Fixed URL in the dropdown for LTS item to have absolute URL to pass documentation validation.
16
+ * Updated the GTM integration to use the new server-side setup in both Gloria and legacy Umberto themes.
16
17
 
17
18
 
18
- ## [10.4.0](https://github.com/cksource/umberto/compare/v10.3.0...v10.4.0) (April 9, 2026)
19
+ ## [10.5.1](https://github.com/cksource/umberto/compare/v10.5.0...v10.5.1) (May 14, 2026)
19
20
 
20
- ### Features
21
+ ### Bug fixes
21
22
 
22
- * Added the LTS edition to the docs product picker.
23
+ * Changed the call for feedback title from `<h3>` to `<div>` to prevent it from appearing in the table of contents.
23
24
 
24
25
 
25
- ## [10.3.0](https://github.com/cksource/umberto/compare/v10.2.0...v10.3.0) (April 3, 2026)
26
+ ## [10.5.0](https://github.com/cksource/umberto/compare/v10.4.1...v10.5.0) (April 14, 2026)
26
27
 
27
28
  ### Features
28
29
 
29
- * Added support for displaying and filtering experimental API items in TypeDoc-based API docs.
30
+ * Added the Call for feedback component for documentation pages.
30
31
 
31
- Experimental annotations now appear in page headers and member listings, and can be toggled with the API filters.
32
32
 
33
+ ## [10.4.1](https://github.com/cksource/umberto/compare/v10.4.0...v10.4.1) (April 13, 2026)
33
34
 
34
- ## [10.2.0](https://github.com/cksource/umberto/compare/v10.1.4...v10.2.0) (April 2, 2026)
35
+ ### Bug fixes
35
36
 
36
- ### Features
37
+ * Fixed URL in the dropdown for LTS item to have absolute URL to pass documentation validation.
37
38
 
38
- * Added support for configuring Kapa AI source groups via the `sourceGroupIds` option. The option can be set globally in `kapa.default` or per project to filter the widget's knowledge base to specific source groups.
39
39
 
40
- ### Bug fixes
41
-
42
- * Fixed a console error thrown when hovering over a CKEditor bookmark element. The `links-prefetch` module now silently ignores anchor elements with no `href` instead of calling `new URL( '' )` and logging a spurious error.
40
+ ## [10.4.0](https://github.com/cksource/umberto/compare/v10.3.0...v10.4.0) (April 9, 2026)
43
41
 
44
- ### Other changes
42
+ ### Features
45
43
 
46
- * Removed the legacy warning banner.
47
- * Added cyan LTS banner at the top of the page on LTS branch.
48
- * Updated the colour of nightly banner to yellow.
44
+ * Added the LTS edition to the docs product picker.
49
45
 
50
46
  ---
51
47
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umberto",
3
- "version": "10.5.0",
3
+ "version": "10.6.0",
4
4
  "description": "CKSource Documentation builder",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "dependencies": {
18
18
  "@babel/core": "^7.18.10",
19
- "@babel/preset-env": "^7.18.10",
19
+ "@babel/preset-env": "^7.29.5",
20
20
  "@minify-html/node": "^0.17.1",
21
21
  "@vscode/markdown-it-katex": "^1.1.2",
22
22
  "babel-loader": "^10.0.0",
@@ -265,7 +265,11 @@ export class TypedocConverter {
265
265
  response.defaultvalue = item.defaultValue;
266
266
  }
267
267
 
268
- const nestedProperties = this._convertProperties( item );
268
+ // Parameter tables expand inline object literals one level only and skip intersection
269
+ // fan-out: TypeDoc materializes mapped types like `{ [K in keyof this]?: unknown }` into
270
+ // a reflection containing every property of the host class, which would otherwise produce
271
+ // dozens of fake `param.*` sub-rows.
272
+ const nestedProperties = this._convertProperties( item, { maxDepth: 0, deepIntersections: false } );
269
273
 
270
274
  if ( !nestedProperties ) {
271
275
  return response;
@@ -285,9 +289,20 @@ export class TypedocConverter {
285
289
  /**
286
290
  * @protected
287
291
  * @param {CallSignatureReflection} signature
292
+ * @param {Object} [options]
293
+ * @param {Number} [options.maxDepth=Infinity] How many additional levels of nested inline object
294
+ * literals to expand under each child. `0` means "do not recurse; emit only the immediate
295
+ * children of `signature.type`". Default `Infinity` is unbounded (terminates naturally at
296
+ * intrinsics) — used for type-alias property tables so nested intersections/inline literals
297
+ * are flattened to dotted names.
298
+ * @param {Boolean} [options.deepIntersections=true] Whether to fan out intersection members the
299
+ * way unions are fanned out. Type-alias property tables want this; function-parameter tables
300
+ * set it to `false` because TypeDoc materializes mapped types like `{ [K in keyof this]?: unknown }`
301
+ * into reflections containing every property of the host class — fanning into them would
302
+ * produce dozens of misleading sub-rows on parameters such as `values: object & ...`.
288
303
  * @returns {Array|null}
289
304
  */
290
- _convertProperties( signature ) {
305
+ _convertProperties( signature, { maxDepth = Infinity, deepIntersections = true } = {} ) {
291
306
  let types;
292
307
 
293
308
  // Support for a literal object (`const foo: { /* object literal */ } = ...`).
@@ -302,7 +317,10 @@ export class TypedocConverter {
302
317
  }
303
318
  }
304
319
  ];
320
+ } else if ( deepIntersections ) {
321
+ types = flattenCompositeTypes( signature.type );
305
322
  } else {
323
+ // Fan out unions only; intersections stay opaque so each member is treated as a whole.
306
324
  types = signature.type.type === 'union' ? signature.type.types : [ signature.type ];
307
325
  }
308
326
 
@@ -315,7 +333,7 @@ export class TypedocConverter {
315
333
  ];
316
334
  } );
317
335
 
318
- return properties.map( child => {
336
+ return properties.flatMap( child => {
319
337
  // Nested properties cannot be duplicated so we can take the first signature when processing functions.
320
338
  const childSignature = child.kind === ReflectionKind.Method ? child.signatures[ 0 ] : child;
321
339
  const response = this._convertTypeToJsDoc( childSignature );
@@ -326,14 +344,17 @@ export class TypedocConverter {
326
344
  response.extraId = `member-${ childSignature.name }`;
327
345
  response.name = TypedocConverter.getComputedName( childSignature );
328
346
 
329
- if ( signature.comment && signature.comment.blockTags ) {
330
- const atProperty = signature.comment.blockTags.find( blockTag => {
331
- return blockTag.tag === '@property' && blockTag.name === childSignature.name;
332
- } );
347
+ // Prefer a matching `@property` block tag on the parent type alias; otherwise fall back to
348
+ // the child reflection's own comment summary (inline `/** ... */` on each field).
349
+ //
350
+ // `blockTags` is an empty array (not absent) when TypeDoc parses a comment with only a summary,
351
+ // so a plain truthy check would always pass and the fallback would be unreachable.
352
+ const atProperty = signature.comment?.blockTags?.find( blockTag => {
353
+ return blockTag.tag === '@property' && blockTag.name === childSignature.name;
354
+ } );
333
355
 
334
- if ( atProperty ) {
335
- response.description = TypedocConverter.convertComment( atProperty.content );
336
- }
356
+ if ( atProperty ) {
357
+ response.description = TypedocConverter.convertComment( atProperty.content );
337
358
  } else if ( childSignature.comment && childSignature.comment.summary ) {
338
359
  response.description = TypedocConverter.convertComment( childSignature.comment.summary );
339
360
  }
@@ -342,11 +363,34 @@ export class TypedocConverter {
342
363
  response.optional = true;
343
364
  }
344
365
 
366
+ if ( childSignature.flags.isReadonly ) {
367
+ response.readonly = true;
368
+ }
369
+
345
370
  if ( childSignature.defaultValue ) {
346
371
  response.defaultvalue = childSignature.defaultValue;
347
372
  }
348
373
 
349
- return response;
374
+ // If the property's type is itself an inline object literal (directly, or as part of an
375
+ // intersection/union), expose its fields as nested rows — mirrors how `_convertParameters`
376
+ // flattens nested object literals in function parameters. The intersection-walking flag is
377
+ // propagated so callers that disable it (parameters) stay consistent through recursion.
378
+ const nestedProperties = maxDepth > 0 ?
379
+ this._convertProperties( childSignature, { maxDepth: maxDepth - 1, deepIntersections } ) :
380
+ null;
381
+
382
+ if ( !nestedProperties || !nestedProperties.length ) {
383
+ return response;
384
+ }
385
+
386
+ return [
387
+ response,
388
+ ...nestedProperties.map( nestedProperty => {
389
+ nestedProperty.name = `${ childSignature.name }.${ nestedProperty.name }`;
390
+
391
+ return nestedProperty;
392
+ } )
393
+ ];
350
394
  } );
351
395
  }
352
396
 
@@ -553,7 +597,12 @@ class TypeConverter {
553
597
  };
554
598
  }
555
599
 
556
- if ( parameter.default ) {
600
+ // The `type += ` = default `` concatenation below assumes `type` is a string.
601
+ // When the constraint is a structured value (intersection, conditional, tuple, generic,
602
+ // function, predicate, operator, raw) or an array (`Array<X>` sugar), `+=` coerces it
603
+ // via `toString()` — producing `[object Object] = ...` or stripping the `Array<>` wrapper.
604
+ // Skip the default in those cases; the constraint is still rendered.
605
+ if ( parameter.default && typeof type === 'string' ) {
557
606
  type += ` = ${ this.convertIntrinsicType( parameter.default ) }`;
558
607
  }
559
608
 
@@ -729,20 +778,50 @@ class TypeConverter {
729
778
  return 'object';
730
779
  }
731
780
 
732
- // A structure represented by an array.
781
+ // A structure represented by an array. The position and label of each element matter.
782
+ // E.g.: `[ number, string ]`, `[ key: number, value: string ]`, or `[ first: string, second?: number ]`.
783
+ // Optional elements appear in two forms in TypeDoc:
784
+ // * `[ key?: T ]` — `namedTupleMember` with `isOptional`.
785
+ // * `( T )?` — an `optional` wrapper around the element type.
733
786
  if ( typeReflection.type === 'tuple' ) {
734
- // It is an array with mixed types.
735
- // The position of these types matter.
736
- return 'tuple';
787
+ return {
788
+ type: 'tuple',
789
+ elements: ( typeReflection.elements || [] ).map( element => {
790
+ if ( element.type === 'namedTupleMember' ) {
791
+ const result = { name: element.name, value: this.convert( element.element ) };
792
+
793
+ if ( element.isOptional ) {
794
+ result.optional = true;
795
+ }
796
+
797
+ return result;
798
+ }
799
+
800
+ if ( element.type === 'optional' ) {
801
+ return { value: this.convert( element.elementType ), optional: true };
802
+ }
803
+
804
+ return { value: this.convert( element ) };
805
+ } )
806
+ };
737
807
  }
738
808
 
809
+ // A namedTupleMember reached outside a tuple context (defensive — TS does not allow this at top level).
739
810
  if ( typeReflection.type === 'namedTupleMember' ) {
740
811
  return this.convert( typeReflection.element );
741
812
  }
742
813
 
743
814
  // A structure that mixes two and more different types.
815
+ // Each member is converted independently so its name/reference is preserved in the rendered signature.
816
+ // Inline object literal members additionally contribute their fields to the doclet's property table
817
+ // (see `flattenCompositeTypes()` consumed by `_convertProperties()`).
744
818
  if ( typeReflection.type === 'intersection' ) {
745
- return 'object';
819
+ return {
820
+ type: 'intersection',
821
+ values: typeReflection.types
822
+ .map( singleType => this.convert( singleType ) )
823
+ .filter( Boolean )
824
+ };
746
825
  }
747
826
 
748
827
  if ( typeReflection.type === 'query' ) {
@@ -776,11 +855,16 @@ class TypeConverter {
776
855
  return type.join( ' ' );
777
856
  }
778
857
 
779
- // A conditional type.
780
- // Perhaps it could be rendered as an union.
858
+ // A conditional type: `CheckType extends ExtendsType ? TrueType : FalseType`.
781
859
  // See: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html.
782
860
  if ( typeReflection.type === 'conditional' ) {
783
- return 'object';
861
+ return {
862
+ type: 'conditional',
863
+ checkType: this.convert( typeReflection.checkType ),
864
+ extendsType: this.convert( typeReflection.extendsType ),
865
+ trueType: this.convert( typeReflection.trueType ),
866
+ falseType: this.convert( typeReflection.falseType )
867
+ };
784
868
  }
785
869
 
786
870
  // See: https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html.
@@ -815,15 +899,24 @@ class TypeConverter {
815
899
  }
816
900
 
817
901
  if ( !Array.isArray( typesOrType ) ) {
818
- // `typesOrType` can be an object representing a generic value.
819
- // TODO: Missing CC here.
902
+ // Predicates fundamentally resolve to `boolean` for narrowing when the target type cannot
903
+ // be expressed compactly inline (an intersection, a conditional, a tuple, or an inline
904
+ // object literal), show just `boolean`. References, generics, etc. keep their normal rendering.
905
+ const compoundTypes = [ 'intersection', 'conditional', 'tuple' ];
906
+
907
+ if ( typeof typesOrType === 'object' && compoundTypes.includes( typesOrType.type ) ) {
908
+ return 'boolean';
909
+ }
910
+
911
+ // For all remaining objects, fall back to `.name` (e.g., `{ type: 'generic', name: 'Array' }`).
912
+ // Guard the case where `.name` is absent so an unanticipated shape does not throw.
820
913
  const value = typeof typesOrType === 'object' ? typesOrType.name : typesOrType;
821
914
 
822
- if ( value.toLowerCase() === 'object' ) {
915
+ if ( value && value.toLowerCase() === 'object' ) {
823
916
  return 'boolean';
824
917
  }
825
918
 
826
- // Convert a single item into an array to avoid having mixed structures in a view.
919
+ // Convert a single item into an array to keep a uniform shape for the renderer.
827
920
  typesOrType = [ typesOrType ];
828
921
  }
829
922
 
@@ -844,6 +937,12 @@ class TypeConverter {
844
937
  };
845
938
  }
846
939
 
940
+ // An `infer X` type-variable binding inside a conditional's `extendsType`.
941
+ // See: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types.
942
+ if ( typeReflection.type === 'inferred' ) {
943
+ return `infer ${ typeReflection.name }`;
944
+ }
945
+
847
946
  // At this stage, the type cannot be converted. Perhaps, we missed something while developing
848
947
  // the conversion. Let's ask users to create a new issue containing the non-supported structure.
849
948
  //
@@ -884,3 +983,60 @@ function mapIndexSignatureObject( subItem, index ) {
884
983
  name: `${ subItem.name }[${ index }]`
885
984
  };
886
985
  }
986
+
987
+ /**
988
+ * Modifier flags applied by the built-in utility type wrappers we descend into.
989
+ * Used by `flattenCompositeTypes()` so that an inline literal hidden inside e.g.
990
+ * `Partial<{...}>` still surfaces in the property table with the right per-field flags.
991
+ */
992
+ const FLAG_PROPAGATING_WRAPPERS = new Map( [
993
+ [ 'Partial', { isOptional: true } ],
994
+ [ 'Required', { isOptional: false } ],
995
+ [ 'Readonly', { isReadonly: true } ]
996
+ ] );
997
+
998
+ /**
999
+ * Flattens a typedoc type into a list of candidate types whose `declaration.children`
1000
+ * contribute to the parent's property table. Unions and intersections are recursively
1001
+ * flattened so members that are inline object literals are reached. References to the
1002
+ * `Partial`, `Required`, and `Readonly` utility wrappers are descended into so an
1003
+ * intersection like `Base & Partial<{ a: T }>` still exposes `a` (with the matching
1004
+ * per-field modifier). Other reference and non-declaration members are left in place
1005
+ * and skipped by the caller's `type.declaration` filter.
1006
+ *
1007
+ * @param {AvailableTypes} type
1008
+ * @returns {Array.<AvailableTypes>}
1009
+ */
1010
+ function flattenCompositeTypes( type ) {
1011
+ if ( type.type === 'union' || type.type === 'intersection' ) {
1012
+ return type.types.flatMap( flattenCompositeTypes );
1013
+ }
1014
+
1015
+ if (
1016
+ type.type === 'reference' &&
1017
+ FLAG_PROPAGATING_WRAPPERS.has( type.name ) &&
1018
+ type.typeArguments &&
1019
+ type.typeArguments.length === 1
1020
+ ) {
1021
+ const flagOverrides = FLAG_PROPAGATING_WRAPPERS.get( type.name );
1022
+
1023
+ return flattenCompositeTypes( type.typeArguments[ 0 ] ).map( inner => {
1024
+ if ( !inner.declaration ) {
1025
+ return inner;
1026
+ }
1027
+
1028
+ return {
1029
+ ...inner,
1030
+ declaration: {
1031
+ ...inner.declaration,
1032
+ children: ( inner.declaration.children || [] ).map( child => ( {
1033
+ ...child,
1034
+ flags: { ...child.flags, ...flagOverrides }
1035
+ } ) )
1036
+ }
1037
+ };
1038
+ } );
1039
+ }
1040
+
1041
+ return [ type ];
1042
+ }
@@ -26,7 +26,7 @@ mixin renderStringValue( inlineObjectOrType )
26
26
  //- TODO: Description.
27
27
  //-
28
28
  //- @param {Object} complexType
29
- //- @param {'function'|'predicate'|'operator'|'generic'} complexType.type
29
+ //- @param {'function'|'predicate'|'operator'|'generic'|'intersection'|'tuple'|'conditional'} complexType.type
30
30
  //- @param {Array.<*>} [complexType.params]
31
31
  //- @param {String} [complexType.name]
32
32
  //- @param {Array.<*>} [complexType.instances]
@@ -69,6 +69,26 @@ mixin renderComplexStructure( complexType )
69
69
  if ( complexType.type === 'generic' && complexType.name && ( complexType.typeParameters || complexType.typeParameter ) )
70
70
  +renderGenericType( complexType )
71
71
 
72
+ //- Intersection: `Foo & Bar & Baz`.
73
+ if ( complexType.type === 'intersection' && complexType.values )
74
+ +renderIntersection( complexType.values )
75
+
76
+ //- Tuple: `[ Type1, Type2 ]` or `[ name1: Type1, name2: Type2 ]`.
77
+ if ( complexType.type === 'tuple' && complexType.elements )
78
+ +renderTuple( complexType.elements )
79
+
80
+ //- Conditional: `CheckType extends ExtendsType ? TrueType : FalseType`.
81
+ if ( complexType.type === 'conditional' )
82
+ +renderType( complexType.checkType )
83
+ | !{ ' ' }
84
+ i !{ 'extends' }
85
+ | !{ ' ' }
86
+ +renderType( complexType.extendsType )
87
+ | !{ ' ? ' }
88
+ +renderType( complexType.trueType )
89
+ | !{ ' : ' }
90
+ +renderType( complexType.falseType )
91
+
72
92
 
73
93
  //- Renders a structure for an union, an array or a single type specified as a string.
74
94
  //-
@@ -110,6 +130,52 @@ mixin renderUnion( iterable )
110
130
  if index < iterable.length - 1
111
131
  | !{ ' | ' }
112
132
 
133
+ //- Renders an intersection structure, e.g., `Foo & Bar & Baz`.
134
+ //-
135
+ //- All types are linkable if they point to an existing JSDoc doclet.
136
+ //-
137
+ //- @param {Array.<String>} type
138
+ mixin renderIntersection( iterable )
139
+ each item, index in iterable
140
+ +renderType( item )
141
+ if index < iterable.length - 1
142
+ | !{ ' & ' }
143
+
144
+ //- Renders a tuple structure, e.g., `[ string, number ]`, `[ key: string, value: number ]`,
145
+ //- or `[ first: string, second?: number ]` (optional last element).
146
+ //-
147
+ //- For anonymous optional elements whose value renders with an infix operator
148
+ //- (`|`, `&`, ` ? `, ` => `), the value is wrapped in parentheses so the trailing `?`
149
+ //- unambiguously applies to the whole expression — matching TypeScript's `( T | U )?` source syntax.
150
+ //- Single tokens (`number?`, `Array<T>?`) and named members (`key?: T | U`) stay unwrapped.
151
+ //-
152
+ //- @param {Array.<{ name?: String, value: *, optional?: Boolean }>} elements
153
+ mixin renderTuple( elements )
154
+ | !{ '&lsqb; ' }
155
+ each element, index in elements
156
+ if ( element.name )
157
+ if ( element.optional )
158
+ | #{ element.name }?:&nbsp;
159
+ else
160
+ | #{ element.name }:&nbsp;
161
+ +renderType( element.value )
162
+ else
163
+ //- The trailing `?` binds tighter than `|`/`&`, so wrap compound values
164
+ //- to keep the source semantics. Plain strings and arrays (`Array<T>`,
165
+ //- which self-brackets) are unambiguous and need no parens.
166
+ - const v = element.value
167
+ - const wrap = element.optional && ( v instanceof Set || ( v && typeof v === 'object' && !Array.isArray( v ) && [ 'intersection', 'conditional', 'function' ].includes( v.type ) ) )
168
+ if ( wrap )
169
+ | !{ '&lpar; ' }
170
+ +renderType( element.value )
171
+ if ( wrap )
172
+ | !{ ' &rpar;' }
173
+ if ( element.optional )
174
+ | ?
175
+ if index < elements.length - 1
176
+ | !{ ', ' }
177
+ | !{ ' &rsqb;' }
178
+
113
179
  //- Draws an inline callback structure.
114
180
  //-
115
181
  //- E.g.: `( ArgumentFoo | ArgumentBar, AnotherArgument ) => ReturnType`
@@ -9,7 +9,7 @@
9
9
  mixin call-for-feedback()
10
10
  //- Stable id: at most one block per page (build fails if more); see validate-call-for-feedback-once.cjs.
11
11
  aside.c-call-for-feedback( role='complementary' aria-labelledby='call-for-feedback-title' )
12
- h3.c-call-for-feedback__title#call-for-feedback-title Call for feedback
12
+ div.c-call-for-feedback__title#call-for-feedback-title Call for feedback
13
13
  p.c-call-for-feedback__text
14
14
  | Have an idea for future improvements? We'd love to hear from you! Share your thoughts and suggestions with us through our&nbsp;
15
15
  a.c-call-for-feedback__link( href='https://ckeditor.com/contact/' ) contact form
@@ -0,0 +1,4 @@
1
+ mixin google-tag-manager-noscript()
2
+ if googletagmanager && googletagmanager.gtm
3
+ noscript
4
+ iframe( src='https://load.site.ckeditor.com/ns.html?id=' + googletagmanager.gtm height='0' width='0' style='display:none;visibility:hidden' )
@@ -1,14 +1,38 @@
1
1
  if googletagmanager
2
2
  script.
3
3
  if (!{ JSON.stringify(googletagmanager.domains) }.indexOf(location.hostname) !== -1) {
4
- (function (w, d, s, l, i) {
5
- w[l] = w[l] || [];
6
- w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
7
- var f = d.getElementsByTagName(s)[0],
8
- j = d.createElement(s),
9
- dl = l != 'dataLayer' ? '&l=' + l : '';
10
- j.async = true;
11
- j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
12
- f.parentNode.insertBefore(j, f);
13
- })( window, document, 'script', 'dataLayer', '!{ googletagmanager.gtm }' );
4
+ !function() {
5
+ "use strict";
6
+ function l(e) { for (var t = e, r = 0, n = document.cookie.split(";"); r < n.length; r++) { var o = n[r].split("="); if (o[0].trim() === t) return o[1]; } }
7
+ function s(e) { return localStorage.getItem(e); }
8
+ function u(e) { return window[e]; }
9
+ function A(e, t) { e = document.querySelector(e); return t ? null == e ? void 0 : e.getAttribute(t) : null == e ? void 0 : e.textContent; }
10
+ var e = window, t = document, r = "script", n = "dataLayer",
11
+ o = "https://site.ckeditor.com",
12
+ a = "https://load.site.ckeditor.com",
13
+ i = "9ao1wxbphqbpj",
14
+ c = "dy7f4ho=AxRfNjw9TzMsIzFHJFYURVheU1cMHl8bARUGGBwfEFYJHw8%3D",
15
+ g = "cookie", v = "_stape_uid", E = "", d = !1;
16
+ try {
17
+ var d = !!g && (m = navigator.userAgent, !!(m = new RegExp("Version/([0-9._]+)(.*Mobile)?.*Safari.*").exec(m))) && 16.4 <= parseFloat(m[1]),
18
+ f = "stapeUserId" === g,
19
+ I = d && !f
20
+ ? function(e, t, r) {
21
+ void 0 === t && (t = "");
22
+ var n = { cookie: l, localStorage: s, jsVariable: u, cssSelector: A }, t = Array.isArray(t) ? t : [t];
23
+ if (e && n[e]) { for (var o = n[e], a = 0, i = t; a < i.length; a++) { var c = i[a], c = r ? o(c, r) : o(c); if (c) return c; } }
24
+ else { console.warn("invalid uid source", e); }
25
+ }(g, v, E)
26
+ : void 0;
27
+ d = d && (!!I || f);
28
+ } catch(e) { console.error(e); }
29
+ var m = e,
30
+ g = (m[n] = m[n] || [], m[n].push({ "gtm.start": (new Date).getTime(), event: "gtm.js" }), t.getElementsByTagName(r)[0]),
31
+ v = I ? "&bi=" + encodeURIComponent(I) : "",
32
+ E = t.createElement(r),
33
+ f = (d && (i = 8 < i.length ? i.replace(/([a-z]{8}$)/, "kp$1") : "kp" + i), !d && a ? a : o);
34
+ E.async = !0;
35
+ E.src = f + "/" + i + ".js?" + c + v;
36
+ null != (e = g.parentNode) && e.insertBefore(E, g);
37
+ }();
14
38
  }
@@ -1,2 +1,3 @@
1
+ include google-tag-manager-noscript
1
2
  include google-tag-manager
2
3
  include google-analytics
@@ -7,6 +7,7 @@ html(lang='en')
7
7
  head
8
8
  include _head/index
9
9
  body.l-layout.js-layout
10
+ +google-tag-manager-noscript
10
11
  +header-nightly-info
11
12
  +header-lts-info
12
13
  +header-promobar
@@ -6,6 +6,7 @@ html(lang='en')
6
6
  head
7
7
  include _head/index
8
8
  body.l-layout.js-layout
9
+ +google-tag-manager-noscript
9
10
  +header-nightly-info
10
11
  +header-lts-info
11
12
  +header-promobar
@@ -7,6 +7,7 @@ html(lang='en')
7
7
  include _head/index
8
8
 
9
9
  body.l-layout.l-layout--muted.js-layout
10
+ +google-tag-manager-noscript
10
11
  +header-nightly-info
11
12
  +header-lts-info
12
13
  +header-promobar
@@ -1,18 +1,38 @@
1
1
  if googletagmanager
2
-
3
- // Google Tag Manager
4
- script.
5
- if (!{ JSON.stringify(googletagmanager.domains) }.indexOf(location.hostname) !== -1) {
6
- (function (w, d, s, l, i) {
7
- w[l] = w[l] || [];
8
- w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
9
- var f = d.getElementsByTagName(s)[0],
10
- j = d.createElement(s),
11
- dl = l != 'dataLayer' ? '&l=' + l : '';
12
- j.async = true;
13
- j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
14
- f.parentNode.insertBefore(j, f);
15
- })( window, document, 'script', 'dataLayer', '!{ googletagmanager.gtm }' );
16
- }
17
-
18
- // End Google Tag Manager
2
+ script.
3
+ if (!{ JSON.stringify(googletagmanager.domains) }.indexOf(location.hostname) !== -1) {
4
+ !function() {
5
+ "use strict";
6
+ function l(e) { for (var t = e, r = 0, n = document.cookie.split(";"); r < n.length; r++) { var o = n[r].split("="); if (o[0].trim() === t) return o[1]; } }
7
+ function s(e) { return localStorage.getItem(e); }
8
+ function u(e) { return window[e]; }
9
+ function A(e, t) { e = document.querySelector(e); return t ? null == e ? void 0 : e.getAttribute(t) : null == e ? void 0 : e.textContent; }
10
+ var e = window, t = document, r = "script", n = "dataLayer",
11
+ o = "https://site.ckeditor.com",
12
+ a = "https://load.site.ckeditor.com",
13
+ i = "9ao1wxbphqbpj",
14
+ c = "dy7f4ho=AxRfNjw9TzMsIzFHJFYURVheU1cMHl8bARUGGBwfEFYJHw8%3D",
15
+ g = "cookie", v = "_stape_uid", E = "", d = !1;
16
+ try {
17
+ var d = !!g && (m = navigator.userAgent, !!(m = new RegExp("Version/([0-9._]+)(.*Mobile)?.*Safari.*").exec(m))) && 16.4 <= parseFloat(m[1]),
18
+ f = "stapeUserId" === g,
19
+ I = d && !f
20
+ ? function(e, t, r) {
21
+ void 0 === t && (t = "");
22
+ var n = { cookie: l, localStorage: s, jsVariable: u, cssSelector: A }, t = Array.isArray(t) ? t : [t];
23
+ if (e && n[e]) { for (var o = n[e], a = 0, i = t; a < i.length; a++) { var c = i[a], c = r ? o(c, r) : o(c); if (c) return c; } }
24
+ else { console.warn("invalid uid source", e); }
25
+ }(g, v, E)
26
+ : void 0;
27
+ d = d && (!!I || f);
28
+ } catch(e) { console.error(e); }
29
+ var m = e,
30
+ g = (m[n] = m[n] || [], m[n].push({ "gtm.start": (new Date).getTime(), event: "gtm.js" }), t.getElementsByTagName(r)[0]),
31
+ v = I ? "&bi=" + encodeURIComponent(I) : "",
32
+ E = t.createElement(r),
33
+ f = (d && (i = 8 < i.length ? i.replace(/([a-z]{8}$)/, "kp$1") : "kp" + i), !d && a ? a : o);
34
+ E.async = !0;
35
+ E.src = f + "/" + i + ".js?" + c + v;
36
+ null != (e = g.parentNode) && e.insertBefore(E, g);
37
+ }();
38
+ }
@@ -4,7 +4,7 @@ if googletagmanager && googletagmanager.gtm
4
4
 
5
5
  //- Google Tag Manager (noscript)
6
6
  noscript
7
- iframe( src="https://www.googletagmanager.com/ns.html?id=" + googletagmanager.gtm height="0" width="0" style="display:none;visibility:hidden" )
7
+ iframe( src="https://load.site.ckeditor.com/ns.html?id=" + googletagmanager.gtm height="0" width="0" style="display:none;visibility:hidden" )
8
8
  //- End Google Tag Manager (noscript)
9
9
 
10
10
  header.top( data-project= projectLocals ? projectLocals.projectSlug : undefined )