Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- Fix version links in CHANGELOG.md
- Fix refresh icon font
- Add code comments
- Return always boolean in `setLocalStorageItem()`

## [0.1.4][] - 2022-05-04

Expand Down
6 changes: 4 additions & 2 deletions public/css/notes.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
.note[data-content='c4']::after,
.note[data-content='h3']::after,
.note[data-content='e2']::after,
.note[data-content='d2']::after {
.note[data-content='d2']::after,
.note[data-content='a5']::after {
content: '';
position: absolute;
background: #0f172a;
Expand All @@ -57,6 +58,7 @@
}

.note[data-content='c4']::after,
.note[data-content='e2']::after {
.note[data-content='e2']::after,
.note[data-content='a5']::after {
top: 50%;
}
1 change: 1 addition & 0 deletions public/game.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<script src="https://cdn.jsdelivr.net/npm/vexflow/build/cjs/vexflow.js"></script>
<script src="js/general.js"></script>
<script src="js/storage.js"></script>
<script src="js/generation.js"></script>
<script src="js/notes.js"></script>
<script src="js/game.js"></script>
</body>
Expand Down
40 changes: 40 additions & 0 deletions public/js/general.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
'use strict';

/**
* Alias for document.getElementById
* @param {!string} id - Element's id
* @returns {Element}
*/
const getElement = (id) => document.getElementById(id);

/**
* Alias for document.createElement
* @param {string} [tag=div] - HTML tag
* @returns {Element}
*/
const createElement = (tag = 'div') => document.createElement(tag);

/**
* An object that describes the basic properties
* of an HTML element to be created
* @typedef {Object} CustomElement
* @property {!string} tag - HTML-tag
* @property {!string} name - Element's identification string
* @property {string[]} [classes] - List of classes to add
* @property {Object.<string, string>[]} [attributes] - List of atttributes to add
* @property {string} [parent] - The parent of the object to which the item will be added.
* name of the element, which is in the array before
*/

/**
* Quick creation of HTML elements
* @param {CustomElement[]} data - Elements to create
* @returns {Object.<string, Element>}
*/
const createElements = (data) => {
const resultElements = {};
for (const { tag, classes, attributes, name, parent } of data) {
Expand All @@ -19,12 +46,25 @@ const createElements = (data) => {
return resultElements;
};

/**
* Finds data by the path (eg. "path.to.string")
* @param {Object} object - Object, to search in
* @param {string} path - Path (eg. "path.to.string")
* @returns {any} - Data that are by the path stored
*/
const resolvePath = (object, path) => {
const steps = path.split('.');
for (const step of steps) object = object[step];
return object;
};

/**
* Updates value in object by the path (eg. "path.to.string")
* @param {Object} object - Object, to search in
* @param {string} path - Path (eg. "path.to.string")
* @param {any} value - New value
* @returns {undefined}
*/
const updateValueByPath = (object, path, value) =>
path.split('.').reduce((o, p, i) => (o[p] = path.split('.').length === ++i ? value : o[p] || {}), object);

Expand Down
31 changes: 30 additions & 1 deletion public/js/notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,59 @@

const NOTES_PX_OFFSET = 10;
const mainNotes = { g: ['e', 5], f: ['g', 3] };

const naturalNotes = ['c', 'd', 'e', 'f', 'g', 'a', 'h'];

/**
* @typedef {('f'|'g')} Clef
*/

/**
* Get margin for note (px)
* @param {string!} note - Note (eg. C#4, Eb5, A2)
* @param {Clef} clef - Clef
* @returns {number} - Amount of px for element to be omit
*/
const getMargin = (note, clef) => {
// Parse given note. Get natural and ocatve
const [naturalNote, octave] = [note[0].toLowerCase(), +note[note.length - 1]];
const naturalNoteIndex = naturalNotes.findIndex((n) => n === naturalNote);
// The main note is the one between the first and second line (top: 0px)
// Parse its natural value and octave
const [mainNote, mainOctave] = mainNotes[clef];
const mainNoteIndex = naturalNotes.findIndex((n) => n === mainNote);
// If the note is in the same octave as the main note,
// return the index difference
if (mainOctave === octave) {
const result = (mainNoteIndex - naturalNoteIndex) * NOTES_PX_OFFSET;
// If the note equals the main note and the result is 0, then return 1.
// This is because the distance between the lines is 19px
// and there is a 1px hole, when we use top: 0px;
return result === 0 ? 1 : result;
}
// Count the number of notes that are between the note and the main one
const octaveAmount = Math.abs(mainOctave - octave);
const notesAmount = octaveAmount * naturalNotes.length;
return (notesAmount - naturalNoteIndex + mainNoteIndex) * NOTES_PX_OFFSET;
};

/**
* Creates an element with stave, specified clef and notes
* @param {string!} containerId - Id of an element, to append stave in
* @param {Object} task - Task object
* @param {Clef} task.clef - Clef
* @param {string[]} task.notes - Notes
*/
const createStave = (containerId, { clef, notes }) => {
// Check the container
const container = window.getElement(containerId);
if (!container) throw new Error('Element not found');
// Create array of custom elements for quick creation
const elementsToCreate = [
{ name: 'stave', classes: ['stave'] },
{ name: 'clefElement', classes: ['clef', clef], parent: 'stave' },
{ name: 'notesContainer', classes: ['notes'], parent: 'stave' },
];
// Add every note to stave
for (const [i, note] of Object.entries(notes)) {
const margin = getMargin(note, clef);
elementsToCreate.push({
Expand All @@ -36,6 +64,7 @@ const createStave = (containerId, { clef, notes }) => {
parent: 'notesContainer',
});
}
// Append stave into container
const { stave } = window.createElements(elementsToCreate);
container.appendChild(stave);
};
Expand Down
25 changes: 24 additions & 1 deletion public/js/storage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
'use strict';

/**
* Gets JSON from local storage and parses it
* @param {string!} key - Key
* @param {boolean} [deleteOnError=true] - Whether to delete the current value of an element
* if an error occurred while parsing it
* @returns {Object}
*/
const getLocalStorageItem = (key, deleteOnError = true) => {
try {
const raw = window.localStorage.getItem(key);
Expand All @@ -12,18 +19,34 @@ const getLocalStorageItem = (key, deleteOnError = true) => {
}
};

/**
* Set the new value of the item in local storage.
* The data passes the stringify
* @param {string!} key - Key
* @param {Object} data - JSON data to store
* @returns {boolean} - Whether or not it's successful
*/
const setLocalStorageItem = (key, data) => {
try {
const stringifiedData = JSON.stringify(data);
window.localStorage.setItem(key, stringifiedData);
return true;
} catch (err) {
console.error(err);
return null;
return false;
}
};

/**
* Alias for getLocalStorageItem('settings')
* @returns {Settings}
*/
const getSettings = () => getLocalStorageItem('settings');
/**
* Alias for setLocalStorageItem('settings', newData);
* @param {Settings} [newData=window.gameSettings] - New settings object
* @returns {boolean} - Whether or not it's successful
*/
const updateSettings = (newData = window.gameSettings) => setLocalStorageItem('settings', newData);

const getState = () => getLocalStorageItem('state');
Expand Down