Some Renaming

This commit is contained in:
Patrick Jentsch 2021-01-18 12:50:58 +01:00
parent bda7a782ec
commit 18c96fce4b
16 changed files with 26 additions and 898 deletions

View File

@ -1,207 +0,0 @@
class AppClient {
constructor(currentUserId) {
this.socket = io({transports: ['websocket']});
this.users = {};
this.users.self = this.loadUser(currentUserId);
}
loadUser(userId) {
let user = new User();
this.users[userId] = user;
this.socket.on(`user_${userId}_init`, msg => user.init(JSON.parse(msg)));
this.socket.on(`user_${userId}_patch`, msg => user.patch(JSON.parse(msg)));
this.socket.emit('start_user_session', userId);
return user;
}
}
class User {
constructor() {
this.data = undefined;
this.eventListeners = {
corporaInit: [],
corporaPatch: [],
jobsInit: [],
jobsPatch: [],
queryResultsInit: [],
queryResultsPatch: []
};
}
init(data) {
this.data = data;
let listener;
for (listener of this.eventListeners.corporaInit) {
listener(this.data.corpora);
}
for (listener of this.eventListeners.jobsInit) {
listener(this.data.jobs);
}
for (listener of this.eventListeners.queryResultsInit) {
listener(this.data.query_results);
}
}
patch(patch) {
this.data = jsonpatch.apply_patch(this.data, patch);
let corporaPatch = patch.filter(operation => operation.path.startsWith("/corpora"));
let jobsPatch = patch.filter(operation => operation.path.startsWith("/jobs"));
let queryResultsPatch = patch.filter(operation => operation.path.startsWith("/query_results"));
for (let listener of this.eventListeners.corporaPatch) {
if (corporaPatch.length > 0) {listener(corporaPatch);}
}
for (let listener of this.eventListeners.jobsPatch) {
if (jobsPatch.length > 0) {listener(jobsPatch);}
}
for (let listener of this.eventListeners.queryResultsPatch) {
if (queryResultsPatch.length > 0) {listener(queryResultsPatch);}
}
for (let operation of jobsPatch) {
if (operation.op !== 'replace') {continue;}
// Matches the only path that should be handled here: /jobs/{jobId}/status
if (/^\/jobs\/(\d+)\/status$/.test(operation.path)) {
let [match, jobId] = operation.path.match(/^\/jobs\/(\d+)\/status$/);
if (this.data.settings.job_status_site_notifications === "end" && !["complete", "failed"].includes(operation.value)) {continue;}
nopaque.flash(`[<a href="/jobs/${jobId}">${this.data.jobs[jobId].title}</a>] New status: ${operation.value}`, "job");
}
}
}
addEventListener(type, listener) {
switch (type) {
case 'corporaInit':
this.eventListeners.corporaInit.push(listener);
if (this.data !== undefined) {listener(this.data.corpora);}
break;
case 'corporaPatch':
this.eventListeners.corporaPatch.push(listener);
break;
case 'jobsInit':
this.eventListeners.jobsInit.push(listener);
if (this.data !== undefined) {listener(this.data.jobs);}
break;
case 'jobsPatch':
this.eventListeners.jobsPatch.push(listener);
break;
case 'queryResultsInit':
this.eventListeners.queryResultsInit.push(listener);
if (this.data !== undefined) {listener(this.data.query_results);}
break;
case 'queryResultsPatch':
this.eventListeners.queryResultsPatch.push(listener);
break;
default:
console.error(`Unknown event type: ${type}`);
}
}
}
/*
* The nopaque object is used as a namespace for nopaque specific functions and
* variables.
*/
var nopaque = {};
nopaque.flash = function(message, category) {
let toast;
let toastActionElement;
switch (category) {
case "corpus":
message = `<i class="left material-icons">book</i>${message}`;
break;
case "error":
message = `<i class="left material-icons red-text">error</i>${message}`;
break;
case "job":
message = `<i class="left material-icons">work</i>${message}`;
break;
default:
message = `<i class="left material-icons">notifications</i>${message}`;
}
toast = M.toast({html: `<span>${message}</span>
<button data-action="close" class="btn-flat toast-action white-text">
<i class="material-icons">close</i>
</button>`});
toastActionElement = toast.el.querySelector('.toast-action[data-action="close"]');
toastActionElement.addEventListener('click', () => {toast.dismiss();});
};
nopaque.Forms = {};
nopaque.Forms.init = function() {
var abortRequestElement, parentElement, progressElement, progressModal,
progressModalElement, request, submitElement;
for (let form of document.querySelectorAll(".nopaque-submit-form")) {
submitElement = form.querySelector('button[type="submit"]');
submitElement.addEventListener("click", function() {
for (let selectElement of form.querySelectorAll('select')) {
if (selectElement.value === "") {
parentElement = selectElement.closest(".input-field");
parentElement.querySelector(".select-dropdown").classList.add("invalid");
for (let helperTextElement of parentElement.querySelectorAll(".helper-text")) {
helperTextElement.remove();
}
parentElement.insertAdjacentHTML("beforeend", `<span class="helper-text red-text">Please select an option.</span>`);
}
}
});
request = new XMLHttpRequest();
if (form.dataset.hasOwnProperty("progressModal")) {
progressModalElement = document.getElementById(form.dataset.progressModal);
progressModal = M.Modal.getInstance(progressModalElement);
progressModal.options.dismissible = false;
abortRequestElement = progressModalElement.querySelector(".abort-request");
abortRequestElement.addEventListener("click", function() {request.abort();});
progressElement = progressModalElement.querySelector(".determinate");
}
form.addEventListener("submit", function(event) {
event.preventDefault();
var formData;
formData = new FormData(form);
// Initialize progress modal
if (progressModalElement) {
progressElement.style.width = "0%";
progressModal.open();
}
request.open("POST", window.location.href);
request.send(formData);
});
request.addEventListener("load", function(event) {
var fieldElement;
if (request.status === 201) {
window.location.href = JSON.parse(this.responseText).redirect_url;
}
if (request.status === 400) {
console.log(request);
for (let [field, errors] of Object.entries(JSON.parse(this.responseText))) {
fieldElement = form.querySelector(`input[name$="${field}"]`).closest(".input-field");
for (let error of errors) {
fieldElement.insertAdjacentHTML("beforeend", `<span class="helper-text red-text">${error}</span>`);
}
}
if (progressModalElement) {
progressModal.close();
}
}
if (request.status === 500) {
location.reload();
}
});
if (progressModalElement) {
request.upload.addEventListener("progress", function(event) {
progressElement.style.width = Math.floor(100 * event.loaded / event.total).toString() + "%";
});
}
}
}

