@@ -20,6 +20,9 @@ let sharedStrings = null
20
20
const entities = [ ]
21
21
const entityData = { }
22
22
23
+ let readyResolver
24
+ const ready = new Promise ( resolve => { readyResolver = resolve } )
25
+
23
26
/*********************************************************
24
27
* Widget Replacement logic
25
28
*********************************************************/
@@ -364,22 +367,6 @@ class DuckWidget {
364
367
}
365
368
}
366
369
367
- /**
368
- * Initialise the Click to Load feature, once the necessary details have been
369
- * returned by the platform.
370
- * @returns {Promise }
371
- */
372
- async function initCTL ( ) {
373
- await replaceClickToLoadElements ( )
374
-
375
- window . addEventListener ( 'ddg-ctp-replace-element' , ( { target } ) => {
376
- replaceClickToLoadElements ( target )
377
- } , { capture : true } )
378
-
379
- // Inform surrogate scripts that CTP is ready
380
- originalWindowDispatchEvent ( createCustomEvent ( 'ddg-ctp-ready' ) )
381
- }
382
-
383
370
function replaceTrackingElement ( widget , trackingElement , placeholderElement , hideTrackingElement = false , currentPlaceholder = null ) {
384
371
widget . dispatchEvent ( trackingElement , 'ddg-ctp-tracking-element' )
385
372
@@ -492,11 +479,14 @@ async function replaceYouTubeCTL (trackingElement, widget, togglePlaceholder = f
492
479
}
493
480
494
481
// Show YouTube Preview for embedded video
482
+ // TODO: Fix the hideTrackingElement option and reenable, or remove it. It's
483
+ // disabled for YouTube videos so far since it caused multiple
484
+ // placeholders to be displayed on the page.
495
485
if ( isYoutubePreviewsEnabled === true ) {
496
486
const { youTubePreview, shadowRoot } = await createYouTubePreview ( trackingElement , widget )
497
487
const currentPlaceholder = togglePlaceholder ? document . getElementById ( `yt-ctl-dialog-${ widget . widgetID } ` ) : null
498
488
replaceTrackingElement (
499
- widget , trackingElement , youTubePreview , /* hideTrackingElement= */ true , currentPlaceholder
489
+ widget , trackingElement , youTubePreview , /* hideTrackingElement= */ false , currentPlaceholder
500
490
)
501
491
showExtraUnblockIfShortPlaceholder ( shadowRoot , youTubePreview )
502
492
@@ -506,7 +496,7 @@ async function replaceYouTubeCTL (trackingElement, widget, togglePlaceholder = f
506
496
const { blockingDialog, shadowRoot } = await createYouTubeBlockingDialog ( trackingElement , widget )
507
497
const currentPlaceholder = togglePlaceholder ? document . getElementById ( `yt-ctl-preview-${ widget . widgetID } ` ) : null
508
498
replaceTrackingElement (
509
- widget , trackingElement , blockingDialog , /* hideTrackingElement= */ true , currentPlaceholder
499
+ widget , trackingElement , blockingDialog , /* hideTrackingElement= */ false , currentPlaceholder
510
500
)
511
501
showExtraUnblockIfShortPlaceholder ( shadowRoot , blockingDialog )
512
502
}
@@ -537,6 +527,8 @@ function showExtraUnblockIfShortPlaceholder (shadowRoot, placeholder) {
537
527
* in the document will be replaced instead.
538
528
*/
539
529
async function replaceClickToLoadElements ( targetElement ) {
530
+ await ready
531
+
540
532
for ( const entity of Object . keys ( config ) ) {
541
533
for ( const widgetData of Object . values ( config [ entity ] . elementData ) ) {
542
534
const selector = widgetData . selectors . join ( )
@@ -1312,7 +1304,7 @@ async function createYouTubePreview (originalElement, widget) {
1312
1304
// Convention is that each function should be named the same as the sendMessage
1313
1305
// method we are calling into eg. calling `sendMessage('getClickToLoadState')`
1314
1306
// will result in a response routed to `updateHandlers.getClickToLoadState()`.
1315
- const updateHandlers = {
1307
+ const messageResponseHandlers = {
1316
1308
getClickToLoadState ( response ) {
1317
1309
devMode = response . devMode
1318
1310
isYoutubePreviewsEnabled = response . youtubePreviewsEnabled
@@ -1322,14 +1314,15 @@ const updateHandlers = {
1322
1314
// first.
1323
1315
1324
1316
// Start Click to Load
1325
- if ( document . readyState === 'complete' ) {
1326
- initCTL ( )
1327
- } else {
1328
- // Content script loaded before page content, so wait for load.
1329
- window . addEventListener ( 'load' , ( event ) => {
1330
- initCTL ( )
1331
- } )
1332
- }
1317
+ window . addEventListener ( 'ddg-ctp-replace-element' , ( { target } ) => {
1318
+ replaceClickToLoadElements ( target )
1319
+ } , { capture : true } )
1320
+
1321
+ // Inform surrogate scripts that CTP is ready
1322
+ originalWindowDispatchEvent ( createCustomEvent ( 'ddg-ctp-ready' ) )
1323
+
1324
+ // Mark the feature as ready, to allow placeholder replacements.
1325
+ readyResolver ( )
1333
1326
} ,
1334
1327
setYoutubePreviewsEnabled : function ( resp ) {
1335
1328
if ( resp ?. messageType && typeof resp ?. value === 'boolean' ) {
@@ -1346,6 +1339,8 @@ const updateHandlers = {
1346
1339
}
1347
1340
}
1348
1341
1342
+ const knownMessageResponseType = Object . prototype . hasOwnProperty . bind ( messageResponseHandlers )
1343
+
1349
1344
export function init ( args ) {
1350
1345
const websiteOwner = args ?. site ?. parentEntity
1351
1346
const settings = args ?. featureSettings ?. clickToPlay || { }
@@ -1411,17 +1406,32 @@ export function init (args) {
1411
1406
} )
1412
1407
1413
1408
// Request the current state of Click to Load from the platform.
1414
- // Note: When the response is received, initCTL() is then called by the
1415
- // response handler to finish starting up the feature.
1409
+ // Note: When the response is received, the response handler finishes
1410
+ // starting up the feature.
1416
1411
sendMessage ( 'getClickToLoadState' )
1417
1412
}
1418
1413
1419
- export function update ( args ) {
1420
- const detail = args && args . detail
1421
- if ( ! ( detail && detail . func ) ) { return }
1414
+ export function update ( message ) {
1415
+ // TODO: Once all Click to Load messages include the feature property, drop
1416
+ // messages that don't include the feature property too.
1417
+ if ( message ?. feature && message ?. feature !== 'clickToLoad' ) return
1422
1418
1423
- const fn = updateHandlers [ detail . func ]
1424
- if ( typeof fn !== 'function' ) { return }
1419
+ const messageType = message ?. messageType
1420
+ if ( ! messageType ) return
1425
1421
1426
- fn ( detail . response )
1422
+ // Message responses.
1423
+ if ( messageType === 'response' ) {
1424
+ const messageResponseType = message ?. responseMessageType
1425
+ if ( messageResponseType && knownMessageResponseType ( messageResponseType ) ) {
1426
+ return messageResponseHandlers [ messageResponseType ] ( message . response )
1427
+ }
1428
+ }
1429
+
1430
+ // Other known update messages.
1431
+ if ( messageType === 'displayClickToLoadPlaceholders' ) {
1432
+ // TODO: Pass `message.options.ruleAction` through, that way only
1433
+ // content corresponding to the entity for that ruleAction need to
1434
+ // be replaced with a placeholder.
1435
+ return replaceClickToLoadElements ( )
1436
+ }
1427
1437
}
0 commit comments