@@ -200,6 +200,10 @@ const StepperManager = (function StepperManagerClosure() {
200200 active : false ,
201201 // Stepper specific functions.
202202 create ( pageIndex ) {
203+ const pageContainer = document . querySelector (
204+ `#viewer div[data-page-number="${ pageIndex + 1 } "]`
205+ ) ;
206+
203207 const debug = document . createElement ( "div" ) ;
204208 debug . id = "stepper" + pageIndex ;
205209 debug . hidden = true ;
@@ -210,7 +214,12 @@ const StepperManager = (function StepperManagerClosure() {
210214 b . value = pageIndex ;
211215 stepperChooser . append ( b ) ;
212216 const initBreakPoints = breakPoints [ pageIndex ] || [ ] ;
213- const stepper = new Stepper ( debug , pageIndex , initBreakPoints ) ;
217+ const stepper = new Stepper (
218+ debug ,
219+ pageIndex ,
220+ initBreakPoints ,
221+ pageContainer
222+ ) ;
214223 steppers . push ( stepper ) ;
215224 if ( steppers . length === 1 ) {
216225 this . selectStepper ( pageIndex , false ) ;
@@ -277,7 +286,7 @@ class Stepper {
277286 return simpleObj ;
278287 }
279288
280- constructor ( panel , pageIndex , initialBreakPoints ) {
289+ constructor ( panel , pageIndex , initialBreakPoints , pageContainer ) {
281290 this . panel = panel ;
282291 this . breakPoint = 0 ;
283292 this . nextBreakPoint = null ;
@@ -286,11 +295,20 @@ class Stepper {
286295 this . currentIdx = - 1 ;
287296 this . operatorListIdx = 0 ;
288297 this . indentLevel = 0 ;
298+ this . operatorGroups = null ;
299+ this . pageContainer = pageContainer ;
289300 }
290301
291302 init ( operatorList ) {
292303 const panel = this . panel ;
293304 const content = this . #c( "div" , "c=continue, s=step" ) ;
305+
306+ const showBoxesToggle = this . #c( "label" , "Show bounding boxes" ) ;
307+ const showBoxesCheckbox = this . #c( "input" ) ;
308+ showBoxesCheckbox . type = "checkbox" ;
309+ showBoxesToggle . prepend ( showBoxesCheckbox ) ;
310+ content . append ( this . #c( "br" ) , showBoxesToggle ) ;
311+
294312 const table = this . #c( "table" ) ;
295313 content . append ( table ) ;
296314 table . cellSpacing = 0 ;
@@ -305,6 +323,22 @@ class Stepper {
305323 panel . append ( content ) ;
306324 this . table = table ;
307325 this . updateOperatorList ( operatorList ) ;
326+
327+ const hoverStyle = this . #c( "style" ) ;
328+ this . hoverStyle = hoverStyle ;
329+ content . prepend ( hoverStyle ) ;
330+ table . addEventListener ( "mouseover" , this . #handleStepHover. bind ( this ) ) ;
331+ table . addEventListener ( "mouseleave" , e => {
332+ hoverStyle . innerText = "" ;
333+ } ) ;
334+
335+ showBoxesCheckbox . addEventListener ( "change" , ( ) => {
336+ if ( showBoxesCheckbox . checked ) {
337+ this . pageContainer . classList . add ( "showDebugBoxes" ) ;
338+ } else {
339+ this . pageContainer . classList . remove ( "showDebugBoxes" ) ;
340+ }
341+ } ) ;
308342 }
309343
310344 updateOperatorList ( operatorList ) {
@@ -397,6 +431,112 @@ class Stepper {
397431 this . table . append ( chunk ) ;
398432 }
399433
434+ setOperatorGroups ( groups ) {
435+ this . operatorGroups = groups ;
436+
437+ let boxesContainer = this . pageContainer . querySelector ( ".pdfBugGroupsLayer" ) ;
438+ if ( ! boxesContainer ) {
439+ boxesContainer = this . #c( "div" ) ;
440+ boxesContainer . classList . add ( "pdfBugGroupsLayer" ) ;
441+ this . pageContainer . append ( boxesContainer ) ;
442+
443+ boxesContainer . addEventListener (
444+ "click" ,
445+ this . #handleDebugBoxClick. bind ( this )
446+ ) ;
447+ boxesContainer . addEventListener (
448+ "mouseover" ,
449+ this . #handleDebugBoxHover. bind ( this )
450+ ) ;
451+ }
452+ boxesContainer . innerHTML = "" ;
453+
454+ for ( let i = 0 ; i < groups . length ; i ++ ) {
455+ const el = this . #c( "div" ) ;
456+ el . style . left = `${ groups [ i ] . minX * 100 } %` ;
457+ el . style . top = `${ groups [ i ] . minY * 100 } %` ;
458+ el . style . width = `${ ( groups [ i ] . maxX - groups [ i ] . minX ) * 100 } %` ;
459+ el . style . height = `${ ( groups [ i ] . maxY - groups [ i ] . minY ) * 100 } %` ;
460+ el . dataset . groupIdx = i ;
461+ boxesContainer . append ( el ) ;
462+ }
463+ }
464+
465+ #handleStepHover( e ) {
466+ const tr = e . target . closest ( "tr" ) ;
467+ if ( ! tr || tr . dataset . idx === undefined ) {
468+ return ;
469+ }
470+
471+ const index = + tr . dataset . idx ;
472+
473+ const closestGroupIndex =
474+ this . operatorGroups ?. findIndex ( ( { data } ) => {
475+ if ( "idx" in data ) {
476+ return data . idx === index ;
477+ }
478+ if ( "startIdx" in data ) {
479+ return data . startIdx <= index && index <= data . endIdx ;
480+ }
481+ return false ;
482+ } ) ?? - 1 ;
483+ if ( closestGroupIndex === - 1 ) {
484+ this . hoverStyle . innerText = "" ;
485+ return ;
486+ }
487+
488+ this . #highlightStepsGroup( closestGroupIndex ) ;
489+ }
490+
491+ #handleDebugBoxHover( e ) {
492+ if ( e . target . dataset . groupIdx === undefined ) {
493+ return ;
494+ }
495+
496+ const groupIdx = Number ( e . target . dataset . groupIdx ) ;
497+ this . #highlightStepsGroup( groupIdx ) ;
498+ }
499+
500+ #handleDebugBoxClick( e ) {
501+ if ( e . target . dataset . groupIdx === undefined ) {
502+ return ;
503+ }
504+
505+ const groupIdx = Number ( e . target . dataset . groupIdx ) ;
506+ const group = this . operatorGroups [ groupIdx ] ;
507+
508+ const firstOp = "idx" in group . data ? group . data . idx : group . data . startIdx ;
509+
510+ this . table . childNodes [ firstOp ] . scrollIntoView ( ) ;
511+ }
512+
513+ #highlightStepsGroup( groupIndex ) {
514+ const group = this . operatorGroups [ groupIndex ] ;
515+
516+ let cssSelector ;
517+ if ( "idx" in group . data ) {
518+ cssSelector = `tr[data-idx="${ group . data . idx } "]` ;
519+ } else if ( "startIdx" in group . data ) {
520+ cssSelector = `:nth-child(n+${ group . data . startIdx + 1 } of tr[data-idx]):nth-child(-n+${ group . data . endIdx + 1 } of tr[data-idx])` ;
521+ }
522+
523+ this . hoverStyle . innerText = `#${ this . panel . id } ${ cssSelector } { background-color: rgba(0, 0, 0, 0.1); }` ;
524+
525+ if ( group . data . dependencies ) {
526+ const selector = group . data . dependencies
527+ . map ( idx => `#${ this . panel . id } tr[data-idx="${ idx } "]` )
528+ . join ( ", " ) ;
529+ this . hoverStyle . innerText += `${ selector } { background-color: rgba(0, 255, 255, 0.1); }` ;
530+ }
531+
532+ this . hoverStyle . innerText += `
533+ #viewer [data-page-number="${ this . pageIndex + 1 } "] .pdfBugGroupsLayer :nth-child(${ groupIndex + 1 } ) {
534+ background-color: var(--hover-background-color);
535+ outline-style: var(--hover-outline-style);
536+ }
537+ ` ;
538+ }
539+
400540 getNextBreakPoint ( ) {
401541 this . breakPoints . sort ( function ( a , b ) {
402542 return a - b ;
0 commit comments