Skip to content

Commit 2d2de21

Browse files
authored
Merge branch 'sugarlabs:master' into master
2 parents 2e29f03 + 8a36b92 commit 2d2de21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+324646
-315498
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Security Scans
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
10+
jobs:
11+
security-scans:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout Code
16+
uses: actions/checkout@v3
17+
18+
- name: Setup Node.js
19+
uses: actions/setup-node@v3
20+
with:
21+
node-version: '18'
22+
23+
- name: Install Dependencies
24+
run: npm install
25+
26+
- name: Run npm Audit
27+
run: npm audit --audit-level=high || echo "npm audit failed"

documentation/README.md

Lines changed: 231 additions & 100 deletions
Large diffs are not rendered by default.

jest.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
testMatch: ['**/__tests__/**/*.test.js', '**/?(*.)+(spec|test).[jt]s?(x)'],
3+
clearMocks: true,
4+
moduleFileExtensions: ['js', 'json', 'node'],
5+
};

js/abc.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const processABCNotes = function(logo, turtle) {
5454
* @param {string|number} note - The musical note to convert. It can be a string note (e.g., 'C#') or a frequency (number).
5555
* @returns {string} The note converted to ABC notation.
5656
*/
57+
5758
const __toABCnote = (note) => {
5859
// beams -- no space between notes
5960
// ties use ()
@@ -64,41 +65,47 @@ const processABCNotes = function(logo, turtle) {
6465
// Also, notes must be lowercase.
6566
// And the octave bounday is at C, not A.
6667

68+
const OCTAVE_NOTATION_MAP = {
69+
10: "'''''",
70+
9: "''''",
71+
8: "'''",
72+
7: "''",
73+
6: "'",
74+
5: "",
75+
4: "",
76+
3: ",",
77+
2: ",,",
78+
1: ",,,"
79+
};
80+
81+
const ACCIDENTAL_MAP = {
82+
"♯": "^",
83+
"♭": "_"
84+
};
85+
86+
// Handle frequency conversion
6787
if (typeof note === "number") {
6888
const pitchObj = frequencyToPitch(note);
6989
note = pitchObj[0] + pitchObj[1];
7090
}
7191

72-
const replacements = {
73-
"♯": "^",
74-
"♭": "_",
75-
"10": "'''''",
76-
"9": "''''",
77-
"8": "'''",
78-
"7": "''",
79-
"6": "'",
80-
"5": "",
81-
"4": "",
82-
"3": ",",
83-
"2": ",,",
84-
"1": ",,,"
85-
};
92+
// Handle accidentals first
93+
for (const [symbol, replacement] of Object.entries(ACCIDENTAL_MAP)) {
94+
note = note.replace(new RegExp(symbol, "g"), replacement);
95+
}
8696

87-
for (const [key, value] of Object.entries(replacements)) {
88-
if (note.includes(key)) {
89-
note = note.replace(new RegExp(key, "g"), value);
90-
if (key.length === 1) break;
97+
// Handle octave notation
98+
for (const [octave, notation] of Object.entries(OCTAVE_NOTATION_MAP)) {
99+
if (note.includes(octave)) {
100+
note = note.replace(new RegExp(octave, "g"), notation);
101+
break; // Only one octave notation should apply
91102
}
92103
}
93104

94-
// Convert to uppercase or lowercase based on the octave
95-
if (note.includes("'''") || note.includes("''") || note.includes("'") || note.includes("")) {
96-
return note.toLowerCase();
97-
} else {
98-
return note.toUpperCase();
99-
}
105+
// Convert case based on octave
106+
return note.includes("'") || note === "" ? note.toLowerCase() : note.toUpperCase();
100107
};
101-
108+
102109
let counter = 0;
103110
let queueSlur = false;
104111
let articulation = false;

js/activity.js

Lines changed: 121 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,14 @@ class Activity {
479479
if (docById("helpfulWheelDiv").style.display !== "none") {
480480
docById("helpfulWheelDiv").style.display = "none";
481481
}
482-
if (docById("helpfulSearchDiv").style.display !== "none" && e.target.id !== "helpfulSearch") {
482+
if (docById("helpfulSearchDiv").style.display !== "none" && !docById("helpfulSearchDiv").contains(e.target) && e.target.id !== "helpfulSearch") {
483483
docById("helpfulSearchDiv").style.display = "none";
484484
}
485485
that.__tick();
486486
}
487487

488+
document.addEventListener("click", this._hideHelpfulSearchWidget);
489+
488490
/*
489491
* Sets up right click functionality opening the context menus
490492
* (if block is right clicked)
@@ -1015,54 +1017,125 @@ class Activity {
10151017
/*
10161018
* Clears "canvas"
10171019
*/
1018-
this._allClear = (noErase) => {
1019-
this.blocks.activeBlock = null;
1020-
hideDOMLabel();
1021-
1022-
this.logo.boxes = {};
1023-
this.logo.time = 0;
1024-
this.hideMsgs();
1025-
this.hideGrids();
1026-
this.turtles.setBackgroundColor(-1);
1027-
this.logo.svgOutput = "";
1028-
this.logo.notationOutput = "";
1029-
for (let turtle = 0; turtle < this.turtles.turtleList.length; turtle++) {
1030-
this.logo.turtleHeaps[turtle] = [];
1031-
this.logo.turtleDicts[turtle] = {};
1032-
this.logo.notation.notationStaging[turtle] = [];
1033-
this.logo.notation.notationDrumStaging[turtle] = [];
1034-
if (noErase === undefined || !noErase) {
1035-
this.turtles.turtleList[turtle].painter.doClear(true, true, true);
1020+
const renderClearConfirmation = (clearCanvasAction) => {
1021+
// Create a custom modal for confirmation
1022+
const modal = document.createElement("div");
1023+
modal.style.position = "fixed";
1024+
modal.style.top = "50%";
1025+
modal.style.left = "50%";
1026+
modal.style.transform = "translate(-50%, -50%)";
1027+
modal.style.width = "400px";
1028+
modal.style.padding = "24px";
1029+
modal.style.backgroundColor = "#fff";
1030+
modal.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.2)";
1031+
modal.style.borderRadius = "8px";
1032+
modal.style.zIndex = "10000";
1033+
modal.style.textAlign = "left";
1034+
const title = document.createElement("h2");
1035+
title.textContent = "Clear Workspace";
1036+
title.style.color = "#0066FF";
1037+
title.style.fontSize = "24px";
1038+
title.style.margin = "0 0 16px 0";
1039+
modal.appendChild(title);
1040+
const message = document.createElement("p");
1041+
message.textContent = "Are you sure you want to clear the workspace?";
1042+
message.style.color = "#666666";
1043+
message.style.fontSize = "16px";
1044+
message.style.marginBottom = "24px";
1045+
modal.appendChild(message);
1046+
// Add buttons
1047+
const buttonContainer = document.createElement("div");
1048+
buttonContainer.style.display = "flex";
1049+
buttonContainer.style.justifyContent = "flex-start";
1050+
1051+
const confirmBtn = document.createElement("button");
1052+
confirmBtn.textContent = "Confirm";
1053+
confirmBtn.style.backgroundColor = "#2196F3";
1054+
confirmBtn.style.color = "white";
1055+
confirmBtn.style.border = "none";
1056+
confirmBtn.style.borderRadius = "4px";
1057+
confirmBtn.style.padding = "8px 16px";
1058+
confirmBtn.style.fontWeight = "bold";
1059+
confirmBtn.style.cursor = "pointer";
1060+
confirmBtn.style.marginRight = "16px";
1061+
confirmBtn.addEventListener("click", () => {
1062+
document.body.removeChild(modal);
1063+
clearCanvasAction();
1064+
});
1065+
1066+
const cancelBtn = document.createElement("button");
1067+
cancelBtn.textContent = "Cancel";
1068+
cancelBtn.style.backgroundColor = "#f1f1f1";
1069+
cancelBtn.style.color = "black";
1070+
cancelBtn.style.border = "none";
1071+
cancelBtn.style.borderRadius = "4px";
1072+
cancelBtn.style.padding = "8px 16px";
1073+
cancelBtn.style.fontWeight = "bold";
1074+
cancelBtn.style.cursor = "pointer";
1075+
cancelBtn.addEventListener("click", () => {
1076+
document.body.removeChild(modal);
1077+
});
1078+
1079+
buttonContainer.appendChild(confirmBtn);
1080+
buttonContainer.appendChild(cancelBtn);
1081+
modal.appendChild(buttonContainer);
1082+
document.body.appendChild(modal);
1083+
};
1084+
1085+
this._allClear = (noErase, skipConfirmation = false) => {
1086+
const clearCanvasAction = () => {
1087+
this.blocks.activeBlock = null;
1088+
hideDOMLabel();
1089+
1090+
this.logo.boxes = {};
1091+
this.logo.time = 0;
1092+
this.hideMsgs();
1093+
this.hideGrids();
1094+
this.turtles.setBackgroundColor(-1);
1095+
this.logo.svgOutput = "";
1096+
this.logo.notationOutput = "";
1097+
for (let turtle = 0; turtle < this.turtles.turtleList.length; turtle++) {
1098+
this.logo.turtleHeaps[turtle] = [];
1099+
this.logo.turtleDicts[turtle] = {};
1100+
this.logo.notation.notationStaging[turtle] = [];
1101+
this.logo.notation.notationDrumStaging[turtle] = [];
1102+
if (noErase === undefined || !noErase) {
1103+
this.turtles.turtleList[turtle].painter.doClear(true, true, true);
1104+
}
10361105
}
1037-
}
1038-
1039-
this.blocksContainer.x = 0;
1040-
this.blocksContainer.y = 0;
1041-
1042-
// Code specific to cleaning up Music Blocks
1043-
Element.prototype.remove = () => {
1044-
this.parentElement.removeChild(this);
1045-
};
1046-
1047-
NodeList.prototype.remove = HTMLCollection.prototype.remove = () => {
1048-
for (let i = 0, len = this.length; i < len; i++) {
1049-
if (this[i] && this[i].parentElement) {
1050-
this[i].parentElement.removeChild(this[i]);
1106+
1107+
this.blocksContainer.x = 0;
1108+
this.blocksContainer.y = 0;
1109+
1110+
Element.prototype.remove = () => {
1111+
this.parentElement.removeChild(this);
1112+
};
1113+
1114+
NodeList.prototype.remove = HTMLCollection.prototype.remove = () => {
1115+
for (let i = 0, len = this.length; i < len; i++) {
1116+
if (this[i] && this[i].parentElement) {
1117+
this[i].parentElement.removeChild(this[i]);
1118+
}
10511119
}
1120+
};
1121+
1122+
const table = docById("myTable");
1123+
if (table !== null) {
1124+
table.remove();
1125+
}
1126+
1127+
if (docById("helpfulWheelDiv").style.display !== "none") {
1128+
docById("helpfulWheelDiv").style.display = "none";
1129+
this.__tick();
10521130
}
10531131
};
1054-
1055-
const table = docById("myTable");
1056-
if (table !== null) {
1057-
table.remove();
1058-
}
1059-
1060-
if (docById("helpfulWheelDiv").style.display !== "none") {
1061-
docById("helpfulWheelDiv").style.display = "none";
1062-
this.__tick();
1132+
1133+
if (skipConfirmation) {
1134+
clearCanvasAction();
1135+
} else {
1136+
renderClearConfirmation(clearCanvasAction);
10631137
}
10641138
};
1065-
10661139
/**
10671140
* Sets up play button functionality; runs Music Blocks.
10681141
* @param env {specifies environment}
@@ -1446,6 +1519,8 @@ class Activity {
14461519
else if (ele.label === "Disable horizontal scrolling")
14471520
ele.display = true;
14481521
})
1522+
activity.textMsg(("Horizontal scrolling enabled."), 3000);
1523+
14491524
} else {
14501525
enableHorizScrollIcon.style.display = "block";
14511526
disableHorizScrollIcon.style.display = "none";
@@ -1456,6 +1531,8 @@ class Activity {
14561531
else if (ele.label === "Disable horizontal scrolling")
14571532
ele.display = false;
14581533
})
1534+
activity.textMsg(("Horizontal scrolling disabled."), 3000);
1535+
14591536
}
14601537
};
14611538

@@ -3651,7 +3728,7 @@ class Activity {
36513728
document.querySelector("#myOpenFile").click();
36523729
window.scroll(0, 0);
36533730
doHardStopButton(that);
3654-
that._allClear(true);
3731+
that._allClear(true, true);
36553732
};
36563733

36573734
window.prepareExport = this.prepareExport;

js/base64Utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ function base64Decode(str) {
2828
}
2929

3030
export default { base64Encode, base64Decode };
31+
//module.exports = { base64Encode, base64Decode };

0 commit comments

Comments
 (0)