@@ -48,12 +48,15 @@ let mutationObserver = null;
48
48
* @typedef HtmlApiResponse
49
49
* @property {any } error
50
50
* @property {Supports } supports
51
- * @property {{tree: any, compatMode:string, doctypeName:string, doctypePublicId:string, doctypeSystemId:string }|null } result
51
+ * @property {{tree: any, compatMode:string, doctypeName:string, doctypePublicId:string, doctypeSystemId:string, playback: ReadonlyArray<[string,any]> }|null } result
52
52
* @property {string|null } normalizedHtml
53
53
* @property {string } html
54
54
*
55
55
*
56
56
* @typedef State
57
+ * @property {any|undefined } playbackTree
58
+ * @property {string|undefined } playbackHTML
59
+ * @property {number|null } playbackPoint
57
60
* @property {string|null } htmlApiDoctypeName
58
61
* @property {string|null } htmlApiDoctypePublicId
59
62
* @property {string|null } htmlApiDoctypeSystemId
@@ -82,8 +85,7 @@ let mutationObserver = null;
82
85
* @property {DOM } DOM
83
86
* @property {boolean } hasMutatedDom
84
87
* @property {HTMLAPISpan|false } span
85
- * @property {string } hoverSpan
86
- * @property {(span:HTMLAPISpan) => readonly [string,string,string] } hoverSpanSplit
88
+ * @property {string } htmlForDisplay
87
89
*/
88
90
89
91
/**
@@ -120,9 +122,27 @@ const store = createStore(NS, {
120
122
quirksMode : Boolean ( localStorage . getItem ( `${ NS } -quirksMode` ) ) ,
121
123
fullParser : Boolean ( localStorage . getItem ( `${ NS } -fullParser` ) ) ,
122
124
125
+ playbackPoint : null ,
123
126
previewCorePrNumber : null ,
124
127
previewGutenbergPrNumber : null ,
125
128
129
+ get playbackTree ( ) {
130
+ if ( store . state . playbackPoint === null ) {
131
+ return undefined ;
132
+ }
133
+ return store . state . htmlapiResponse . result ?. playback ?. [
134
+ store . state . playbackPoint
135
+ ] ?. [ 1 ] ;
136
+ } ,
137
+ get playbackHTML ( ) {
138
+ if ( store . state . playbackPoint === null ) {
139
+ return undefined ;
140
+ }
141
+ return store . state . htmlapiResponse . result ?. playback ?. [
142
+ store . state . playbackPoint
143
+ ] ?. [ 0 ] ;
144
+ } ,
145
+
126
146
/** @type {Link|null } */
127
147
get previewCoreLink ( ) {
128
148
if ( ! store . state . previewCorePrNumber ) {
@@ -223,51 +243,33 @@ const store = createStore(NS, {
223
243
return store . state . htmlPreambleForProcessing + store . state . html ;
224
244
} ,
225
245
226
- get hoverSpan ( ) {
246
+ get htmlForDisplay ( ) {
227
247
/** @type {string | undefined } */
228
- const html = store . state . htmlapiResponse . html ;
248
+ const html = store . state . playbackHTML ?? store . state . htmlapiResponse . html ;
229
249
if ( ! html ) {
230
250
return '' ;
231
251
}
232
252
return store . state . showInvisible ? replaceInvisible ( html ) : html ;
233
253
} ,
234
-
235
- /** @param {HTMLAPISpan } span */
236
- hoverSpanSplit ( span ) {
237
- /** @type {string | undefined } */
238
- const html = store . state . htmlapiResponse . html ;
239
- if ( ! html ) {
240
- return /** @type {const } */ ( [ '' , '' , '' ] ) ;
241
- }
242
- const buf = new TextEncoder ( ) . encode ( html ) ;
243
- const decoder = new TextDecoder ( ) ;
244
-
245
- const { start : spanStart , length } = span ;
246
- const spanEnd = spanStart + length ;
247
- const split = /** @type {const } */ ( [
248
- decoder . decode ( buf . slice ( 0 , spanStart ) ) ,
249
- decoder . decode ( buf . slice ( spanStart , spanEnd ) ) ,
250
- decoder . decode ( buf . slice ( spanEnd ) ) ,
251
- ] ) ;
252
-
253
- return store . state . showInvisible
254
- ? // @ts -expect-error It's fine, really.
255
- /** @type {typeof split } */ ( split . map ( replaceInvisible ) )
256
- : split ;
257
- } ,
258
254
} ,
259
255
260
256
clearSpan ( ) {
261
257
const el = /** @type {HTMLElement } */ (
262
258
document . getElementById ( 'processed-html' )
263
259
) ;
264
260
el . classList . remove ( 'has-highlighted-span' ) ;
265
- el . textContent = store . state . hoverSpan ;
261
+ el . textContent = store . state . htmlForDisplay ;
266
262
} ,
267
263
268
264
/** @param {MouseEvent } e */
269
265
handleSpanOver ( e ) {
270
266
const target = /** @type {HTMLElement } */ ( e . target ) ;
267
+
268
+ const html = store . state . playbackHTML ?? store . state . htmlapiResponse . html ;
269
+ if ( ! html ) {
270
+ return ;
271
+ }
272
+
271
273
/** @type {HTMLElement|null } */
272
274
const spanElement = target . dataset [ 'spanStart' ]
273
275
? target
@@ -276,20 +278,30 @@ const store = createStore(NS, {
276
278
if ( ! spanElement ) {
277
279
return ;
278
280
}
279
- const { spanStart, spanLength } = spanElement . dataset ;
280
- if ( ! spanStart || ! spanLength ) {
281
+
282
+ const { spanStart : spanStartVal , spanLength : spanLengthVal } =
283
+ spanElement . dataset ;
284
+ if ( ! spanStartVal || ! spanLengthVal ) {
281
285
return ;
282
286
}
287
+ const spanStart = Number ( spanStartVal ) ;
288
+ const spanLength = Number ( spanLengthVal ) ;
289
+
290
+ const buf = new TextEncoder ( ) . encode ( html ) ;
291
+ const decoder = new TextDecoder ( ) ;
292
+
293
+ const spanEnd = spanStart + spanLength ;
294
+ /** @type {readonly [Text,Text,Text] } */
295
+ // @ts -expect-error trust me!
296
+ const [ before , current , after ] = /** @type {const } */ ( [
297
+ decoder . decode ( buf . slice ( 0 , spanStart ) ) ,
298
+ decoder . decode ( buf . slice ( spanStart , spanEnd ) ) ,
299
+ decoder . decode ( buf . slice ( spanEnd ) ) ,
300
+ ] ) . map ( ( text ) => {
301
+ const t = store . state . showInvisible ? replaceInvisible ( text ) : text ;
302
+ return document . createTextNode ( t ) ;
303
+ } ) ;
283
304
284
- // @ts -expect-error 3-tuple to 3-tuple
285
- const [ before , current , after ] = /** @type {readonly [Text,Text,Text] } */ (
286
- store . state
287
- . hoverSpanSplit ( {
288
- start : Number ( spanStart ) ,
289
- length : Number ( spanLength ) ,
290
- } )
291
- . map ( ( text ) => document . createTextNode ( text ) )
292
- ) ;
293
305
const highlightCurrent = document . createElement ( 'span' ) ;
294
306
highlightCurrent . className = 'highlight-span' ;
295
307
highlightCurrent . appendChild ( current ) ;
@@ -502,6 +514,7 @@ const store = createStore(NS, {
502
514
}
503
515
504
516
store . state . htmlapiResponse = data ;
517
+ store . state . playbackPoint = null ;
505
518
store . clearSpan ( ) ;
506
519
507
520
if ( data . error ) {
@@ -536,13 +549,24 @@ const store = createStore(NS, {
536
549
mutationObserver ?. disconnect ( ) ;
537
550
store . state . hasMutatedDom = false ;
538
551
552
+ const html = store . state . playbackHTML ?? store . state . htmlForProcessing ;
553
+
539
554
iframeDocument . open ( ) ;
540
- iframeDocument . write ( store . state . htmlForProcessing ) ;
555
+ iframeDocument . write ( html ) ;
541
556
iframeDocument . close ( ) ;
542
557
543
- if ( store . state . htmlapiResponse . result ?. tree ) {
558
+ const tree =
559
+ store . state . playbackTree ?? store . state . htmlapiResponse . result ?. tree ;
560
+
561
+ const processedHtmlEl = /** @type {HTMLElement } */ (
562
+ document . getElementById ( 'processed-html' )
563
+ ) ;
564
+ processedHtmlEl . classList . remove ( 'has-highlighted-span' ) ;
565
+ processedHtmlEl . textContent = store . state . htmlForDisplay ;
566
+
567
+ if ( tree ) {
544
568
printHtmlApiTree (
545
- store . state . htmlapiResponse . result . tree ,
569
+ tree ,
546
570
// @ts -expect-error
547
571
document . getElementById ( 'html_api_result_holder' ) ,
548
572
{
@@ -593,6 +617,12 @@ const store = createStore(NS, {
593
617
alert ( 'Copy failed, make sure the browser window is focused.' ) ;
594
618
}
595
619
} ,
620
+
621
+ /** @param {InputEvent } e */
622
+ handlePlaybackChange ( e ) {
623
+ const val = /** @type {HTMLInputElement } */ ( e . target ) . valueAsNumber ;
624
+ store . state . playbackPoint = val - 1 ;
625
+ } ,
596
626
} ) ;
597
627
598
628
/** @param {keyof State } stateKey */
0 commit comments