mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-25 10:54:18 +00:00
Seperate Lists and Displays in seperated files
This commit is contained in:
parent
d90b0c409c
commit
581fd5bcb5
@ -1,500 +0,0 @@
|
|||||||
class RessourceList {
|
|
||||||
/* A wrapper class for the list.js list.
|
|
||||||
* This class is not meant to be used directly, instead it should be used as
|
|
||||||
* a base class for concrete ressource list implementations.
|
|
||||||
*/
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
if (listElement.dataset.userId) {
|
|
||||||
if (listElement.dataset.userId in nopaque.appClient.users) {
|
|
||||||
this.user = nopaque.appClient.users[listElement.dataset.userId];
|
|
||||||
} else {
|
|
||||||
console.error(`User not found: ${listElement.dataset.userId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.user = nopaque.appClient.users.self;
|
|
||||||
}
|
|
||||||
this.list = new List(listElement, {...RessourceList.options, ...options});
|
|
||||||
this.list.list.innerHTML = `<tr class="show-if-only-child">
|
|
||||||
<td colspan="3">
|
|
||||||
<span class="card-title"><i class="left material-icons" style="font-size: inherit;">file_download</i>Nothing here...</span>
|
|
||||||
<p>No ressource available (yet).</p>
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
eventHandler(eventType, payload) {
|
|
||||||
switch (eventType) {
|
|
||||||
case 'init':
|
|
||||||
this.init(payload);
|
|
||||||
break;
|
|
||||||
case 'patch':
|
|
||||||
this.patch(payload);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown event type: ${eventType}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(ressources) {
|
|
||||||
this.list.clear();
|
|
||||||
this.add(Object.values(ressources));
|
|
||||||
this.list.sort('id', {order: 'desc'});
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
/*
|
|
||||||
* It's not possible to generalize a patch Handler for all type of
|
|
||||||
* ressources. So this method is meant to be an interface.
|
|
||||||
*/
|
|
||||||
console.error('patch method not implemented!');
|
|
||||||
}
|
|
||||||
|
|
||||||
add(values) {
|
|
||||||
let ressources = Array.isArray(values) ? values : [values];
|
|
||||||
if (typeof this.preprocessRessource === 'function') {
|
|
||||||
ressources = ressources.map(ressource => this.preprocessRessource(ressource));
|
|
||||||
}
|
|
||||||
// Set a callback function ('() => {return;}') to force List.js perform the
|
|
||||||
// add method asynchronous: https://listjs.com/api/#add
|
|
||||||
this.list.add(ressources, () => {return;});
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(id) {
|
|
||||||
this.list.remove('id', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
replace(id, valueName, newValue) {
|
|
||||||
this.list.get('id', id)[0].values({[valueName]: newValue});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RessourceList.options = {page: 5, pagination: [{innerWindow: 4, outerWindow: 1}]};
|
|
||||||
|
|
||||||
|
|
||||||
class CorpusList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...CorpusList.options, ...options});
|
|
||||||
this.corpora = undefined;
|
|
||||||
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
|
||||||
listElement.addEventListener('click', event => this.onclick(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
init(corpora) {
|
|
||||||
this.corpora = corpora;
|
|
||||||
super.init(corpora);
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(event) {
|
|
||||||
let ressourceElement = event.target.closest('tr');
|
|
||||||
if (ressourceElement === null) {return;}
|
|
||||||
let corpusId = ressourceElement.dataset.id;
|
|
||||||
let actionButtonElement = event.target.closest('.action-button');
|
|
||||||
let action = (actionButtonElement === null) ? 'view' : actionButtonElement.dataset.action;
|
|
||||||
switch (action) {
|
|
||||||
case 'analyse':
|
|
||||||
window.location.href = this.corpora[corpusId].analysis_url;
|
|
||||||
case 'delete':
|
|
||||||
let deleteModalHTML = `<div class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Confirm corpus deletion</h4>
|
|
||||||
<p>Do you really want to delete the corpus <b>${this.corpora[corpusId].title}</b>? All files will be permanently deleted!</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
|
||||||
<a class="btn modal-close red waves-effect waves-light" href="${this.corpora[corpusId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
let deleteModalParentElement = document.querySelector('main');
|
|
||||||
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
|
||||||
let deleteModalElement = deleteModalParentElement.lastChild;
|
|
||||||
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
|
||||||
deleteModal.open();
|
|
||||||
break;
|
|
||||||
case 'view':
|
|
||||||
// TODO: handle unprepared corpora
|
|
||||||
window.location.href = this.corpora[corpusId].url;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown action: ${action}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let id, match, re, valueName;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'add':
|
|
||||||
// Matches the only paths that should be handled here: /corpora/{corpusId}
|
|
||||||
re = /^\/corpora\/(\d+)$/;
|
|
||||||
if (re.test(operation.path)) {this.add(operation.value);}
|
|
||||||
break;
|
|
||||||
case 'remove':
|
|
||||||
// See case 'add' ;)
|
|
||||||
re = /^\/corpora\/(\d+)$/;
|
|
||||||
if (re.test(operation.path)) {
|
|
||||||
[match, id] = operation.path.match(re);
|
|
||||||
this.remove(id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'replace':
|
|
||||||
// Matches the only paths that should be handled here: /corpora/{corpusId}/{status || description || title}
|
|
||||||
re = /^\/corpora\/(\d+)\/(status|description|title)$/;
|
|
||||||
if (re.test(operation.path)) {
|
|
||||||
[match, id, valueName] = operation.path.match(re);
|
|
||||||
this.replace(id, valueName, operation.value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessRessource(corpus) {
|
|
||||||
return {id: corpus.id,
|
|
||||||
status: corpus.status,
|
|
||||||
description: corpus.description,
|
|
||||||
title: corpus.title};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CorpusList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><a class="btn-floating disabled"><i class="material-icons">book</i></a></td>
|
|
||||||
<td><b class="title"></b><br><i class="description"></i></td>
|
|
||||||
<td><span class="badge new status" data-badge-caption=""></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="analyse" data-position="top" data-tooltip="Analyse"><i class="material-icons">search</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class CorpusFileList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...CorpusFileList.options, ...options});
|
|
||||||
this.corpus = undefined;
|
|
||||||
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.corpusId);
|
|
||||||
listElement.addEventListener('click', event => this.onclick(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
init(corpus) {
|
|
||||||
this.corpus = corpus;
|
|
||||||
super.init(this.corpus.files);
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(event) {
|
|
||||||
let ressourceElement = event.target.closest('tr');
|
|
||||||
if (ressourceElement === null) {return;}
|
|
||||||
let corpusFileId = ressourceElement.dataset.id;
|
|
||||||
let actionButtonElement = event.target.closest('.action-button');
|
|
||||||
if (actionButtonElement === null) {return;}
|
|
||||||
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
|
|
||||||
switch (action) {
|
|
||||||
case 'delete':
|
|
||||||
let deleteModalHTML = `<div class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Confirm corpus deletion</h4>
|
|
||||||
<p>Do you really want to delete the corpus file <b>${this.corpus.files[corpusFileId].filename}</b>? It will be permanently deleted!</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
|
||||||
<a class="btn modal-close red waves-effect waves-light" href="${this.corpus.files[corpusFileId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
let deleteModalParentElement = document.querySelector('main');
|
|
||||||
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
|
||||||
let deleteModalElement = deleteModalParentElement.lastChild;
|
|
||||||
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
|
||||||
deleteModal.open();
|
|
||||||
break;
|
|
||||||
case 'download':
|
|
||||||
window.location.href = this.corpus.files[corpusFileId].download_url;
|
|
||||||
break;
|
|
||||||
case 'view':
|
|
||||||
window.location.href = this.corpus.files[corpusFileId].url;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown action: "${action}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let id, match, re;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'add':
|
|
||||||
// Matches the only paths that should be handled here: /corpora/{this.corpus.id}/files/{corpusFileId}
|
|
||||||
re = new RegExp('^/corpora/' + this.corpus.id + '/files/(\\d+)$');
|
|
||||||
if (re.test(operation.path)) {this.add(operation.value);}
|
|
||||||
break;
|
|
||||||
case 'remove':
|
|
||||||
// See case add ;)
|
|
||||||
re = new RegExp('^/corpora/' + this.corpus.id + '/files/(\\d+)$');
|
|
||||||
if (re.test(operation.path)) {
|
|
||||||
[match, id] = operation.path.match(re);
|
|
||||||
this.remove(id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessRessource(corpusFile) {
|
|
||||||
return {id: corpusFile.id, author: corpusFile.author, filename: corpusFile.filename, 'publishing-year': corpusFile.publishing_year, title: corpusFile.title};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CorpusFileList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><span class="filename"></span></td>
|
|
||||||
<td><span class="author"></span></td>
|
|
||||||
<td><span class="title"></span></td>
|
|
||||||
<td><span class="publishing-year"></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, 'author', 'filename', 'publishing-year', 'title']
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class JobList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...JobList.options, ...options});
|
|
||||||
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
|
||||||
listElement.addEventListener('click', event => this.onclick(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(event) {
|
|
||||||
let ressourceElement = event.target.closest('tr');
|
|
||||||
if (ressourceElement === null) {return;}
|
|
||||||
let jobId = ressourceElement.dataset.id;
|
|
||||||
let actionButtonElement = event.target.closest('.action-button');
|
|
||||||
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
|
|
||||||
switch (action) {
|
|
||||||
case 'delete':
|
|
||||||
let deleteModalHTML = `<div class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Confirm job deletion</h4>
|
|
||||||
<p>Do you really want to delete the job <b>${this.user.data.jobs[jobId].title}</b>? All files will be permanently deleted!</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
|
||||||
<a class="btn modal-close red waves-effect waves-light" href="${this.user.data.jobs[jobId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
let deleteModalParentElement = document.querySelector('main');
|
|
||||||
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
|
||||||
let deleteModalElement = deleteModalParentElement.lastChild;
|
|
||||||
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
|
||||||
deleteModal.open();
|
|
||||||
break;
|
|
||||||
case 'view':
|
|
||||||
window.location.href = this.user.data.jobs[jobId].url;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown action: "${action}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let id, match, re, valueName;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'add':
|
|
||||||
// Matches the only paths that should be handled here: /jobs/{jobId}
|
|
||||||
re = /^\/jobs\/(\d+)$/;
|
|
||||||
if (re.test(operation.path)) {this.add(operation.value);}
|
|
||||||
break;
|
|
||||||
case 'remove':
|
|
||||||
// See case add ;)
|
|
||||||
re = /^\/jobs\/(\d+)$/;
|
|
||||||
if (re.test(operation.path)) {
|
|
||||||
[match, id] = operation.path.match(re);
|
|
||||||
this.remove(id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'replace':
|
|
||||||
// Matches the only paths that should be handled here: /jobs/{jobId}/{service || status || description || title}
|
|
||||||
re = /^\/jobs\/(\d+)\/(status|description|title)$/;
|
|
||||||
if (re.test(operation.path)) {
|
|
||||||
[match, id, valueName] = operation.path.match(re);
|
|
||||||
this.replace(id, valueName, operation.value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessRessource(job) {
|
|
||||||
return {id: job.id,
|
|
||||||
service: job.service,
|
|
||||||
status: job.status,
|
|
||||||
description: job.description,
|
|
||||||
title: job.title};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><a class="btn-floating disabled"><i class="material-icons service"></i></a></td>
|
|
||||||
<td><b class="title"></b><br><i class="description"></i></td>
|
|
||||||
<td><span class="badge new status" data-badge-caption=""></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, {name: 'service', attr: 'data-service'}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class JobInputList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...JobInputList.options, ...options});
|
|
||||||
this.job = undefined;
|
|
||||||
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.jobId);
|
|
||||||
listElement.addEventListener('click', event => this.onclick(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
init(job) {
|
|
||||||
this.job = job;
|
|
||||||
super.init(this.job.inputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(event) {
|
|
||||||
let ressourceElement = event.target.closest('tr');
|
|
||||||
if (ressourceElement === null) {return;}
|
|
||||||
let jobInputId = ressourceElement.dataset.id;
|
|
||||||
let actionButtonElement = event.target.closest('.action-button');
|
|
||||||
if (actionButtonElement === null) {return;}
|
|
||||||
let action = actionButtonElement.dataset.action;
|
|
||||||
switch (action) {
|
|
||||||
case 'download':
|
|
||||||
window.location.href = this.job.inputs[jobInputId].download_url;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown action: "${action}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessRessource(jobInput) {
|
|
||||||
return {id: jobInput.id, filename: jobInput.filename};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobInputList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><span class="filename"></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, 'filename']
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class JobResultList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...JobResultList.options, ...options});
|
|
||||||
this.job = undefined;
|
|
||||||
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.jobId);
|
|
||||||
listElement.addEventListener('click', event => this.onclick(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
init(job) {
|
|
||||||
this.job = job;
|
|
||||||
super.init(this.job.results);
|
|
||||||
}
|
|
||||||
|
|
||||||
onclick(event) {
|
|
||||||
let ressourceElement = event.target.closest('tr');
|
|
||||||
if (ressourceElement === null) {return;}
|
|
||||||
let jobResultId = ressourceElement.dataset.id;
|
|
||||||
let actionButtonElement = event.target.closest('.action-button');
|
|
||||||
if (actionButtonElement === null) {return;}
|
|
||||||
let action = actionButtonElement.dataset.action;
|
|
||||||
switch (action) {
|
|
||||||
case 'download':
|
|
||||||
window.location.href = this.job.results[jobResultId].download_url;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Unknown action: "${action}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let re;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'add':
|
|
||||||
// Matches the only paths that should be handled here: /jobs/{this.job.id}/results/{jobResultId}
|
|
||||||
re = new RegExp('^/jobs/' + this.job.id + '/results/(\\d+)$');
|
|
||||||
if (re.test(operation.path)) {this.add(operation.value);}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessRessource(jobResult) {
|
|
||||||
let description;
|
|
||||||
if (jobResult.filename.endsWith('.pdf.zip')) {
|
|
||||||
description = 'PDF files with text layer';
|
|
||||||
} else if (jobResult.filename.endsWith('.txt.zip')) {
|
|
||||||
description = 'Raw text files';
|
|
||||||
} else if (jobResult.filename.endsWith('.vrt.zip')) {
|
|
||||||
description = 'VRT compliant files including the NLP data';
|
|
||||||
} else if (jobResult.filename.endsWith('.xml.zip')) {
|
|
||||||
description = 'TEI compliant files';
|
|
||||||
} else if (jobResult.filename.endsWith('.poco.zip')) {
|
|
||||||
description = 'HOCR and image files for post correction (PoCo)';
|
|
||||||
} else {
|
|
||||||
description = 'All result files created during this job';
|
|
||||||
}
|
|
||||||
return {id: jobResult.id, description: description, filename: jobResult.filename};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JobResultList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><span class="description"></span></td>
|
|
||||||
<td><span class="filename"></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, 'description', 'filename']
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class QueryResultList extends RessourceList {
|
|
||||||
constructor(listElement, options = {}) {
|
|
||||||
super(listElement, {...QueryResultList.options, ...options});
|
|
||||||
this.user.eventListeners.queryResult.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QueryResultList.options = {
|
|
||||||
item: `<tr>
|
|
||||||
<td><b class="title"></b><br><i class="description"></i><br></td>
|
|
||||||
<td><span class="corpus_title"></span><br><span class="query"></span></td>
|
|
||||||
<td class="right-align">
|
|
||||||
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
|
||||||
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="analyse" data-position="top" data-tooltip="Analyse"><i class="material-icons">search</i></a>
|
|
||||||
</td>
|
|
||||||
</tr>`,
|
|
||||||
valueNames: [{data: ['id']}, 'corpus_title', 'description', 'query', 'title']
|
|
||||||
};
|
|
@ -1,215 +0,0 @@
|
|||||||
class RessourceDisplay {
|
|
||||||
constructor(displayElement) {
|
|
||||||
if (displayElement.dataset.userId) {
|
|
||||||
if (displayElement.dataset.userId in nopaque.appClient.users) {
|
|
||||||
this.user = nopaque.appClient.users[displayElement.dataset.userId];
|
|
||||||
} else {
|
|
||||||
console.error(`User not found: ${displayElement.dataset.userId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.user = nopaque.appClient.users.self;
|
|
||||||
}
|
|
||||||
this.displayElement = displayElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
eventHandler(eventType, payload) {
|
|
||||||
switch (eventType) {
|
|
||||||
case 'init':
|
|
||||||
this.init(payload);
|
|
||||||
break;
|
|
||||||
case 'patch':
|
|
||||||
this.patch(payload);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(`Unknown event type: ${eventType}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {console.error('init method not implemented!');}
|
|
||||||
|
|
||||||
patch() {console.error('patch method not implemented!');}
|
|
||||||
|
|
||||||
setElement(element, value) {
|
|
||||||
switch (element.tagName) {
|
|
||||||
case 'INPUT':
|
|
||||||
element.value = value;
|
|
||||||
M.updateTextFields();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
element.innerText = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CorpusDisplay extends RessourceDisplay {
|
|
||||||
constructor(displayElement) {
|
|
||||||
super(displayElement);
|
|
||||||
this.corpus = undefined;
|
|
||||||
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), displayElement.dataset.corpusId);
|
|
||||||
}
|
|
||||||
|
|
||||||
init(corpus) {
|
|
||||||
this.corpus = corpus;
|
|
||||||
this.setCreationDate(this.corpus.creation_date);
|
|
||||||
this.setDescription(this.corpus.description);
|
|
||||||
this.setLastEditedDate(this.corpus.last_edited_date);
|
|
||||||
this.setStatus(this.corpus.status);
|
|
||||||
this.setTitle(this.corpus.title);
|
|
||||||
this.setTokenRatio(this.corpus.current_nr_of_tokens, this.corpus.max_nr_of_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let re;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'replace':
|
|
||||||
// Matches: /jobs/{this.job.id}/status
|
|
||||||
re = new RegExp('^/corpora/' + this.corpus.id + '/last_edited_date');
|
|
||||||
if (re.test(operation.path)) {this.setLastEditedDate(operation.value); break;}
|
|
||||||
// Matches: /jobs/{this.job.id}/status
|
|
||||||
re = new RegExp('^/corpora/' + this.corpus.id + '/status$');
|
|
||||||
if (re.test(operation.path)) {this.setStatus(operation.value); break;}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle(title) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-title')) {this.setElement(element, title);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTokenRatio(currentNrOfTokens, maxNrOfTokens) {
|
|
||||||
let tokenRatio = `${currentNrOfTokens}/${maxNrOfTokens}`;
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-token-ratio')) {this.setElement(element, tokenRatio);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setDescription(description) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-description')) {this.setElement(element, description);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatus(status) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-status')) {this.setElement(element, status);}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.status-spinner')) {
|
|
||||||
if (['complete', 'failed', 'unprepared'].includes(status)) {
|
|
||||||
element.classList.add('hide');
|
|
||||||
} else {
|
|
||||||
element.classList.remove('hide');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.build-corpus-trigger')) {
|
|
||||||
if (['complete', 'failed'].includes(status)) {
|
|
||||||
element.classList.remove('hide');
|
|
||||||
} else {
|
|
||||||
element.classList.add('hide');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCreationDate(creationDateTimestamp) {
|
|
||||||
let creationDate = new Date(creationDateTimestamp * 1000).toLocaleString("en-US");
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-creation-date')) {this.setElement(element, creationDate);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLastEditedDate(endDateTimestamp) {
|
|
||||||
let endDate = new Date(endDateTimestamp * 1000).toLocaleString("en-US");
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.corpus-end-date')) {this.setElement(element, endDate);}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class JobDisplay extends RessourceDisplay {
|
|
||||||
constructor(displayElement) {
|
|
||||||
super(displayElement);
|
|
||||||
this.job = undefined;
|
|
||||||
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), displayElement.dataset.jobId);
|
|
||||||
}
|
|
||||||
|
|
||||||
init(job) {
|
|
||||||
this.job = job;
|
|
||||||
this.setCreationDate(this.job.creation_date);
|
|
||||||
this.setEndDate(this.job.creation_date);
|
|
||||||
this.setDescription(this.job.description);
|
|
||||||
this.setService(this.job.service);
|
|
||||||
this.setServiceArgs(this.job.service_args);
|
|
||||||
this.setServiceVersion(this.job.service_version);
|
|
||||||
this.setStatus(this.job.status);
|
|
||||||
this.setTitle(this.job.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
patch(patch) {
|
|
||||||
let re;
|
|
||||||
for (let operation of patch) {
|
|
||||||
switch(operation.op) {
|
|
||||||
case 'replace':
|
|
||||||
// Matches: /jobs/{this.job.id}/status
|
|
||||||
re = new RegExp('^/jobs/' + this.job.id + '/end_date');
|
|
||||||
if (re.test(operation.path)) {this.setEndDate(operation.value); break;}
|
|
||||||
// Matches: /jobs/{this.job.id}/status
|
|
||||||
re = new RegExp('^/jobs/' + this.job.id + '/status$');
|
|
||||||
if (re.test(operation.path)) {this.setStatus(operation.value); break;}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle(title) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-title')) {this.setElement(element, title);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setDescription(description) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-description')) {this.setElement(element, description);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatus(status) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-status')) {
|
|
||||||
this.setElement(element, status);
|
|
||||||
}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.status-spinner')) {
|
|
||||||
if (['complete', 'failed'].includes(status)) {
|
|
||||||
element.classList.add('hide');
|
|
||||||
} else {
|
|
||||||
element.classList.remove('hide');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.restart-job-trigger')) {
|
|
||||||
if (['complete', 'failed'].includes(status)) {
|
|
||||||
element.classList.remove('hide');
|
|
||||||
} else {
|
|
||||||
element.classList.add('hide');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCreationDate(creationDateTimestamp) {
|
|
||||||
let creationDate = new Date(creationDateTimestamp * 1000).toLocaleString("en-US");
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-creation-date')) {this.setElement(element, creationDate);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setEndDate(endDateTimestamp) {
|
|
||||||
let endDate = new Date(endDateTimestamp * 1000).toLocaleString("en-US");
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-end-date')) {this.setElement(element, endDate);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setService(service) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-service')) {this.setElement(element, service);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setServiceArgs(serviceArgs) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-service-args')) {this.setElement(element, serviceArgs);}
|
|
||||||
}
|
|
||||||
|
|
||||||
setServiceVersion(serviceVersion) {
|
|
||||||
for (let element of this.displayElement.querySelectorAll('.job-service-version')) {this.setElement(element, serviceVersion);}
|
|
||||||
}
|
|
||||||
}
|
|
77
web/app/static/js/nopaque/displays/CorpusDisplay.js
Normal file
77
web/app/static/js/nopaque/displays/CorpusDisplay.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
class CorpusDisplay extends RessourceDisplay {
|
||||||
|
constructor(displayElement) {
|
||||||
|
super(displayElement);
|
||||||
|
this.corpus = undefined;
|
||||||
|
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), displayElement.dataset.corpusId);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(corpus) {
|
||||||
|
this.corpus = corpus;
|
||||||
|
this.setCreationDate(this.corpus.creation_date);
|
||||||
|
this.setDescription(this.corpus.description);
|
||||||
|
this.setLastEditedDate(this.corpus.last_edited_date);
|
||||||
|
this.setStatus(this.corpus.status);
|
||||||
|
this.setTitle(this.corpus.title);
|
||||||
|
this.setTokenRatio(this.corpus.current_nr_of_tokens, this.corpus.max_nr_of_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let re;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'replace':
|
||||||
|
// Matches: /jobs/{this.job.id}/status
|
||||||
|
re = new RegExp('^/corpora/' + this.corpus.id + '/last_edited_date');
|
||||||
|
if (re.test(operation.path)) {this.setLastEditedDate(operation.value); break;}
|
||||||
|
// Matches: /jobs/{this.job.id}/status
|
||||||
|
re = new RegExp('^/corpora/' + this.corpus.id + '/status$');
|
||||||
|
if (re.test(operation.path)) {this.setStatus(operation.value); break;}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTitle(title) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-title')) {this.setElement(element, title);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTokenRatio(currentNrOfTokens, maxNrOfTokens) {
|
||||||
|
let tokenRatio = `${currentNrOfTokens}/${maxNrOfTokens}`;
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-token-ratio')) {this.setElement(element, tokenRatio);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDescription(description) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-description')) {this.setElement(element, description);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(status) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-status')) {this.setElement(element, status);}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.status-spinner')) {
|
||||||
|
if (['complete', 'failed', 'unprepared'].includes(status)) {
|
||||||
|
element.classList.add('hide');
|
||||||
|
} else {
|
||||||
|
element.classList.remove('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.build-corpus-trigger')) {
|
||||||
|
if (['complete', 'failed'].includes(status)) {
|
||||||
|
element.classList.remove('hide');
|
||||||
|
} else {
|
||||||
|
element.classList.add('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreationDate(creationDateTimestamp) {
|
||||||
|
let creationDate = new Date(creationDateTimestamp * 1000).toLocaleString("en-US");
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-creation-date')) {this.setElement(element, creationDate);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastEditedDate(endDateTimestamp) {
|
||||||
|
let endDate = new Date(endDateTimestamp * 1000).toLocaleString("en-US");
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.corpus-end-date')) {this.setElement(element, endDate);}
|
||||||
|
}
|
||||||
|
}
|
88
web/app/static/js/nopaque/displays/JobDisplay.js
Normal file
88
web/app/static/js/nopaque/displays/JobDisplay.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
class JobDisplay extends RessourceDisplay {
|
||||||
|
constructor(displayElement) {
|
||||||
|
super(displayElement);
|
||||||
|
this.job = undefined;
|
||||||
|
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), displayElement.dataset.jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(job) {
|
||||||
|
this.job = job;
|
||||||
|
this.setCreationDate(this.job.creation_date);
|
||||||
|
this.setEndDate(this.job.creation_date);
|
||||||
|
this.setDescription(this.job.description);
|
||||||
|
this.setService(this.job.service);
|
||||||
|
this.setServiceArgs(this.job.service_args);
|
||||||
|
this.setServiceVersion(this.job.service_version);
|
||||||
|
this.setStatus(this.job.status);
|
||||||
|
this.setTitle(this.job.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let re;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'replace':
|
||||||
|
// Matches: /jobs/{this.job.id}/status
|
||||||
|
re = new RegExp('^/jobs/' + this.job.id + '/end_date');
|
||||||
|
if (re.test(operation.path)) {this.setEndDate(operation.value); break;}
|
||||||
|
// Matches: /jobs/{this.job.id}/status
|
||||||
|
re = new RegExp('^/jobs/' + this.job.id + '/status$');
|
||||||
|
if (re.test(operation.path)) {this.setStatus(operation.value); break;}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTitle(title) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-title')) {this.setElement(element, title);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDescription(description) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-description')) {this.setElement(element, description);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(status) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-status')) {
|
||||||
|
this.setElement(element, status);
|
||||||
|
}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.status')) {element.dataset.status = status;}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.status-spinner')) {
|
||||||
|
if (['complete', 'failed'].includes(status)) {
|
||||||
|
element.classList.add('hide');
|
||||||
|
} else {
|
||||||
|
element.classList.remove('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.restart-job-trigger')) {
|
||||||
|
if (['complete', 'failed'].includes(status)) {
|
||||||
|
element.classList.remove('hide');
|
||||||
|
} else {
|
||||||
|
element.classList.add('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreationDate(creationDateTimestamp) {
|
||||||
|
let creationDate = new Date(creationDateTimestamp * 1000).toLocaleString("en-US");
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-creation-date')) {this.setElement(element, creationDate);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setEndDate(endDateTimestamp) {
|
||||||
|
let endDate = new Date(endDateTimestamp * 1000).toLocaleString("en-US");
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-end-date')) {this.setElement(element, endDate);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setService(service) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-service')) {this.setElement(element, service);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setServiceArgs(serviceArgs) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-service-args')) {this.setElement(element, serviceArgs);}
|
||||||
|
}
|
||||||
|
|
||||||
|
setServiceVersion(serviceVersion) {
|
||||||
|
for (let element of this.displayElement.querySelectorAll('.job-service-version')) {this.setElement(element, serviceVersion);}
|
||||||
|
}
|
||||||
|
}
|
45
web/app/static/js/nopaque/displays/RessourceDisplay.js
Normal file
45
web/app/static/js/nopaque/displays/RessourceDisplay.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
class RessourceDisplay {
|
||||||
|
constructor(displayElement) {
|
||||||
|
if (displayElement.dataset.userId) {
|
||||||
|
if (displayElement.dataset.userId in nopaque.appClient.users) {
|
||||||
|
this.user = nopaque.appClient.users[displayElement.dataset.userId];
|
||||||
|
} else {
|
||||||
|
console.error(`User not found: ${displayElement.dataset.userId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.user = nopaque.appClient.users.self;
|
||||||
|
}
|
||||||
|
this.displayElement = displayElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventHandler(eventType, payload) {
|
||||||
|
switch (eventType) {
|
||||||
|
case 'init':
|
||||||
|
this.init(payload);
|
||||||
|
break;
|
||||||
|
case 'patch':
|
||||||
|
this.patch(payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(`Unknown event type: ${eventType}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {console.error('init method not implemented!');}
|
||||||
|
|
||||||
|
patch() {console.error('patch method not implemented!');}
|
||||||
|
|
||||||
|
setElement(element, value) {
|
||||||
|
switch (element.tagName) {
|
||||||
|
case 'INPUT':
|
||||||
|
element.value = value;
|
||||||
|
M.updateTextFields();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
element.innerText = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
web/app/static/js/nopaque/lists/CorpusFileList.js
Normal file
91
web/app/static/js/nopaque/lists/CorpusFileList.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
class CorpusFileList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...CorpusFileList.options, ...options});
|
||||||
|
this.corpus = undefined;
|
||||||
|
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.corpusId);
|
||||||
|
listElement.addEventListener('click', event => this.onclick(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
init(corpus) {
|
||||||
|
this.corpus = corpus;
|
||||||
|
super.init(this.corpus.files);
|
||||||
|
}
|
||||||
|
|
||||||
|
onclick(event) {
|
||||||
|
let ressourceElement = event.target.closest('tr');
|
||||||
|
if (ressourceElement === null) {return;}
|
||||||
|
let corpusFileId = ressourceElement.dataset.id;
|
||||||
|
let actionButtonElement = event.target.closest('.action-button');
|
||||||
|
if (actionButtonElement === null) {return;}
|
||||||
|
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
let deleteModalHTML = `<div class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Confirm corpus deletion</h4>
|
||||||
|
<p>Do you really want to delete the corpus file <b>${this.corpus.files[corpusFileId].filename}</b>? It will be permanently deleted!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||||
|
<a class="btn modal-close red waves-effect waves-light" href="${this.corpus.files[corpusFileId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
let deleteModalParentElement = document.querySelector('main');
|
||||||
|
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
||||||
|
let deleteModalElement = deleteModalParentElement.lastChild;
|
||||||
|
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
||||||
|
deleteModal.open();
|
||||||
|
break;
|
||||||
|
case 'download':
|
||||||
|
window.location.href = this.corpus.files[corpusFileId].download_url;
|
||||||
|
break;
|
||||||
|
case 'view':
|
||||||
|
window.location.href = this.corpus.files[corpusFileId].url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown action: "${action}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let id, match, re;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'add':
|
||||||
|
// Matches the only paths that should be handled here: /corpora/{this.corpus.id}/files/{corpusFileId}
|
||||||
|
re = new RegExp('^/corpora/' + this.corpus.id + '/files/(\\d+)$');
|
||||||
|
if (re.test(operation.path)) {this.add(operation.value);}
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
// See case add ;)
|
||||||
|
re = new RegExp('^/corpora/' + this.corpus.id + '/files/(\\d+)$');
|
||||||
|
if (re.test(operation.path)) {
|
||||||
|
[match, id] = operation.path.match(re);
|
||||||
|
this.remove(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessRessource(corpusFile) {
|
||||||
|
return {id: corpusFile.id, author: corpusFile.author, filename: corpusFile.filename, 'publishing-year': corpusFile.publishing_year, title: corpusFile.title};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CorpusFileList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><span class="filename"></span></td>
|
||||||
|
<td><span class="author"></span></td>
|
||||||
|
<td><span class="title"></span></td>
|
||||||
|
<td><span class="publishing-year"></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, 'author', 'filename', 'publishing-year', 'title']
|
||||||
|
};
|
100
web/app/static/js/nopaque/lists/CorpusList.js
Normal file
100
web/app/static/js/nopaque/lists/CorpusList.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
class CorpusList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...CorpusList.options, ...options});
|
||||||
|
this.corpora = undefined;
|
||||||
|
this.user.eventListeners.corpus.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
||||||
|
listElement.addEventListener('click', event => this.onclick(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
init(corpora) {
|
||||||
|
this.corpora = corpora;
|
||||||
|
super.init(corpora);
|
||||||
|
}
|
||||||
|
|
||||||
|
onclick(event) {
|
||||||
|
let ressourceElement = event.target.closest('tr');
|
||||||
|
if (ressourceElement === null) {return;}
|
||||||
|
let corpusId = ressourceElement.dataset.id;
|
||||||
|
let actionButtonElement = event.target.closest('.action-button');
|
||||||
|
let action = (actionButtonElement === null) ? 'view' : actionButtonElement.dataset.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'analyse':
|
||||||
|
window.location.href = this.corpora[corpusId].analysis_url;
|
||||||
|
case 'delete':
|
||||||
|
let deleteModalHTML = `<div class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Confirm corpus deletion</h4>
|
||||||
|
<p>Do you really want to delete the corpus <b>${this.corpora[corpusId].title}</b>? All files will be permanently deleted!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||||
|
<a class="btn modal-close red waves-effect waves-light" href="${this.corpora[corpusId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
let deleteModalParentElement = document.querySelector('main');
|
||||||
|
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
||||||
|
let deleteModalElement = deleteModalParentElement.lastChild;
|
||||||
|
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
||||||
|
deleteModal.open();
|
||||||
|
break;
|
||||||
|
case 'view':
|
||||||
|
// TODO: handle unprepared corpora
|
||||||
|
window.location.href = this.corpora[corpusId].url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown action: ${action}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let id, match, re, valueName;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'add':
|
||||||
|
// Matches the only paths that should be handled here: /corpora/{corpusId}
|
||||||
|
re = /^\/corpora\/(\d+)$/;
|
||||||
|
if (re.test(operation.path)) {this.add(operation.value);}
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
// See case 'add' ;)
|
||||||
|
re = /^\/corpora\/(\d+)$/;
|
||||||
|
if (re.test(operation.path)) {
|
||||||
|
[match, id] = operation.path.match(re);
|
||||||
|
this.remove(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
// Matches the only paths that should be handled here: /corpora/{corpusId}/{status || description || title}
|
||||||
|
re = /^\/corpora\/(\d+)\/(status|description|title)$/;
|
||||||
|
if (re.test(operation.path)) {
|
||||||
|
[match, id, valueName] = operation.path.match(re);
|
||||||
|
this.replace(id, valueName, operation.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessRessource(corpus) {
|
||||||
|
return {id: corpus.id,
|
||||||
|
status: corpus.status,
|
||||||
|
description: corpus.description,
|
||||||
|
title: corpus.title};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CorpusList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><a class="btn-floating disabled"><i class="material-icons">book</i></a></td>
|
||||||
|
<td><b class="title"></b><br><i class="description"></i></td>
|
||||||
|
<td><span class="badge new status" data-badge-caption=""></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="analyse" data-position="top" data-tooltip="Analyse"><i class="material-icons">search</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
||||||
|
};
|
43
web/app/static/js/nopaque/lists/JobInputList.js
Normal file
43
web/app/static/js/nopaque/lists/JobInputList.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
class JobInputList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...JobInputList.options, ...options});
|
||||||
|
this.job = undefined;
|
||||||
|
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.jobId);
|
||||||
|
listElement.addEventListener('click', event => this.onclick(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
init(job) {
|
||||||
|
this.job = job;
|
||||||
|
super.init(this.job.inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
onclick(event) {
|
||||||
|
let ressourceElement = event.target.closest('tr');
|
||||||
|
if (ressourceElement === null) {return;}
|
||||||
|
let jobInputId = ressourceElement.dataset.id;
|
||||||
|
let actionButtonElement = event.target.closest('.action-button');
|
||||||
|
if (actionButtonElement === null) {return;}
|
||||||
|
let action = actionButtonElement.dataset.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'download':
|
||||||
|
window.location.href = this.job.inputs[jobInputId].download_url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown action: "${action}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessRessource(jobInput) {
|
||||||
|
return {id: jobInput.id, filename: jobInput.filename};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JobInputList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><span class="filename"></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, 'filename']
|
||||||
|
};
|
91
web/app/static/js/nopaque/lists/JobList.js
Normal file
91
web/app/static/js/nopaque/lists/JobList.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
class JobList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...JobList.options, ...options});
|
||||||
|
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
||||||
|
listElement.addEventListener('click', event => this.onclick(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
onclick(event) {
|
||||||
|
let ressourceElement = event.target.closest('tr');
|
||||||
|
if (ressourceElement === null) {return;}
|
||||||
|
let jobId = ressourceElement.dataset.id;
|
||||||
|
let actionButtonElement = event.target.closest('.action-button');
|
||||||
|
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
let deleteModalHTML = `<div class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Confirm job deletion</h4>
|
||||||
|
<p>Do you really want to delete the job <b>${this.user.data.jobs[jobId].title}</b>? All files will be permanently deleted!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||||
|
<a class="btn modal-close red waves-effect waves-light" href="${this.user.data.jobs[jobId].url}/delete"><i class="material-icons left">delete</i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
let deleteModalParentElement = document.querySelector('main');
|
||||||
|
deleteModalParentElement.insertAdjacentHTML('beforeend', deleteModalHTML);
|
||||||
|
let deleteModalElement = deleteModalParentElement.lastChild;
|
||||||
|
let deleteModal = M.Modal.init(deleteModalElement, {onCloseEnd: () => {deleteModal.destroy(); deleteModalElement.remove();}});
|
||||||
|
deleteModal.open();
|
||||||
|
break;
|
||||||
|
case 'view':
|
||||||
|
window.location.href = this.user.data.jobs[jobId].url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown action: "${action}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let id, match, re, valueName;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'add':
|
||||||
|
// Matches the only paths that should be handled here: /jobs/{jobId}
|
||||||
|
re = /^\/jobs\/(\d+)$/;
|
||||||
|
if (re.test(operation.path)) {this.add(operation.value);}
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
// See case add ;)
|
||||||
|
re = /^\/jobs\/(\d+)$/;
|
||||||
|
if (re.test(operation.path)) {
|
||||||
|
[match, id] = operation.path.match(re);
|
||||||
|
this.remove(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
// Matches the only paths that should be handled here: /jobs/{jobId}/{service || status || description || title}
|
||||||
|
re = /^\/jobs\/(\d+)\/(status|description|title)$/;
|
||||||
|
if (re.test(operation.path)) {
|
||||||
|
[match, id, valueName] = operation.path.match(re);
|
||||||
|
this.replace(id, valueName, operation.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessRessource(job) {
|
||||||
|
return {id: job.id,
|
||||||
|
service: job.service,
|
||||||
|
status: job.status,
|
||||||
|
description: job.description,
|
||||||
|
title: job.title};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JobList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><a class="btn-floating disabled"><i class="material-icons service"></i></a></td>
|
||||||
|
<td><b class="title"></b><br><i class="description"></i></td>
|
||||||
|
<td><span class="badge new status" data-badge-caption=""></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, {name: 'service', attr: 'data-service'}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
||||||
|
};
|
73
web/app/static/js/nopaque/lists/JobResultList.js
Normal file
73
web/app/static/js/nopaque/lists/JobResultList.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
class JobResultList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...JobResultList.options, ...options});
|
||||||
|
this.job = undefined;
|
||||||
|
this.user.eventListeners.job.addEventListener((eventType, payload) => this.eventHandler(eventType, payload), listElement.dataset.jobId);
|
||||||
|
listElement.addEventListener('click', event => this.onclick(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
init(job) {
|
||||||
|
this.job = job;
|
||||||
|
super.init(this.job.results);
|
||||||
|
}
|
||||||
|
|
||||||
|
onclick(event) {
|
||||||
|
let ressourceElement = event.target.closest('tr');
|
||||||
|
if (ressourceElement === null) {return;}
|
||||||
|
let jobResultId = ressourceElement.dataset.id;
|
||||||
|
let actionButtonElement = event.target.closest('.action-button');
|
||||||
|
if (actionButtonElement === null) {return;}
|
||||||
|
let action = actionButtonElement.dataset.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'download':
|
||||||
|
window.location.href = this.job.results[jobResultId].download_url;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown action: "${action}"`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
let re;
|
||||||
|
for (let operation of patch) {
|
||||||
|
switch(operation.op) {
|
||||||
|
case 'add':
|
||||||
|
// Matches the only paths that should be handled here: /jobs/{this.job.id}/results/{jobResultId}
|
||||||
|
re = new RegExp('^/jobs/' + this.job.id + '/results/(\\d+)$');
|
||||||
|
if (re.test(operation.path)) {this.add(operation.value);}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessRessource(jobResult) {
|
||||||
|
let description;
|
||||||
|
if (jobResult.filename.endsWith('.pdf.zip')) {
|
||||||
|
description = 'PDF files with text layer';
|
||||||
|
} else if (jobResult.filename.endsWith('.txt.zip')) {
|
||||||
|
description = 'Raw text files';
|
||||||
|
} else if (jobResult.filename.endsWith('.vrt.zip')) {
|
||||||
|
description = 'VRT compliant files including the NLP data';
|
||||||
|
} else if (jobResult.filename.endsWith('.xml.zip')) {
|
||||||
|
description = 'TEI compliant files';
|
||||||
|
} else if (jobResult.filename.endsWith('.poco.zip')) {
|
||||||
|
description = 'HOCR and image files for post correction (PoCo)';
|
||||||
|
} else {
|
||||||
|
description = 'All result files created during this job';
|
||||||
|
}
|
||||||
|
return {id: jobResult.id, description: description, filename: jobResult.filename};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JobResultList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><span class="description"></span></td>
|
||||||
|
<td><span class="filename"></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, 'description', 'filename']
|
||||||
|
};
|
18
web/app/static/js/nopaque/lists/QueryResultList.js
Normal file
18
web/app/static/js/nopaque/lists/QueryResultList.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
class QueryResultList extends RessourceList {
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
super(listElement, {...QueryResultList.options, ...options});
|
||||||
|
this.user.eventListeners.queryResult.addEventListener((eventType, payload) => this.eventHandler(eventType, payload));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QueryResultList.options = {
|
||||||
|
item: `<tr>
|
||||||
|
<td><b class="title"></b><br><i class="description"></i><br></td>
|
||||||
|
<td><span class="corpus_title"></span><br><span class="query"></span></td>
|
||||||
|
<td class="right-align">
|
||||||
|
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
|
||||||
|
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="analyse" data-position="top" data-tooltip="Analyse"><i class="material-icons">search</i></a>
|
||||||
|
</td>
|
||||||
|
</tr>`,
|
||||||
|
valueNames: [{data: ['id']}, 'corpus_title', 'description', 'query', 'title']
|
||||||
|
};
|
72
web/app/static/js/nopaque/lists/RessourceList.js
Normal file
72
web/app/static/js/nopaque/lists/RessourceList.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
class RessourceList {
|
||||||
|
/* A wrapper class for the list.js list.
|
||||||
|
* This class is not meant to be used directly, instead it should be used as
|
||||||
|
* a base class for concrete ressource list implementations.
|
||||||
|
*/
|
||||||
|
constructor(listElement, options = {}) {
|
||||||
|
if (listElement.dataset.userId) {
|
||||||
|
if (listElement.dataset.userId in nopaque.appClient.users) {
|
||||||
|
this.user = nopaque.appClient.users[listElement.dataset.userId];
|
||||||
|
} else {
|
||||||
|
console.error(`User not found: ${listElement.dataset.userId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.user = nopaque.appClient.users.self;
|
||||||
|
}
|
||||||
|
this.list = new List(listElement, {...RessourceList.options, ...options});
|
||||||
|
this.list.list.innerHTML = `<tr class="show-if-only-child">
|
||||||
|
<td colspan="3">
|
||||||
|
<span class="card-title"><i class="left material-icons" style="font-size: inherit;">file_download</i>Nothing here...</span>
|
||||||
|
<p>No ressource available (yet).</p>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventHandler(eventType, payload) {
|
||||||
|
switch (eventType) {
|
||||||
|
case 'init':
|
||||||
|
this.init(payload);
|
||||||
|
break;
|
||||||
|
case 'patch':
|
||||||
|
this.patch(payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown event type: ${eventType}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(ressources) {
|
||||||
|
this.list.clear();
|
||||||
|
this.add(Object.values(ressources));
|
||||||
|
this.list.sort('id', {order: 'desc'});
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(patch) {
|
||||||
|
/*
|
||||||
|
* It's not possible to generalize a patch Handler for all type of
|
||||||
|
* ressources. So this method is meant to be an interface.
|
||||||
|
*/
|
||||||
|
console.error('patch method not implemented!');
|
||||||
|
}
|
||||||
|
|
||||||
|
add(values) {
|
||||||
|
let ressources = Array.isArray(values) ? values : [values];
|
||||||
|
if (typeof this.preprocessRessource === 'function') {
|
||||||
|
ressources = ressources.map(ressource => this.preprocessRessource(ressource));
|
||||||
|
}
|
||||||
|
// Set a callback function ('() => {return;}') to force List.js perform the
|
||||||
|
// add method asynchronous: https://listjs.com/api/#add
|
||||||
|
this.list.add(ressources, () => {return;});
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(id) {
|
||||||
|
this.list.remove('id', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
replace(id, valueName, newValue) {
|
||||||
|
this.list.get('id', id)[0].values({[valueName]: newValue});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RessourceList.options = {page: 5, pagination: [{innerWindow: 4, outerWindow: 1}]};
|
@ -70,7 +70,7 @@
|
|||||||
<span class="sort" data-sort="corpus">Corpus</span> and<br>
|
<span class="sort" data-sort="corpus">Corpus</span> and<br>
|
||||||
<span class="sort" data-sort="query">Query</span>
|
<span class="sort" data-sort="query">Query</span>
|
||||||
</th>
|
</th>
|
||||||
<th>{# Actions #}</th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list">
|
<tbody class="list">
|
||||||
@ -122,6 +122,7 @@
|
|||||||
<p><a class="modal-trigger waves-effect waves-light btn" href="#" data-target="new-job-modal"><i class="material-icons left">add</i>New job</a></p>
|
<p><a class="modal-trigger waves-effect waves-light btn" href="#" data-target="new-job-modal"><i class="material-icons left">add</i>New job</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="new-job-modal" class="modal">
|
<div id="new-job-modal" class="modal">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h4>Select a service</h4>
|
<h4>Select a service</h4>
|
||||||
|
@ -253,9 +253,17 @@
|
|||||||
<script src="{{ url_for('static', filename='js/jsonpatch.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/jsonpatch.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/list.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/list.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/socket.io.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/nopaque/index.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/nopaque/main.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/nopaque/RessourceDisplays.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/nopaque/displays/RessourceDisplay.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/nopaque/RessorceLists.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/nopaque/displays/CorpusDisplay.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/displays/JobDisplay.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/RessourceList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/CorpusList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/CorpusFileList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/JobList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/JobInputList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/JobResultList.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/nopaque/lists/QueryResultList.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
// Disable all option elements with no value
|
// Disable all option elements with no value
|
||||||
for (let optionElement of document.querySelectorAll('option[value=""]')) {optionElement.disabled = true;}
|
for (let optionElement of document.querySelectorAll('option[value=""]')) {optionElement.disabled = true;}
|
||||||
|
Loading…
Reference in New Issue
Block a user