View File

@ -1,258 +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 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']
};

View File

@ -1,420 +0,0 @@
class RessourceList extends List {
constructor(idOrElement, subscriberList, type, options) {
if (!type || !["Corpus", "CorpusFile", "Job", "JobInput", "QueryResult", "User"].includes(type)) {
throw "Unknown Type!";
}
super(idOrElement, {...RessourceList.options['common'],
...RessourceList.options[type],
...(options ? options : {})});
if (subscriberList) {subscriberList.push(this);}
this.type = type;
}
_init(ressources) {
this.clear();
this._add(Object.values(ressources));
this.sort("id", {order: "desc"});
}
_update(patch) {
let item, pathArray;
for (let operation of patch) {
/* "/{ressourceName}/{ressourceId}/..." -> ["{ressourceId}", "..."] */
pathArray = operation.path.split("/").slice(2);
switch(operation.op) {
case "add":
if (pathArray.includes("results")) {break;}
this._add([operation.value]);
break;
case "remove":
this.remove("id", pathArray[0]);
break;
case "replace":
item = this.get("id", pathArray[0])[0];
switch(pathArray[1]) {
case "status":
item.values({status: operation.value,
"analyse-link": ["analysing", "prepared", "start analysis"].includes(operation.value) ? `/corpora/${pathArray[0]}/analyse` : ""});
break;
default:
break;
}
default:
break;
}
}
}
_add(values, callback) {
this.add(values.map(x => RessourceList.dataMappers[this.type](x)), callback);
// Initialize modal and tooltipped elements in list
M.AutoInit(this.listContainer);
}
}
RessourceList.dataMappers = {
// A data mapper describes entitys rendered per row. One key value pair holds
// the data to be rendered in the list.js table. Key has to correspond
// with the ValueNames defined below in RessourceList.options ValueNames.
// Links are declared with double ticks(") around them. The key for links
// have to correspond with the class of an <a> element in the
// RessourceList.options item blueprint.
/* ### Corpus mapper ### */
Corpus: corpus => ({
creation_date: corpus.creation_date,
description: corpus.description,
id: corpus.id,
link: `/corpora/${corpus.id}`,
status: corpus.status,
title: corpus.title,
title1: corpus.title,
"analyse-link": ["analysing", "prepared", "start analysis"].includes(corpus.status) ? `/corpora/${corpus.id}/analyse` : "",
"delete-link": `/corpora/${corpus.id}/delete`,
"delete-modal": `delete-corpus-${corpus.id}-modal`,
"delete-modal-trigger": `delete-corpus-${corpus.id}-modal`,
}),
/* ### CorpusFile mapper ### TODO: replace delete-modal with delete-onclick */
CorpusFile: corpus_file => ({
author: corpus_file.author,
filename: corpus_file.filename,
id: corpus_file.id,
link: `${corpus_file.corpus_id}/files/${corpus_file.id}`,
"publishing-year": corpus_file.publishing_year,
title: corpus_file.title,
title1: corpus_file.title,
"delete-link": `/corpora/${corpus_file.corpus_id}/files/${corpus_file.id}/delete`,
"delete-modal": `delete-corpus-file-${corpus_file.id}-modal`,
"delete-modal-trigger": `delete-corpus-file-${corpus_file.id}-modal`,
"download-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/download`,
}),
/* ### Job mapper ### */
Job: job => ({
creation_date: job.creation_date,
description: job.description,
id: job.id,
link: `/jobs/${job.id}`,
service: job.service.name,
status: job.status,
title: job.title,
title1: job.title,
"delete-link": `/jobs/${job.id}/delete`,
"delete-modal": `delete-job-${job.id}-modal`,
"delete-modal-trigger": `delete-job-${job.id}-modal`,
}),
/* ### JobInput mapper ### */
JobInput: job_input => ({
filename: job_input.filename,
id: job_input.job_id,
"download-link": `${job_input.job_id}/inputs/${job_input.id}/download`
}),
/* ### QueryResult mapper ### */
QueryResult: query_result => ({
corpus_name: query_result.query_metadata.corpus_name,
description: query_result.description,
id: query_result.id,
link: `/corpora/result/${query_result.id}`,
query: query_result.query_metadata.query,
title: query_result.title,
"delete-link": `/corpora/result/${query_result.id}/delete`,
"delete-modal": `delete-query-result-${query_result.id}-modal`,
"delete-modal-trigger": `delete-query-result-${query_result.id}-modal`,
"inspect-link": `/corpora/result/${query_result.id}/inspect`,
}),
/* ### User mapper ### */
User: user => ({
confirmed: user.confirmed,
email: user.email,
id: user.id,
link: `users/${user.id}`,
role: user.role.name,
username: user.username,
username2: user.username,
"delete-link": `/admin/users/${user.id}/delete`,
"delete-modal": `delete-user-${user.id}-modal`,
"delete-modal-trigger": `delete-user-${user.id}-modal`,
}),
};
RessourceList.options = {
// common list.js options for 5 rows per page etc.
common: {page: 5, pagination: [{innerWindow: 4, outerWindow: 1}]},
// extended list.js options for 10 rows per page etc.
extended: {
page: 10,
pagination: [
{
name: "paginationTop",
paginationClass: "paginationTop",
innerWindow: 8,
outerWindow: 1
},
{
paginationClass: "paginationBottom",
innerWindow: 8,
outerWindow: 1,
},
],
},
/* Type specific List.js options. Usually only "item" and "valueNames" gets
* defined here but it is possible to define other List.js options.
* item: https://listjs.com/api/#item
* valueNames: https://listjs.com/api/#valueNames
*/
Corpus: {
item: `<tr>
<td>
<a class="btn-floating disabled">
<i class="material-icons service">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>
<div class="right-align">
<a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Edit">
<i class="material-icons">edit</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light analyse-link" data-position="top" data-tooltip="Analyse">
<i class="material-icons">search</i>
</a>
</div>
<div class="modal delete-modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the corpus <b class="title1"></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 delete-link"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</td>
</tr>`,
valueNames: [
"creation_date",
"description",
"title",
"title1",
{data: ["id"]},
{name: "analyse-link", attr: "href"},
{name: "delete-link", attr: "href"},
{name: "delete-modal-trigger", attr: "data-target"},
{name: "delete-modal", attr: "id"},
{name: "link", attr: "href"},
{name: "status", attr: "data-status"},
]
},
CorpusFile: {
item: `<tr>
<td class="filename" style="word-break: break-word;"></td>
<td class="author" style="word-break: break-word;"></td>
<td class="title" style="word-break: break-word;"></td>
<td class="publishing-year" style="word-break: break-word;"></td>
<td>
<div class="right-align">
<a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Edit">
<i class="material-icons">edit</i>
</a>
</div>
<div class="modal delete-modal">
<div class="modal-content">
<h4>Confirm corpus file deletion</h4>
<p>Do you really want to delete the corpus file <b class="title1"></b>? It 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 delete-link"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</td>
</tr>`,
valueNames: [
"author",
"filename",
"publishing-year",
"title",
"title1",
{data: ["id"]},
{name: "delete-link", attr: "href"},
{name: "delete-modal-trigger", attr: "data-target"},
{name: "delete-modal", attr: "id"},
{name: "download-link", attr: "href"},
{name: "link", attr: "href"},
],
},
Job: {
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>
<div class="right-align">
<a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Go to job">
<i class="material-icons">send</i>
</a>
</div>
<div class="modal delete-modal">
<div class="modal-content">
<h4>Confirm job deletion</h4>
<p>Do you really want to delete the job <b class="title1"></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 delete-link"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</td>
</tr>`,
valueNames: [
"creation_date",
"description",
"title",
"title1",
{data: ["id"]},
{name: "delete-link", attr: "href"},
{name: "delete-modal-trigger", attr: "data-target"},
{name: "delete-modal", attr: "id"},
{name: "link", attr: "href"},
{name: "service", attr: "data-service"},
{name: "status", attr: "data-status"},
],
},
JobInput: {
item : `<tr>
<td class="filename"></td>
<td class="right-align">
<a class="btn-floating tooltipped waves-effect waves-light download-link" data-position="top" data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
</td>
</tr>`,
valueNames: [
"filename",
"id",
{name: "download-link", attr: "href"},
],
},
QueryResult: {
item: `<tr>
<td>
<b class="title"></b><br>
<i class="description"></i><br>
</td>
<td>
<span class="corpus_name"></span><br>
<span class="query"></span>
</td>
<td>
<div class="right-align">
<a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Info">
<i class="material-icons">info</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light inspect-link" data-position="top" data-tooltip="Analyse">
<i class="material-icons">search</i>
</a>
</div>
<div class="modal delete-modal">
<div class="modal-content">
<h4>Confirm query result deletion</h4>
<p>Do you really want to delete the query result <b class="title1"></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 delete-link"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</td>
</tr>`,
valueNames: [
"corpus_name",
"description",
"query",
"title",
"title2",
{data: ["id"]},
{name: "delete-link", attr: "href"},
{name: "delete-modal-trigger", attr: "data-target"},
{name: "delete-modal", attr: "id"},
{name: "inspect-link", attr: "href"},
{name: "link", attr: "href"},
],
},
User: {
item: `<tr>
<td class="id"></td>
<td class="username"></td>
<td class="email"></td>
<td class="role"></td>
<td>
<div class="right-align">
<a class="btn-floating modal-trigger red tooltipped waves-effect waves-light delete-modal-trigger" data-position="top" data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
<a class="btn-floating tooltipped waves-effect waves-light link" data-position="top" data-tooltip="Go to user">
<i class="material-icons">send</i>
</a>
</div>
<div class="modal delete-modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the job <b class="title1"></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 delete-link"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
</td>
</tr>`,
valueNames: [
"username",
"username2",
"email",
"role",
"id",
{name: "link", attr: "href"},
{name: "delete-link", attr: "href"},
{name: "delete-modal-trigger", attr: "data-target"},
{name: "delete-modal", attr: "id"},
],
},
};
export { RessourceList, };

View File

@ -6,6 +6,7 @@ class CorpusList extends RessourceList {
} }
init(corpora) { init(corpora) {
console.log('CorpusList.init called');
this.corpora = corpora; this.corpora = corpora;
super.init(corpora); super.init(corpora);
} }

View File

@ -24,6 +24,18 @@ class User {
corpus: { corpus: {
addEventListener(listener, corpusId='*') { addEventListener(listener, corpusId='*') {
if (corpusId in this) {this[corpusId].push(listener);} else {this[corpusId] = [listener];} if (corpusId in this) {this[corpusId].push(listener);} else {this[corpusId] = [listener];}
if (this.data !== undefined) {
console.log('User data is already initialized');
if (corpusId === '*') {
console.log('*');
listener(this.data.corpora);
} else if (corpusId in this.data.corpora) {
console.log(corpusId);
listener(this.data.corpora[corpusId]);
}
} else {
console.log('User data is not initialized');
}
} }
}, },
job: { job: {
@ -40,6 +52,7 @@ class User {
} }
init(data) { init(data) {
console.log('User.init called');
this.data = data; this.data = data;
for (let [corpusId, eventListeners] of Object.entries(this.eventListeners.corpus)) { for (let [corpusId, eventListeners] of Object.entries(this.eventListeners.corpus)) {

View File

@ -254,17 +254,17 @@
<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/main.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/displays/RessourceDisplay.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceDisplays/RessourceDisplay.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/displays/CorpusDisplay.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceDisplays/CorpusDisplay.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/displays/JobDisplay.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceDisplays/JobDisplay.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/RessourceList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/RessourceList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/CorpusList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/CorpusList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/CorpusFileList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/CorpusFileList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/JobList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/JobList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/JobInputList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/JobInputList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/JobResultList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/JobResultList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/QueryResultList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/QueryResultList.js') }}"></script>
<script src="{{ url_for('static', filename='js/nopaque/lists/UserList.js') }}"></script> <script src="{{ url_for('static', filename='js/nopaque/RessourceLists/UserList.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;}
@ -273,8 +273,7 @@
M.Dropdown.init(document.querySelectorAll('#nav-more-dropdown-trigger'), {alignment: 'right', constrainWidth: false, coverTrigger: false}); M.Dropdown.init(document.querySelectorAll('#nav-more-dropdown-trigger'), {alignment: 'right', constrainWidth: false, coverTrigger: false});
nopaque.Forms.init(); nopaque.Forms.init();
for (let flashedMessage of {{ get_flashed_messages(with_categories=True)|tojson }}) {nopaque.flash(flashedMessage[1], flashedMessage[0]);} for (let flashedMessage of {{ get_flashed_messages(with_categories=True)|tojson }}) {nopaque.flash(flashedMessage[1], flashedMessage[0]);}
</script>
<script>
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
nopaque.appClient = new AppClient({{ current_user.id }}); nopaque.appClient = new AppClient({{ current_user.id }});
{% endif %} {% endif %}