Skip to content

Commit 9356d32

Browse files
author
Faisal Mehmood
committed
turtles: add fallbacks for LEADING/base64Encode/MBOUNDARY and fix utils.js unreachable code
1 parent 60acb02 commit 9356d32

File tree

2 files changed

+244
-130
lines changed

2 files changed

+244
-130
lines changed

js/turtles.js

Lines changed: 100 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@
3030

3131
// What is the scale factor when stage is shrunk?
3232
const 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 {
12921344
if (typeof module !== "undefined" && module.exports) {
12931345
module.exports = Turtles;
12941346
}
1347+
1348+
if (typeof window !== "undefined") {
1349+
window.Turtles = Turtles;
1350+
}

0 commit comments

Comments
 (0)