Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions css/activities.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,72 @@
font-size: large;
}

.trash-view {
position: relative;
background-color: white;
max-width: 396px;
max-height: 200px;
overflow-y: auto;
font-size: 16px;
color: black;
border: 2px solid #87cefa;
list-style-type: none;
margin: 0;
padding: 0;
text-align: left;
}

.button-container {
position: sticky;
display: flex;
justify-content: space-between;
top: 0;
z-index: 10;
display: flex;
gap: 10px;
background: #2196F3;
margin: 0;
padding: 5px;
border-bottom: 1px solid #d9d9d9;
}

.trash-item {
padding: 2px 12px;
margin: 1px 0;
border-radius: 4px;
transition: background-color 0.3s;
}

.trash-item.hover {
background-color: #d9d9d9;
}

.trash-item-icon {
width: 30px;
height: 30px;
margin-right: 10px;
vertical-align: middle;
}

#restoreLastIcon, #restoreAllIcon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
cursor: pointer;
}


.material-icons.md-48 {
font-size: 32px;
}


.hidden {
display: none;
}

.ui-menu {
position: relative;
background-color: rgba(255, 255, 255, 1);
Expand Down Expand Up @@ -982,6 +1048,7 @@ table {
#helpBodyDiv .message {
text-align: center;
line-height: 1.2;
margin: auto;
}

#helpBodyDiv .icon-container {
Expand Down
39 changes: 23 additions & 16 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,26 @@
</script>
<div class="loading-text" id="loadingText" style="margin-top:1.5rem;"></div>
<script>
document.addEventListener("DOMContentLoaded", function() {
setTimeout(function() {
const loadingText = document.getElementById("loadingText");
const texts = ["Do, Re, Mi, Fa, Sol, La, Ti, Do", "Loading Music Blocks...", "Reading Music..."];

let index = 0;
setInterval(function() {
loadingText.textContent = texts[index];
index = (index + 1) % texts.length;
}, 2000); // Change 2000 to adjust cycle duration (in milliseconds)

// Show the loading text after 6 seconds
loadingText.style.opacity = 1;
}, 4000); // Change 6000 to adjust the delay (in milliseconds)
});
document.addEventListener("DOMContentLoaded", function () {
setTimeout(function () {
const loadingText = document.getElementById("loadingText");
const texts = [_("Do, Re, Mi, Fa, Sol, La, Ti, Do"), _("Loading Music Blocks..."), _("Reading Music...")];
let index = 0;

const intervalId = setInterval(function () {
loadingText.textContent = texts[index];
index = (index + 1) % texts.length;
}, 1500);

// Stop changing text and finalize loading after 6 seconds
setTimeout(function () {
clearInterval(intervalId);
loadingText.textContent = _("Loading Complete!");
loadingText.style.opacity = 1;
}, 6000);
}, 4000);
});

</script>
</div>

Expand Down Expand Up @@ -515,7 +520,7 @@
<div
id="mb-logo"
class="logo left tooltipped"
style="line-height: 0; height: 100%; padding-right: 0;"
style="display: flex; align-items: center; line-height: 0; height: 100%; padding-right: 0;"
data-position="bottom"
>
<amp-img
Expand Down Expand Up @@ -780,6 +785,7 @@
>
<a style="color: transparent;">space&nbsp;&nbsp;&nbsp;</a>
</li>

<li>
<a
id="restoreIcon"
Expand All @@ -789,6 +795,7 @@
>restore_from_trash</i
></a
>
<div id="trashList"></div>
</li>
<li>
<a
Expand Down
2 changes: 2 additions & 0 deletions js/SaveInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ class SaveInterface {
* @instance
*/
afterSaveLilypond(filename) {
filename = docById("fileName").value;
const ly = saveLilypondOutput(this.activity);
switch (this.notationConvert) {
case "pdf":
Expand All @@ -559,6 +560,7 @@ class SaveInterface {
*/

afterSaveLilypondLY(lydata, filename) {
filename = docById("fileName").value;
if (platform.FF) {
// eslint-disable-next-line no-console
console.debug('execCommand("copy") does not work on FireFox');
Expand Down
71 changes: 71 additions & 0 deletions js/__tests__/basicblocks.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const { initBasicProtoBlocks, BACKWARDCOMPATIBILIYDICT } = require('../basicblocks');

const mockActivity = {
blocks: {
palettes: {},
protoBlockDict: {
block1: { palette: { add: jest.fn() } },
block2: { palette: { add: jest.fn() } },
blockWithoutPalette: {}
}
},
palettes: {},
};

const setupFunctions = [
'setupRhythmBlockPaletteBlocks', 'setupRhythmBlocks', 'setupMeterBlocks',
'setupPitchBlocks', 'setupIntervalsBlocks', 'setupToneBlocks',
'setupOrnamentBlocks', 'setupVolumeBlocks', 'setupDrumBlocks',
'setupWidgetBlocks', 'setupFlowBlocks', 'setupNumberBlocks',
'setupActionBlocks', 'setupBoxesBlocks', 'setupBooleanBlocks',
'setupHeapBlocks', 'setupDictBlocks', 'setupExtrasBlocks',
'setupProgramBlocks', 'setupGraphicsBlocks', 'setupPenBlocks',
'setupMediaBlocks', 'setupSensorsBlocks', 'setupEnsembleBlocks'
];

setupFunctions.forEach(fnName => {
global[fnName] = jest.fn();
});

describe('initBasicProtoBlocks', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should assign palettes to activity.blocks.palettes', () => {
initBasicProtoBlocks(mockActivity);
expect(mockActivity.blocks.palettes).toBe(mockActivity.palettes);
});

it('should call all setup functions with activity', () => {
initBasicProtoBlocks(mockActivity);
setupFunctions.forEach(fnName => {
expect(global[fnName]).toHaveBeenCalledWith(mockActivity);
});
});

it('should add blocks with palettes to their respective palettes', () => {
initBasicProtoBlocks(mockActivity);

expect(mockActivity.blocks.protoBlockDict.block1.palette.add).toHaveBeenCalledWith(
mockActivity.blocks.protoBlockDict.block1
);
expect(mockActivity.blocks.protoBlockDict.block2.palette.add).toHaveBeenCalledWith(
mockActivity.blocks.protoBlockDict.block2
);
expect(mockActivity.blocks.protoBlockDict.blockWithoutPalette.palette).toBeUndefined();
});
});

describe('BACKWARDCOMPATIBILIYDICT', () => {
it('should be defined and not empty', () => {
expect(BACKWARDCOMPATIBILIYDICT).toBeDefined();
expect(Object.keys(BACKWARDCOMPATIBILIYDICT).length).toBeGreaterThan(0);
});

it('should correctly map old block names to new block names', () => {
expect(BACKWARDCOMPATIBILIYDICT.fullscreen).toBe('vspace');
expect(BACKWARDCOMPATIBILIYDICT.seth).toBe('setheading');
expect(BACKWARDCOMPATIBILIYDICT.random2).toBe('random');
});
});
90 changes: 90 additions & 0 deletions js/__tests__/boundary.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
global.base64Encode = jest.fn((str) => str);
global.createjs = {
Container: jest.fn(() => ({
children: [],
addChild: jest.fn(),
removeChild: jest.fn(),
visible: true,
})),
Bitmap: jest.fn(),
};

global.window = {
btoa: jest.fn((str) => Buffer.from(str).toString("base64")),
};

global.BOUNDARY = `
<svg xmlns="http://www.w3.org/2000/svg" width="WIDTH" height="HEIGHT">
<rect x="X" y="Y" width="DX" height="DY" stroke="stroke_color" fill="none" stroke-width="2"/>
</svg>
`;

const Boundary = require('../boundary');

describe('Boundary Class', () => {
let stage;
let boundary;

beforeEach(() => {
stage = {
addChild: jest.fn(),
setChildIndex: jest.fn(),
};

boundary = new Boundary(stage);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should initialize with a container and add it to the stage', () => {
expect(stage.addChild).toHaveBeenCalledWith(boundary._container);
expect(stage.setChildIndex).toHaveBeenCalledWith(boundary._container, 0);
});

it('should call create and destroy methods when setting scale', () => {
const createSpy = jest.spyOn(boundary, 'create');
const destroySpy = jest.spyOn(boundary, 'destroy');

boundary.setScale(800, 600, 2);

expect(destroySpy).toHaveBeenCalled();
expect(createSpy).toHaveBeenCalledWith(800, 600, 2);
});

it('should correctly determine if a point is off-screen', () => {
boundary.create(800, 600, 2);

expect(boundary.offScreen(50, 50)).toBe(true);
expect(boundary.offScreen(boundary.x + 1, boundary.y + 1)).toBe(false);
expect(boundary.offScreen(boundary.x + boundary.dx + 1, boundary.y + boundary.dy + 1)).toBe(true);
});

it('should hide and show the container', () => {
boundary.hide();
expect(boundary._container.visible).toBe(false);

boundary.show();
expect(boundary._container.visible).toBe(true);
});

it('should destroy the first child in the container', () => {
const childMock = {};
boundary._container.children.push(childMock);

boundary.destroy();
expect(boundary._container.removeChild).toHaveBeenCalledWith(childMock);
});

it('should create a boundary with the correct dimensions and add it to the container', () => {
const mockImage = { onload: null, src: '' };
const imgMock = jest.spyOn(global, 'Image').mockImplementation(() => mockImage);

boundary.create(800, 600, 2);

expect(mockImage.onload).not.toBeNull();
expect(mockImage.src).toContain('data:image/svg+xml;base64,');
imgMock.mockRestore();
});
});
Loading
Loading