Skip to content

Commit

Permalink
Merge pull request #1153 from DDMAL/blank-mei
Browse files Browse the repository at this point in the history
Feat: allow user to create new MEI files
  • Loading branch information
yinanazhou authored Dec 12, 2023
2 parents cc89af5 + 224f885 commit 5bdffbe
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 102 deletions.
52 changes: 26 additions & 26 deletions assets/js/verovio-toolkit.js

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions deployment/server/samples/mei/mei_template.mei
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://music-encoding.org/schema/dev/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="https://music-encoding.org/schema/dev/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<mei
xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.0.0-dev">
<meiHead>
<fileDesc>
<titleStmt>
<title>MEI Encoding Output (1.0.0)</title>
</titleStmt>
<pubStmt/>
</fileDesc>
</meiHead>
<music>
<facsimile>
<surface lrx="4872" lry="6496">
<zone ulx="601" uly="506" lrx="1666" lry="732"/>
</surface>
</facsimile>
<body>
<mdiv>
<score>
<scoreDef>
<staffGrp>
<staffDef n="1" notationtype="neume" lines="4" clef.shape="C" clef.line="3"/>
</staffGrp>
</scoreDef>
<section>
<staff n="1">
<layer n="1"/>
</staff>
</section>
</score>
</mdiv>
</body>
</music>
</mei>
11 changes: 10 additions & 1 deletion src/Dashboard/DashboardContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@ export const uploadAreaHTML =
<div>Name</div>
<div class="sort_name arrow_btn">&#x22C0;</div>
</div>
<div class="file_list" id="mei_list"></div>
<div class="file_list" id="mei_list">
<div class="unpaired_item_container" style="width: 100%; background: #DAEAF1; border-radius: 5px;">
<label class="unpaired_item_label">
<input type="radio" class="unpaired_item_radio" name="mei_radio_group" value="create_mei">
<span style="margin-top: auto;margin-left: 3px;">
Create new MEI file
</span>
</label>
</div>
</div>
</div>
<div id="image_container">
Expand Down
2 changes: 1 addition & 1 deletion src/Dashboard/FileSystem/FileSystemManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IFolder, FileSystemTools } from '.';
import { fetchUploads, db } from '../Storage';
import { fetchUploads } from '../Storage';
import { samples } from '../samples_filenames';

interface FileSystemProps {
Expand Down
2 changes: 1 addition & 1 deletion src/Dashboard/UploadArea.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ModalWindow, ModalWindowView } from '../utils/ModalWindow';
import { addNewFiles, handleUploadAllDocuments, handleMakePair, sortFileByName } from './UploadTools';
import { addNewFiles, handleUploadAllDocuments, handleMakePair, sortFileByName } from './UploadTools';
import { updateDashboard } from './Dashboard';
import { IFolder } from './FileSystem';

Expand Down
83 changes: 81 additions & 2 deletions src/Dashboard/UploadFileManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { uuidv4 } from '../utils/random';
import * as vkbeautify from 'vkbeautify';

/**
* Manager for file uploading and pairing process
*/
Expand All @@ -7,6 +10,7 @@ class UploadFileManager {
private allFiles = new Map<string, {file: File, count: number}>();
private folios = new Array<folio>(); // filename, mei_filename, image_filename
// private manuscripts = new Array<string>();
private meiTemplate: string;

// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}
Expand All @@ -30,6 +34,74 @@ class UploadFileManager {
}
}

public async setMeiTemplate(templatePath: string): Promise<void> {
try {
const response = await fetch(templatePath);
if (!response.ok) {
throw new Error(`Failed to load MEI template from ${templatePath}`);
}

this.meiTemplate = await response.text();
} catch (error) {
console.error(error);
}
}

