mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 09:15:41 +00:00
259 lines
12 KiB
JavaScript
259 lines
12 KiB
JavaScript
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 template 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.valueNames = ['id'];
|
|
for (let element of this.list.valueNames) {
|
|
switch (typeof element) {
|
|
case 'object':
|
|
if (element.hasOwnProperty('name')) {this.valueNames.push(element.name);}
|
|
break;
|
|
case 'string':
|
|
this.valueNames.push(element);
|
|
break;
|
|
default:
|
|
console.error(`Unknown value name definition: ${element}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
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];
|
|
// Discard ressource values, that are not defined to be used in the list.
|
|
ressources = ressources.map(ressource => {
|
|
let cleanedRessource = {};
|
|
for (let [valueName, value] of Object.entries(ressource)) {
|
|
if (this.valueNames.includes(valueName)) {cleanedRessource[valueName] = value;}
|
|
}
|
|
return cleanedRessource;
|
|
});
|
|
// 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) {
|
|
if (this.valueNames.includes(valueName)) {
|
|
let item = this.list.get('id', id)[0];
|
|
item.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.user.addEventListener('corporaInit', corpora => this.init(corpora));
|
|
this.user.addEventListener('corporaPatch', patch => this.patch(patch));
|
|
listElement.addEventListener('click', (event) => {this.onclick(event)});
|
|
}
|
|
|
|
onclick(event) {
|
|
let corpusId = event.target.closest('tr').dataset.id;
|
|
let actionButtonElement = event.target.closest('.action-button');
|
|
let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
|
|
switch (action) {
|
|
case 'analyse':
|
|
window.location.href = nopaque.user.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>${nopaque.user.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="${nopaque.user.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 = nopaque.user.corpora[corpusId].url;
|
|
break;
|
|
default:
|
|
console.error(`Unknown action: ${action}`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
patch(patch) {
|
|
for (let operation of patch) {
|
|
switch(operation.op) {
|
|
case 'add':
|
|
// Matches the only paths that should be handled here: /corpora/{corpusId}
|
|
if (/^\/corpora\/(\d+)$/.test(operation.path)) {this.add(operation.value);}
|
|
break;
|
|
case 'remove':
|
|
// See case 'add' ;)
|
|
if (/^\/corpora\/(\d+)$/.test(operation.path)) {
|
|
let [match, id] = operation.path.match(/^\/corpora\/(\d+)$/);
|
|
this.remove(corpusId);
|
|
}
|
|
break;
|
|
case 'replace':
|
|
// Matches the only paths that should be handled here: /corpora/{corpusId}/{status || description || title}
|
|
if (/^\/corpora\/(\d+)\/(status|description|title)$/.test(operation.path)) {
|
|
let [match, id, valueName] = operation.path.match(/^\/corpora\/(\d+)\/(status|description|title)$/);
|
|
this.replace(id, valueName, operation.value);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 JobList extends RessourceList {
|
|
constructor(listElement, options = {}) {
|
|
super(listElement, {...JobList.options, ...options});
|
|
this.user.addEventListener('jobsInit', jobs => this.init(jobs));
|
|
this.user.addEventListener('jobsPatch', patch => this.patch(patch));
|
|
listElement.addEventListener('click', (event) => {this.onclick(event)});
|
|
}
|
|
|
|
onclick(event) {
|
|
let jobId = event.target.closest('tr').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) {
|
|
for (let operation of patch) {
|
|
switch(operation.op) {
|
|
case 'add':
|
|
// Matches the only paths that should be handled here: /jobs/{jobId}
|
|
if (/^\/jobs\/(\d+)$/.test(operation.path)) {this.add(operation.value);}
|
|
break;
|
|
case 'remove':
|
|
// See case add ;)
|
|
if (/^\/jobs\/(\d+)$/.test(operation.path)) {
|
|
let [match, id] = operation.path.match(/^\/jobs\/(\d+)$/);
|
|
this.remove(jobId);
|
|
}
|
|
break;
|
|
case 'replace':
|
|
// Matches the only paths that should be handled here: /jobs/{jobId}/{service || status || description || title}
|
|
if (/^\/jobs\/(\d+)\/(service|status|description|title)$/.test(operation.path)) {
|
|
let [match, id, valueName] = operation.path.match(/^\/jobs\/(\d+)\/(service|status|description|title)$/);
|
|
this.replace(id, valueName, operation.value);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 QueryResultList extends RessourceList {
|
|
constructor(listElement, options = {}) {
|
|
super(listElement, {...QueryResultList.options, ...options});
|
|
this.user.addEventListener('queryResultsInit', queryResults => this.init(queryResults));
|
|
this.user.addEventListener('queryResultsPatch', patch => this.init(patch));
|
|
}
|
|
}
|
|
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']
|
|
};
|