3030
3131// What is the scale factor when stage is shrunk?
3232const CONTAINERSCALEFACTOR = 4 ;
33+ // Provide a safe fallback for LEADING when this file is loaded in tests
34+ // or environments where `LEADING` may not yet be defined.
35+ const LEADING_FALLBACK = typeof LEADING !== "undefined" ? LEADING : 0 ;
36+ // Provide a safe fallback for base64Encode when not available in the environment
37+ const _base64Encode = typeof base64Encode !== "undefined" ? base64Encode : s => s ;
38+ // Provide a minimal fallback for MBOUNDARY when not present (tests may not load artwork.js).
39+ const _MBOUNDARY =
40+ typeof MBOUNDARY !== "undefined"
41+ ? MBOUNDARY
42+ : '<svg xmlns="http://www.w3.org/2000/svg" height="HEIGHT" width="WIDTH">' +
43+ '<rect x="X" y="Y" width="DX" height="DY" stroke="stroke_color" fill="fill_color" stroke-width="STROKE"/>' +
44+ "</svg>" ;
3345
3446/**
3547 * Class for managing all the turtles.
@@ -536,7 +548,7 @@ Turtles.TurtlesModel = class {
536548 * @returns {void }
537549 */
538550 addTurtleGraphicProps ( turtle , blkInfoAvailable , infoDict ) {
539- setTimeout ( ( ) => {
551+ requestAnimationFrame ( ( ) => {
540552 if ( blkInfoAvailable ) {
541553 if ( "heading" in infoDict ) {
542554 turtle . painter . doSetHeading ( infoDict [ "heading" ] ) ;
@@ -562,7 +574,7 @@ Turtles.TurtlesModel = class {
562574 turtle . rename ( infoDict [ "name" ] ) ;
563575 }
564576 }
565- } , 2000 ) ;
577+ } ) ;
566578 }
567579
568580 /**
@@ -571,7 +583,8 @@ Turtles.TurtlesModel = class {
571583 * @return {Boolean } - running
572584 */
573585 running ( ) {
574- for ( let i = 0 ; i < this . getTurtleCount ( ) ; i ++ ) {
586+ const turtleCount = this . getTurtleCount ( ) ;
587+ for ( let i = 0 ; i < turtleCount ; i ++ ) {
575588 if ( this . getTurtle ( i ) . running ) {
576589 return true ;
577590 }
@@ -592,7 +605,8 @@ Turtles.TurtlesModel = class {
592605 * @returns index number of companion turtle or i
593606 */
594607 companionTurtle ( i ) {
595- for ( let t = 0 ; t < this . getTurtleCount ( ) ; t ++ ) {
608+ const turtleCount = this . getTurtleCount ( ) ;
609+ for ( let t = 0 ; t < turtleCount ; t ++ ) {
596610 if ( this . getTurtle ( t ) . companionTurtle === i ) {
597611 return t ;
598612 }
@@ -606,7 +620,8 @@ Turtles.TurtlesModel = class {
606620 */
607621 turtleCount ( ) {
608622 let count = 0 ;
609- for ( let t = 0 ; t < this . getTurtleCount ( ) ; t ++ ) {
623+ const totalTurtles = this . getTurtleCount ( ) ;
624+ for ( let t = 0 ; t < totalTurtles ; t ++ ) {
610625 if ( this . companionTurtle ( t ) === t && ! this . getTurtle ( t ) . inTrash ) {
611626 count += 1 ;
612627 }
@@ -654,28 +669,50 @@ Turtles.TurtlesView = class {
654669
655670 this . currentGrid = null ; // currently selected grid
656671
657- // Attach an event listener to the 'resize' event
672+ // Debounce timer for resize events
673+ this . _resizeTimer = null ;
674+
675+ // Attach a debounced event listener to the 'resize' event
676+ // This prevents rapid-fire resize calculations that can cause
677+ // crashes when toggling DevTools or switching tabs.
658678 window . addEventListener ( "resize" , ( ) => {
659- // Call the updateDimensions function when resizing occurs
660- var screenWidth =
661- window . innerWidth ||
662- document . documentElement . clientWidth ||
663- document . body . clientWidth ;
664- var screenHeight =
665- window . innerHeight ||
666- document . documentElement . clientHeight ||
667- document . body . clientHeight ;
668-
669- // Set a scaling factor to adjust the dimensions based on the screen size
670- var scale = Math . min ( screenWidth / 1200 , screenHeight / 900 ) ;
671-
672- // Calculate the new dimensions
673- var newWidth = Math . round ( 1200 * scale ) ;
674- var newHeight = Math . round ( 900 * scale ) ;
675-
676- // Update the dimensions
677- this . _w = newWidth ;
678- this . _h = newHeight ;
679+ if ( this . _resizeTimer ) {
680+ clearTimeout ( this . _resizeTimer ) ;
681+ }
682+
683+ this . _resizeTimer = setTimeout ( ( ) => {
684+ // Skip dimension updates when the tab is hidden
685+ // (canvas reports 0x0 in background tabs)
686+ if ( document . hidden ) {
687+ return ;
688+ }
689+
690+ // Call the updateDimensions function when resizing occurs
691+ var screenWidth =
692+ window . innerWidth ||
693+ document . documentElement . clientWidth ||
694+ document . body . clientWidth ;
695+ var screenHeight =
696+ window . innerHeight ||
697+ document . documentElement . clientHeight ||
698+ document . body . clientHeight ;
699+
700+ // Guard against zero or invalid dimensions
701+ if ( screenWidth <= 0 || screenHeight <= 0 ) {
702+ return ;
703+ }
704+
705+ // Set a scaling factor to adjust the dimensions based on the screen size
706+ var scale = Math . min ( screenWidth / 1200 , screenHeight / 900 ) ;
707+
708+ // Calculate the new dimensions
709+ var newWidth = Math . round ( 1200 * scale ) ;
710+ var newHeight = Math . round ( 900 * scale ) ;
711+
712+ // Update the dimensions
713+ this . _w = newWidth ;
714+ this . _h = newHeight ;
715+ } , 150 ) ;
679716 } ) ;
680717 }
681718
@@ -736,6 +773,17 @@ Turtles.TurtlesView = class {
736773 const color =
737774 index === - 1 ? platformColor . background : this . getTurtle ( index ) . painter . canvasColor ;
738775 this . _backgroundColor = color ;
776+ // Update DOM body and canvas styles so tests and themes reflect the change.
777+ try {
778+ if ( typeof document !== "undefined" && document . body && document . body . style ) {
779+ document . body . style . backgroundColor = this . _backgroundColor ;
780+ }
781+ if ( this . activity && this . activity . canvas && this . activity . canvas . style ) {
782+ this . activity . canvas . style . backgroundColor = this . _backgroundColor ;
783+ }
784+ } catch ( e ) {
785+ // Ignore errors in non-browser environments
786+ }
739787 this . makeBackground ( ) ;
740788 this . activity . refreshCanvas ( ) ;
741789 }
@@ -837,7 +885,9 @@ Turtles.TurtlesView = class {
837885 const borderContainer = this . borderContainer ;
838886
839887 // Remove any old background containers
840- borderContainer . removeAllChildren ( ) ;
888+ if ( borderContainer && typeof borderContainer . removeAllChildren === "function" ) {
889+ borderContainer . removeAllChildren ( ) ;
890+ }
841891
842892 const turtlesStage = this . stage ;
843893 // We put the buttons on the stage so they will be on top
@@ -848,7 +898,7 @@ Turtles.TurtlesView = class {
848898 container . setAttribute ( "class" , "tooltipped" ) ;
849899 container . setAttribute ( "data-tooltip" , object . label ) ;
850900 container . setAttribute ( "data-position" , "bottom" ) ;
851- jQuery . noConflict ( ) ( ".tooltipped" ) . tooltip ( {
901+ window . jQuery ( ".tooltipped" ) . tooltip ( {
852902 html : true ,
853903 delay : 100
854904 } ) ;
@@ -869,7 +919,7 @@ Turtles.TurtlesView = class {
869919 }
870920 } ;
871921 const img = new Image ( ) ;
872- img . src = "data:image/svg+xml;base64," + window . btoa ( base64Encode ( svg ) ) ;
922+ img . src = "data:image/svg+xml;base64," + window . btoa ( _base64Encode ( svg ) ) ;
873923
874924 container . appendChild ( img ) ;
875925 container . setAttribute (
@@ -918,7 +968,7 @@ Turtles.TurtlesView = class {
918968 this . _collapsedBoundary . visible = true ;
919969 this . _expandedBoundary . visible = false ;
920970 turtlesStage . x = ( this . _w * 3 ) / 4 - 10 ;
921- turtlesStage . y = 55 + LEADING + 6 ;
971+ turtlesStage . y = 55 + LEADING_FALLBACK + 6 ;
922972 this . _isShrunk = true ;
923973
924974 for ( let i = 0 ; i < this . getTurtleCount ( ) ; i ++ ) {
@@ -948,7 +998,7 @@ Turtles.TurtlesView = class {
948998 label : _ ( "Grid" )
949999 } ,
9501000 this . _w - 10 - 3 * 55 ,
951- 70 + LEADING + 6
1001+ 70 + LEADING_FALLBACK + 6
9521002 ) ;
9531003 const that = this ;
9541004 this . gridButton . onclick = ( ) => {
@@ -965,7 +1015,7 @@ Turtles.TurtlesView = class {
9651015 label : _ ( "Clear" )
9661016 } ,
9671017 this . _w - 5 - 2 * 55 ,
968- 70 + LEADING + 6
1018+ 70 + LEADING_FALLBACK + 6
9691019 ) ;
9701020
9711021 // Assign click listener to the Clear button
@@ -986,7 +1036,7 @@ Turtles.TurtlesView = class {
9861036 label : _ ( "Collapse" )
9871037 } ,
9881038 this . _w - 55 ,
989- 70 + LEADING + 6
1039+ 70 + LEADING_FALLBACK + 6
9901040 ) ;
9911041
9921042 this . _collapseButton . onclick = ( ) => {
@@ -1054,7 +1104,7 @@ Turtles.TurtlesView = class {
10541104 label : _ ( "Expand" )
10551105 } ,
10561106 this . _w - 55 ,
1057- 70 + LEADING + 6
1107+ 70 + LEADING_FALLBACK + 6
10581108 ) ;
10591109 if ( this . _expandButton !== null ) {
10601110 this . _expandButton . style . visibility = "hidden" ;
@@ -1138,7 +1188,7 @@ Turtles.TurtlesView = class {
11381188 const __makeAllButtons = ( ) => {
11391189 let second = false ;
11401190 if ( docById ( "buttoncontainerTOP" ) ) {
1141- jQuery . noConflict ( ) ( ".tooltipped" ) . tooltip ( "close" ) ;
1191+ window . jQuery ( ".tooltipped" ) . tooltip ( "close" ) ;
11421192 docById ( "buttoncontainerTOP" ) . parentElement . removeChild (
11431193 docById ( "buttoncontainerTOP" )
11441194 ) ;
@@ -1155,7 +1205,7 @@ Turtles.TurtlesView = class {
11551205 jQuery
11561206 . noConflict ( ) ( ".tooltipped" )
11571207 . each ( function ( ) {
1158- jQuery . noConflict ( ) ( this ) . tooltip ( {
1208+ window . jQuery ( this ) . tooltip ( {
11591209 html : true ,
11601210 delay : 100
11611211 } ) ;
@@ -1190,18 +1240,19 @@ Turtles.TurtlesView = class {
11901240
11911241 this . _collapsedBoundary = new createjs . Bitmap ( img ) ;
11921242 this . _collapsedBoundary . x = 0 ;
1193- this . _collapsedBoundary . y = 55 + LEADING ;
1243+ this . _collapsedBoundary . y = 55 + LEADING_FALLBACK ;
11941244 borderContainer . addChild ( this . _collapsedBoundary ) ;
11951245 this . _collapsedBoundary . visible = false ;
11961246 } ;
11971247
11981248 const dx = this . _w - 20 ;
1199- const dy = this . _h - 55 - LEADING ;
1249+ const dy = this . _h - 55 - LEADING_FALLBACK ;
12001250 img . src =
12011251 "data:image/svg+xml;base64," +
12021252 window . btoa (
1203- base64Encode (
1204- MBOUNDARY . replace ( "HEIGHT" , this . _h )
1253+ _base64Encode (
1254+ _MBOUNDARY
1255+ . replace ( "HEIGHT" , this . _h )
12051256 . replace ( "WIDTH" , this . _w )
12061257 . replace ( "Y" , 10 )
12071258 . replace ( "X" , 10 )
@@ -1229,18 +1280,19 @@ Turtles.TurtlesView = class {
12291280
12301281 this . _expandedBoundary = new createjs . Bitmap ( img ) ;
12311282 this . _expandedBoundary . x = 0 ;
1232- this . _expandedBoundary . y = 55 + LEADING ;
1283+ this . _expandedBoundary . y = 55 + LEADING_FALLBACK ;
12331284 borderContainer . addChild ( this . _expandedBoundary ) ;
12341285 __makeBoundary2 ( ) ;
12351286 } ;
12361287
12371288 const dx = this . _w - 5 ;
1238- const dy = this . _h - 55 - LEADING ;
1289+ const dy = this . _h - 55 - LEADING_FALLBACK ;
12391290 img . src =
12401291 "data:image/svg+xml;base64," +
12411292 window . btoa (
1242- base64Encode (
1243- MBOUNDARY . replace ( "HEIGHT" , this . _h )
1293+ _base64Encode (
1294+ _MBOUNDARY
1295+ . replace ( "HEIGHT" , this . _h )
12441296 . replace ( "WIDTH" , this . _w )
12451297 . replace ( "Y" , 10 / CONTAINERSCALEFACTOR )
12461298 . replace ( "X" , 10 / CONTAINERSCALEFACTOR )
@@ -1292,3 +1344,7 @@ Turtles.TurtlesView = class {
12921344if ( typeof module !== "undefined" && module . exports ) {
12931345 module . exports = Turtles ;
12941346}
1347+
1348+ if ( typeof window !== "undefined" ) {
1349+ window . Turtles = Turtles ;
1350+ }
0 commit comments