public createMeiFile(filename: string): File {
try {
if (!this.meiTemplate) {
throw new Error('Cannot find MEI template');
}

const parser = new DOMParser();
const serializer = new XMLSerializer();
const meiDoc = parser.parseFromString(this.meiTemplate, 'text/xml');
const mei = meiDoc.documentElement;

const meiHead = mei.querySelector('meiHead');
meiHead.setAttribute('xml:id', 'm-' + uuidv4());
const fileDesc = mei.querySelector('fileDesc');
fileDesc.setAttribute('xml:id', 'm-' + uuidv4());
const titleStmt = mei.querySelector('titleStmt');
titleStmt.setAttribute('xml:id', 'm-' + uuidv4());
const title = mei.querySelector('title');
title.setAttribute('xml:id', 'm-' + uuidv4());
const pubStmt = mei.querySelector('pubStmt');
pubStmt.setAttribute('xml:id', 'm-' + uuidv4());
const facsimile = mei.querySelector('facsimile');
facsimile.setAttribute('xml:id', 'm-' + uuidv4());
const surface = mei.querySelector('surface');
surface.setAttribute('xml:id', 'm-' + uuidv4());

const mdiv = mei.querySelector('mdiv');
mdiv.setAttribute('xml:id', 'm-' + uuidv4());
const score = mei.querySelector('score');
score.setAttribute('xml:id', 'm-' + uuidv4());
const scoreDef = mei.querySelector('scoreDef');
scoreDef.setAttribute('xml:id', 'm-' + uuidv4());
const staffGrp = mei.querySelector('staffGrp');
staffGrp.setAttribute('xml:id', 'm-' + uuidv4());
const staffDef = mei.querySelector('staffDef');
staffDef.setAttribute('xml:id', 'm-' + uuidv4());
const section = mei.querySelector('section');
section.setAttribute('xml:id', 'm-' + uuidv4());
const staff = mei.querySelector('staff');
staff.setAttribute('xml:id', 'm-' + uuidv4());
const layer = mei.querySelector('layer');
layer.setAttribute('xml:id', 'm-' + uuidv4());
const zone = mei.querySelector('zone');
zone.setAttribute('xml:id', 'm-' + uuidv4());
staff.setAttribute('facs', '#'+zone.getAttribute('xml:id'));

const meiFileContent = vkbeautify.xml(serializer.serializeToString(meiDoc));
const meiBlob = new Blob([meiFileContent], { type: 'text/xml' });

return new File([meiBlob], filename, { type: 'text/xml' });
} catch (error) {
console.error(error);
}
}

