1+ import type { FluentBundle } from "@fluent/bundle" ;
2+ import {
3+ Localization ,
4+ type FormattedMessage ,
5+ type MessageKey ,
6+ } from "./localization.js" ;
17import translateElement from "./overlay.js" ;
2- import Localization from "./localization.js" ;
38
49const L10NID_ATTR_NAME = "data-l10n-id" ;
510const L10NARGS_ATTR_NAME = "data-l10n-args" ;
@@ -14,37 +19,36 @@ const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`;
1419 * formatting of translations and methods for observing DOM
1520 * trees with a `MutationObserver`.
1621 */
17- export default class DOMLocalization extends Localization {
18- /**
19- * @param {Array<String> } resourceIds - List of resource IDs
20- * @param {Function } generateBundles - Function that returns a
21- * generator over FluentBundles
22- * @returns {DOMLocalization }
23- */
24- constructor ( resourceIds , generateBundles ) {
25- super ( resourceIds , generateBundles ) ;
22+ export class DOMLocalization extends Localization {
23+ // A Set of DOM trees observed by the `MutationObserver`.
24+ roots = new Set < Element | DocumentFragment > ( ) ;
2625
27- // A Set of DOM trees observed by the `MutationObserver`.
28- this . roots = new Set ( ) ;
29- // requestAnimationFrame handler.
30- this . pendingrAF = null ;
31- // list of elements pending for translation.
32- this . pendingElements = new Set ( ) ;
33- this . windowElement = null ;
34- this . mutationObserver = null ;
35-
36- this . observerConfig = {
37- attributes : true ,
38- characterData : false ,
39- childList : true ,
40- subtree : true ,
41- attributeFilter : [ L10NID_ATTR_NAME , L10NARGS_ATTR_NAME ] ,
42- } ;
26+ // requestAnimationFrame handler.
27+ pendingrAF : number | null = null ;
28+
29+ pendingElements = new Set < Element > ( ) ;
30+ windowElement : ( Window & typeof globalThis ) | null = null ;
31+ mutationObserver : MutationObserver | null = null ;
32+
33+ observerConfig = {
34+ attributes : true ,
35+ characterData : false ,
36+ childList : true ,
37+ subtree : true ,
38+ attributeFilter : [ L10NID_ATTR_NAME , L10NARGS_ATTR_NAME ] ,
39+ } ;
40+
41+ constructor (
42+ resourceIds : string [ ] ,
43+ generateBundles : ( resourceIds : string [ ] ) => Iterable < FluentBundle >
44+ ) {
45+ super ( resourceIds , generateBundles ) ;
4346 }
4447
45- onChange ( eager = false ) {
48+ onChange ( eager = false ) : void {
4649 super . onChange ( eager ) ;
4750 if ( this . roots ) {
51+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
4852 this . translateRoots ( ) ;
4953 }
5054 }
@@ -79,12 +83,15 @@ export default class DOMLocalization extends Localization {
7983 * </p>
8084 * ```
8185 *
82- * @param {Element } element - Element to set attributes on
83- * @param {string } id - l10n-id string
84- * @param {Object<string, string> } args - KVP list of l10n arguments
85- * @returns {Element }
86+ * @param element - Element to set attributes on
87+ * @param id - l10n-id string
88+ * @param args - KVP list of l10n arguments
8689 */
87- setAttributes ( element , id , args ) {
90+ setAttributes (
91+ element : Element ,
92+ id : string ,
93+ args : Record < string , string >
94+ ) : Element {
8895 element . setAttribute ( L10NID_ATTR_NAME , id ) ;
8996 if ( args ) {
9097 element . setAttribute ( L10NARGS_ATTR_NAME , JSON . stringify ( args ) ) ;
@@ -103,14 +110,15 @@ export default class DOMLocalization extends Localization {
103110 * );
104111 * // -> { id: 'hello', args: { who: 'world' } }
105112 * ```
106- *
107- * @param {Element } element - HTML element
108- * @returns {{id: string, args: Object} }
109113 */
110- getAttributes ( element ) {
114+ getAttributes ( element : Element ) : {
115+ id : string | null ;
116+ args : Record < string , unknown > | null ;
117+ } {
118+ const args = element . getAttribute ( L10NARGS_ATTR_NAME ) ;
111119 return {
112120 id : element . getAttribute ( L10NID_ATTR_NAME ) ,
113- args : JSON . parse ( element . getAttribute ( L10NARGS_ATTR_NAME ) || null ) ,
121+ args : args ? ( JSON . parse ( args ) as Record < string , unknown > ) : null ,
114122 } ;
115123 }
116124
@@ -119,10 +127,8 @@ export default class DOMLocalization extends Localization {
119127 *
120128 * Additionally, if this `DOMLocalization` has an observer, start observing
121129 * `newRoot` in order to translate mutations in it.
122- *
123- * @param {Element | DocumentFragment } newRoot - Root to observe.
124130 */
125- connectRoot ( newRoot ) {
131+ connectRoot ( newRoot : Element | DocumentFragment ) : void {
126132 for ( const root of this . roots ) {
127133 if (
128134 root === newRoot ||
@@ -140,13 +146,13 @@ export default class DOMLocalization extends Localization {
140146 }
141147 } else {
142148 this . windowElement = newRoot . ownerDocument . defaultView ;
143- this . mutationObserver = new this . windowElement . MutationObserver (
149+ this . mutationObserver = new this . windowElement ! . MutationObserver (
144150 mutations => this . translateMutations ( mutations )
145151 ) ;
146152 }
147153
148154 this . roots . add ( newRoot ) ;
149- this . mutationObserver . observe ( newRoot , this . observerConfig ) ;
155+ this . mutationObserver ! . observe ( newRoot , this . observerConfig ) ;
150156 }
151157
152158 /**
@@ -157,11 +163,8 @@ export default class DOMLocalization extends Localization {
157163 *
158164 * Returns `true` if the root was the last one managed by this
159165 * `DOMLocalization`.
160- *
161- * @param {Element | DocumentFragment } root - Root to disconnect.
162- * @returns {boolean }
163166 */
164- disconnectRoot ( root ) {
167+ disconnectRoot ( root : Element | DocumentFragment ) : boolean {
165168 this . roots . delete ( root ) ;
166169 // Pause the mutation observer to stop observing `root`.
167170 this . pauseObserving ( ) ;
@@ -187,15 +190,15 @@ export default class DOMLocalization extends Localization {
187190 *
188191 * @returns {Promise }
189192 */
190- translateRoots ( ) {
193+ translateRoots ( ) : Promise < void [ ] > {
191194 const roots = Array . from ( this . roots ) ;
192195 return Promise . all ( roots . map ( root => this . translateFragment ( root ) ) ) ;
193196 }
194197
195198 /**
196199 * Pauses the `MutationObserver`.
197200 */
198- pauseObserving ( ) {
201+ pauseObserving ( ) : void {
199202 if ( ! this . mutationObserver ) {
200203 return ;
201204 }
@@ -207,7 +210,7 @@ export default class DOMLocalization extends Localization {
207210 /**
208211 * Resumes the `MutationObserver`.
209212 */
210- resumeObserving ( ) {
213+ resumeObserving ( ) : void {
211214 if ( ! this . mutationObserver ) {
212215 return ;
213216 }
@@ -219,19 +222,19 @@ export default class DOMLocalization extends Localization {
219222
220223 /**
221224 * Translate mutations detected by the `MutationObserver`.
222- *
223- * @private
224225 */
225- translateMutations ( mutations ) {
226+ private translateMutations ( mutations : MutationRecord [ ] ) : void {
226227 for ( const mutation of mutations ) {
227228 switch ( mutation . type ) {
228- case "attributes" :
229- if ( mutation . target . hasAttribute ( "data-l10n-id" ) ) {
230- this . pendingElements . add ( mutation . target ) ;
229+ case "attributes" : {
230+ const target = mutation . target as HTMLElement ;
231+ if ( target . hasAttribute ( "data-l10n-id" ) ) {
232+ this . pendingElements . add ( target ) ;
231233 }
232234 break ;
235+ }
233236 case "childList" :
234- for ( const addedNode of mutation . addedNodes ) {
237+ for ( const addedNode of mutation . addedNodes as NodeListOf < HTMLElement > ) {
235238 if ( addedNode . nodeType === addedNode . ELEMENT_NODE ) {
236239 if ( addedNode . childElementCount ) {
237240 for ( const element of this . getTranslatables ( addedNode ) ) {
@@ -250,7 +253,8 @@ export default class DOMLocalization extends Localization {
250253 // into a single requestAnimationFrame.
251254 if ( this . pendingElements . size > 0 ) {
252255 if ( this . pendingrAF === null ) {
253- this . pendingrAF = this . windowElement . requestAnimationFrame ( ( ) => {
256+ this . pendingrAF = this . windowElement ! . requestAnimationFrame ( ( ) => {
257+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
254258 this . translateElements ( Array . from ( this . pendingElements ) ) ;
255259 this . pendingElements . clear ( ) ;
256260 this . pendingrAF = null ;
@@ -269,10 +273,10 @@ export default class DOMLocalization extends Localization {
269273 *
270274 * Returns a `Promise` that gets resolved once the translation is complete.
271275 *
272- * @param {Element | DocumentFragment } frag - Element or DocumentFragment to be translated
276+ * @param { } frag - Element or DocumentFragment to be translated
273277 * @returns {Promise }
274278 */
275- translateFragment ( frag ) {
279+ translateFragment ( frag : Element | DocumentFragment ) : Promise < void > {
276280 return this . translateElements ( this . getTranslatables ( frag ) ) ;
277281 }
278282
@@ -285,28 +289,25 @@ export default class DOMLocalization extends Localization {
285289 * with information about which translations to use.
286290 *
287291 * Returns a `Promise` that gets resolved once the translation is complete.
288- *
289- * @param {Array<Element> } elements - List of elements to be translated
290- * @returns {Promise }
291292 */
292- async translateElements ( elements ) {
293+ async translateElements ( elements : Element [ ] ) : Promise < void > {
293294 if ( ! elements . length ) {
294295 return undefined ;
295296 }
296297
298+ // eslint-disable-next-line @typescript-eslint/unbound-method
297299 const keys = elements . map ( this . getAttributes ) ;
298- const translations = await this . formatMessages ( keys ) ;
300+ const translations = await this . formatMessages ( keys as MessageKey [ ] ) ;
299301 return this . applyTranslations ( elements , translations ) ;
300302 }
301303
302304 /**
303305 * Applies translations onto elements.
304- *
305- * @param {Array<Element> } elements
306- * @param {Array<Object> } translations
307- * @private
308306 */
309- applyTranslations ( elements , translations ) {
307+ private applyTranslations (
308+ elements : Element [ ] ,
309+ translations : FormattedMessage [ ]
310+ ) : void {
310311 this . pauseObserving ( ) ;
311312
312313 for ( let i = 0 ; i < elements . length ; i ++ ) {
@@ -320,18 +321,11 @@ export default class DOMLocalization extends Localization {
320321
321322 /**
322323 * Collects all translatable child elements of the element.
323- *
324- * @param {Element | DocumentFragment } element
325- * @returns {Array<Element> }
326- * @private
327324 */
328- getTranslatables ( element ) {
325+ private getTranslatables ( element : Element | DocumentFragment ) : Element [ ] {
329326 const nodes = Array . from ( element . querySelectorAll ( L10N_ELEMENT_QUERY ) ) ;
330327
331- if (
332- typeof element . hasAttribute === "function" &&
333- element . hasAttribute ( L10NID_ATTR_NAME )
334- ) {
328+ if ( element instanceof Element && element . hasAttribute ( L10NID_ATTR_NAME ) ) {
335329 nodes . push ( element ) ;
336330 }
337331
0 commit comments