public getFile(key: string): File {
if (this.allFiles.has(key)) {
return this.allFiles.get(key).file;
Expand All @@ -55,11 +127,12 @@ class UploadFileManager {
else return 0;
}

public addFolio(name: string, mei: string, image: string): void {
public addFolio(name: string, mei: string, image: string, isCreated: boolean): void {
const newFolio: folio = {
filename: name,
mei_filename: mei,
image_filename: image
image_filename: image,
isCreated: isCreated,
};
this.folios.push(newFolio);
}
Expand All @@ -68,6 +141,11 @@ class UploadFileManager {
// this.manuscripts.push(filename);
// }

public isCreatedFolio(filename: string): boolean {
const folio = this.folios.find((folio) => folio.filename === filename);
return folio ? folio.isCreated : false;
}

public removeFolio(filename: string): void {
const idx = this.folios.findIndex( folio => folio.filename === filename);
this.folios.splice(idx, 1);
Expand Down Expand Up @@ -117,4 +195,5 @@ type folio = {
filename: string,
mei_filename: string,
image_filename: string,
isCreated: boolean,
};
35 changes: 29 additions & 6 deletions src/Dashboard/UploadTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { IFolder, FileSystemTools } from './FileSystem';

const fm = UploadFileManager.getInstance();

// Set MEI template
const templatePath = 'samples/mei/mei_template.mei';
fm.setMeiTemplate(templatePath);

// Adds new files to upload pairing container and filemanager, returns list of rejected files
export function addNewFiles( files: File[] ): File[] {
const mei_container: HTMLDivElement = document.querySelector('#mei_list');
Expand Down Expand Up @@ -59,7 +63,7 @@ function createUnpairedItem(filename: string, group: string): HTMLDivElement {

const text = document.createElement('span');
text.innerText = formatFilename(filename, 50);
text.setAttribute('style', 'margin-top: auto');
text.setAttribute('style', 'margin-top: auto; margin-left: 3px;');

const delBtn = document.createElement('img');
delBtn.className = 'unpaired_del_btn';
Expand All @@ -86,16 +90,32 @@ export function handleMakePair(): void {
const selectedImageElement: HTMLInputElement = document.querySelector('input[name="image_radio_group"]:checked');
if (selectedMeiElement === null || selectedImageElement === null) return;

const mei_filename = selectedMeiElement.value;
let mei_filename = selectedMeiElement.value;
const image_filename = selectedImageElement.value;
let isCreated = false;

if (selectedMeiElement.value === 'create_mei'){
isCreated = true;
// Change extension
const fn = image_filename.split('.');
if (fn.length > 1) {
fn.pop();
}
fn.push('mei');

mei_filename = fn.join('.');
// Create new MEI file
const newMeiFile = fm.createMeiFile(mei_filename);
fm.addFile(newMeiFile);
}
const filename = mei_filename.substring(0, mei_filename.length-4);
// make and append UI element
const paired_el = createPairedFolio(filename, mei_filename, image_filename);
paired_container.appendChild(paired_el);
// reflect in file manager
fm.addFolio(filename, mei_filename, image_filename);
fm.addFolio(filename, mei_filename, image_filename, isCreated);
// remove from unpaired mei and image lists
selectedMeiElement.parentElement.parentElement.remove();
if (!isCreated) selectedMeiElement.parentElement.parentElement.remove();
selectedImageElement.parentElement.parentElement.remove();
}

Expand All @@ -117,10 +137,13 @@ function createPairedFolio(filename: string, mei_filename: string, image_filenam
// remove folio from UI
folio.remove();
// remove folio from file manager
const isCreated = fm.isCreatedFolio(filename);
fm.removeFolio(filename);
// add items back to unpaired containers
const meiItem = createUnpairedItem(mei_filename, 'mei');
mei_container.appendChild(meiItem);
if (!isCreated) {
const meiItem = createUnpairedItem(mei_filename, 'mei');
mei_container.appendChild(meiItem);
}
const imageItem = createUnpairedItem(image_filename, 'image');
image_container.appendChild(imageItem);
}
Expand Down
29 changes: 16 additions & 13 deletions src/utils/ModalWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,14 @@ export class ModalWindow implements ModalWindowInterface {
* Set content of modal window
*/
private setModalWindowContent(content?: string): void {
const container = document.getElementById('neon-modal-window-content-container');
const title = document.getElementById('neon-modal-window-header-title');

switch (this.modalWindowView) {
case ModalWindowView.EDIT_TEXT:
document.getElementById('neon-modal-window-content-container').innerHTML = editTextModal;
container.innerHTML = editTextModal;
// set modal window title
document.getElementById('neon-modal-window-header-title').innerText = 'EDIT SYLLABLE TEXT';
title.innerText = 'EDIT SYLLABLE TEXT';

// span and current text of selected-to-edit syllable and filter out unwanted chars
const span = <HTMLSpanElement> document.getElementById('syl_text').querySelectorAll('span.selected-to-edit')[0];
Expand All @@ -177,38 +180,38 @@ export class ModalWindow implements ModalWindowInterface {
break;

case ModalWindowView.HOTKEYS:
document.getElementById('neon-modal-window-content-container').innerHTML = hotkeysModal;
document.getElementById('neon-modal-window-header-title').innerText = 'HOTKEYS';
container.innerHTML = hotkeysModal;
title.innerText = 'HOTKEYS';
break;

case ModalWindowView.VALIDATION_STATUS:
document.getElementById('neon-modal-window-content-container').innerHTML =
container.innerHTML =
`<div style="margin-bottom: 30px;white-space: pre-line;">${content}</div>
<div class="neon-modal-window-btn">
<a href="data:text/plain;charset=utf-8,${encodeURIComponent(content)}" download="validation.log">
Export
</a>
</div>`;
document.getElementById('neon-modal-window-header-title').innerText = 'ERROR LOG';
title.innerText = 'ERROR LOG';
break;

case ModalWindowView.DOCUMENT_UPLOAD:
document.getElementById('neon-modal-window-header-title').innerText = 'DOCUMENT UPLOAD';
document.getElementById('neon-modal-window-content-container').innerHTML = uploadAreaHTML;
title.innerText = 'DOCUMENT UPLOAD';
container.innerHTML = uploadAreaHTML;
break;

case ModalWindowView.MOVE_TO:
document.getElementById('neon-modal-window-header-title').innerText = 'MOVE TO';
title.innerText = 'MOVE TO';
break;

case ModalWindowView.NEW_FOLDER:
document.getElementById('neon-modal-window-header-title').innerText = 'NEW FOLDER';
document.getElementById('neon-modal-window-content-container').innerHTML = newFolderHTML;
title.innerText = 'NEW FOLDER';
container.innerHTML = newFolderHTML;
break;

case ModalWindowView.RENAME:
document.getElementById('neon-modal-window-header-title').innerText = 'RENAME';
document.getElementById('neon-modal-window-content-container').innerHTML = renameHTML;
title.innerText = 'RENAME';
container.innerHTML = renameHTML;
break;

default:
Expand Down
52 changes: 26 additions & 26 deletions verovio-util/verovio-dev/index.js

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions verovio-util/verovio.js

Large diffs are not rendered by default.

0 comments on commit 5bdffbe

Please sign in to comment.