mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 04:12:45 +00:00 
			
		
		
		
	Merge branch 'query-builder' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into query-builder
This commit is contained in:
		@@ -104,7 +104,7 @@ class CorpusAnalysisStaticVisualization {
 | 
			
		||||
  renderTextInfoList() {
 | 
			
		||||
    let corpusData = this.data.corpus.o.staticData;
 | 
			
		||||
    let corpusTextInfoListElement = document.querySelector('.corpus-text-info-list');
 | 
			
		||||
    let corpusTextInfoList = new CorpusTextInfoList(corpusTextInfoListElement);
 | 
			
		||||
    let corpusTextInfoList = new ResourceLists.CorpusTextInfoList(corpusTextInfoListElement);
 | 
			
		||||
    let texts = corpusData.s_attrs.text.lexicon;
 | 
			
		||||
    let textData = [];
 | 
			
		||||
    for (let i = 0; i < Object.entries(texts).length; i++) {
 | 
			
		||||
@@ -213,7 +213,7 @@ class CorpusAnalysisStaticVisualization {
 | 
			
		||||
 | 
			
		||||
  async renderTokenList() {
 | 
			
		||||
    let corpusTokenListElement = document.querySelector('.corpus-token-list');
 | 
			
		||||
    let corpusTokenList = new CorpusTokenList(corpusTokenListElement);
 | 
			
		||||
    let corpusTokenList = new ResourceLists.CorpusTokenList(corpusTokenListElement);
 | 
			
		||||
    let filteredData = this.filterData();
 | 
			
		||||
    let stopwords = this.data.stopwords;
 | 
			
		||||
    if (this.data.stopwords === undefined) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
class Utils {
 | 
			
		||||
  static escape(text) {
 | 
			
		||||
    // https://codereview.stackexchange.com/a/126722
 | 
			
		||||
    var table = {
 | 
			
		||||
      '<': 'lt',
 | 
			
		||||
      '>': 'gt',
 | 
			
		||||
      '"': 'quot',
 | 
			
		||||
      '\'': 'apos',
 | 
			
		||||
      '&': 'amp',
 | 
			
		||||
      '\r': '#10',
 | 
			
		||||
      '\n': '#13'
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    return text.toString().replace(/[<>"'\r\n&]/g, (chr) => {
 | 
			
		||||
      return '&' + table[chr] + ';';
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static unescape(escapedText) {
 | 
			
		||||
    var table = {
 | 
			
		||||
      'lt': '<',
 | 
			
		||||
      'gt': '>',
 | 
			
		||||
      'quot': '"',
 | 
			
		||||
      'apos': "'",
 | 
			
		||||
      'amp': '&',
 | 
			
		||||
      '#10': '\r',
 | 
			
		||||
      '#13': '\n'
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    return escapedText.replace(/&(#?\w+);/g, (match, entity) => {
 | 
			
		||||
      if (table.hasOwnProperty(entity)) {
 | 
			
		||||
        return table[entity];
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      return match;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  static HTMLToElement(HTMLString) {
 | 
			
		||||
    let templateElement = document.createElement('template');
 | 
			
		||||
    templateElement.innerHTML = HTMLString.trim();
 | 
			
		||||
    return templateElement.content.firstChild;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static generateElementId(prefix='', suffix='') {
 | 
			
		||||
    for (let i = 0; true; i++) {
 | 
			
		||||
      if (document.querySelector(`#${prefix}${i}${suffix}`) !== null) {continue;}
 | 
			
		||||
      return `${prefix}${i}${suffix}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static isObject(object) {
 | 
			
		||||
    return object !== null && typeof object === 'object' && !Array.isArray(object);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static mergeObjectsDeep(...objects) {
 | 
			
		||||
    let mergedObject = {};
 | 
			
		||||
    if (objects.length === 0) {
 | 
			
		||||
      return mergedObject;
 | 
			
		||||
    }
 | 
			
		||||
    if (!Utils.isObject(objects[0])) {throw 'Cannot merge non-object';}
 | 
			
		||||
    if (objects.length === 1) {
 | 
			
		||||
      return Utils.mergeObjectsDeep(mergedObject, objects[0]);
 | 
			
		||||
    }
 | 
			
		||||
    if (!Utils.isObject(objects[1])) {throw 'Cannot merge non-object';}
 | 
			
		||||
    for (let key in objects[0]) {
 | 
			
		||||
      if (objects[0].hasOwnProperty(key)) {
 | 
			
		||||
        if (objects[1].hasOwnProperty(key)) {
 | 
			
		||||
          if (Utils.isObject(objects[0][key]) && Utils.isObject(objects[1][key])) {
 | 
			
		||||
            mergedObject[key] = Utils.mergeObjectsDeep(objects[0][key], objects[1][key]);
 | 
			
		||||
          } else {
 | 
			
		||||
            mergedObject[key] = objects[1][key];
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          mergedObject[key] = objects[0][key];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    for (let key in objects[1]) {
 | 
			
		||||
      if (objects[1].hasOwnProperty(key)) {
 | 
			
		||||
        if (!objects[0].hasOwnProperty(key)) {
 | 
			
		||||
          mergedObject[key] = objects[1][key];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (objects.length === 2) {
 | 
			
		||||
      return mergedObject;
 | 
			
		||||
    }
 | 
			
		||||
    return Utils.mergeObjectsDeep(mergedObject, ...objects.slice(2));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
class App {
 | 
			
		||||
App.App = class App {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.data = {
 | 
			
		||||
      promises: {getUser: {}, subscribeUser: {}},
 | 
			
		||||
@@ -101,4 +101,4 @@ class App {
 | 
			
		||||
    // Apply Patch
 | 
			
		||||
    jsonpatch.applyPatch(this.data, filteredPatch);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								app/static/js/app/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/static/js/app/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
App = {};
 | 
			
		||||
@@ -524,7 +524,7 @@ cqi.api.APIClient = class APIClient {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Dump the values of <field> for match ranges <first> .. <last>
 | 
			
		||||
   * in <subcorpus>. <field> is one of the CQI_CONST_FIELD_* constants.
 | 
			
		||||
   * in <subcorpus>. <field> is one of the cqi.constants.FIELD_* constants.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param {string} subcorpus
 | 
			
		||||
   * @param {number} field
 | 
			
		||||
@@ -561,9 +561,9 @@ cqi.api.APIClient = class APIClient {
 | 
			
		||||
   *
 | 
			
		||||
   * returns <n> (id, frequency) pairs flattened into a list of size 2*<n>
 | 
			
		||||
   * field is one of
 | 
			
		||||
   * - CQI_CONST_FIELD_MATCH
 | 
			
		||||
   * - CQI_CONST_FIELD_TARGET
 | 
			
		||||
   * - CQI_CONST_FIELD_KEYWORD
 | 
			
		||||
   * - cqi.constants.FIELD_MATCH
 | 
			
		||||
   * - cqi.constants.FIELD_TARGET
 | 
			
		||||
   * - cqi.constants.FIELD_KEYWORD
 | 
			
		||||
   *
 | 
			
		||||
   * NB: pairs are sorted by frequency desc.
 | 
			
		||||
   * 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								app/static/js/cqi/constants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/static/js/cqi/constants.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
cqi.constants = {};
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_KEYWORD = 9;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_MATCH = 16;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_MATCHEND = 17;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET = 0;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_0 = 0;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_1 = 1;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_2 = 2;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_3 = 3;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_4 = 4;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_5 = 5;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_6 = 6;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_7 = 7;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_8 = 8;
 | 
			
		||||
 | 
			
		||||
/** @type {number} */
 | 
			
		||||
cqi.constants.FIELD_TARGET_9 = 9;
 | 
			
		||||
@@ -1,6 +1 @@
 | 
			
		||||
var cqi = {};
 | 
			
		||||
 | 
			
		||||
cqi.CONST_FIELD_KEYWORD = 9;
 | 
			
		||||
cqi.CONST_FIELD_MATCH = 16;
 | 
			
		||||
cqi.CONST_FIELD_MATCHEND = 17;
 | 
			
		||||
cqi.CONST_FIELD_TARGET = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -145,17 +145,17 @@ cqi.models.subcorpora.SubcorpusCollection = class SubcorpusCollection extends cq
 | 
			
		||||
    let apiName = `${this.corpus.apiName}:${subcorpusName}`;
 | 
			
		||||
     /** @type {object} */
 | 
			
		||||
    let fields = {};
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.CONST_FIELD_MATCH)) {
 | 
			
		||||
      fields.match = cqi.CONST_FIELD_MATCH;
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.constants.FIELD_MATCH)) {
 | 
			
		||||
      fields.match = cqi.constants.FIELD_MATCH;
 | 
			
		||||
    }
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.CONST_FIELD_MATCHEND)) {
 | 
			
		||||
      fields.matchend = cqi.CONST_FIELD_MATCHEND
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.constants.FIELD_MATCHEND)) {
 | 
			
		||||
      fields.matchend = cqi.constants.FIELD_MATCHEND
 | 
			
		||||
    }
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.CONST_FIELD_TARGET)) {
 | 
			
		||||
      fields.target = cqi.CONST_FIELD_TARGET
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.constants.FIELD_TARGET)) {
 | 
			
		||||
      fields.target = cqi.constants.FIELD_TARGET
 | 
			
		||||
    }
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.CONST_FIELD_KEYWORD)) {
 | 
			
		||||
      fields.keyword = cqi.CONST_FIELD_KEYWORD
 | 
			
		||||
    if (await this.client.api.cqp_subcorpus_has_field(apiName, cqi.constants.FIELD_KEYWORD)) {
 | 
			
		||||
      fields.keyword = cqi.constants.FIELD_KEYWORD
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      api_name: apiName,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										138
									
								
								app/static/js/forms/base-form.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								app/static/js/forms/base-form.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
Forms.BaseForm = class BaseForm {
 | 
			
		||||
  static htmlClass;
 | 
			
		||||
 | 
			
		||||
  constructor(formElement) {
 | 
			
		||||
    this.formElement = formElement;
 | 
			
		||||
    this.eventListeners = {
 | 
			
		||||
      'requestLoad': []
 | 
			
		||||
    };
 | 
			
		||||
    this.afterRequestListeners = [];
 | 
			
		||||
 | 
			
		||||
    for (let selectElement of this.formElement.querySelectorAll('select')) {
 | 
			
		||||
      selectElement.removeAttribute('required');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.formElement.addEventListener('submit', (event) => {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      this.submit(event);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addEventListener(eventType, listener) {
 | 
			
		||||
    if (eventType in this.eventListeners) {
 | 
			
		||||
      this.eventListeners[eventType].push(listener);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw `Unknown event type ${eventType}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  submit(event) {
 | 
			
		||||
    let request = new XMLHttpRequest();
 | 
			
		||||
    let modalElement = Utils.HTMLToElement(
 | 
			
		||||
      `
 | 
			
		||||
        <div class="modal">
 | 
			
		||||
          <div class="modal-content">
 | 
			
		||||
            <h4><i class="material-icons left">file_upload</i>Submitting...</h4>
 | 
			
		||||
            <div class="progress">
 | 
			
		||||
              <div class="determinate" style="width: 0%"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="modal-footer">
 | 
			
		||||
            <a class="action-button btn red waves-effect waves-light modal-close" data-action="cancel">Cancel</a>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      `
 | 
			
		||||
    );
 | 
			
		||||
    document.querySelector('#modals').appendChild(modalElement);
 | 
			
		||||
    let modal = M.Modal.init(
 | 
			
		||||
      modalElement,
 | 
			
		||||
      {
 | 
			
		||||
        dismissible: false,
 | 
			
		||||
        onCloseEnd: () => {
 | 
			
		||||
          modal.destroy();
 | 
			
		||||
          modalElement.remove();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
    modal.open();
 | 
			
		||||
 | 
			
		||||
    // Remove all previous helper text elements that indicate errors
 | 
			
		||||
    let errorHelperTextElements = this.formElement
 | 
			
		||||
      .querySelectorAll('.helper-text[data-helper-text-type="error"]');
 | 
			
		||||
    for (let errorHelperTextElement of errorHelperTextElements) {
 | 
			
		||||
      errorHelperTextElement.remove();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if select elements are filled out properly
 | 
			
		||||
    for (let selectElement of this.formElement.querySelectorAll('select')) {
 | 
			
		||||
      if (selectElement.value === '') {
 | 
			
		||||
        let inputFieldElement = selectElement.closest('.input-field');
 | 
			
		||||
        let errorHelperTextElement = Utils.HTMLToElement(
 | 
			
		||||
          '<span class="helper-text error-color-text" data-helper-text-type="error">Please select an option.</span>'
 | 
			
		||||
        );
 | 
			
		||||
        inputFieldElement.appendChild(errorHelperTextElement);
 | 
			
		||||
        inputFieldElement.querySelector('.select-dropdown').classList.add('invalid');
 | 
			
		||||
        modal.close();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Setup abort handling
 | 
			
		||||
    let cancelElement = modalElement.querySelector('.action-button[data-action="cancel"]');
 | 
			
		||||
    cancelElement.addEventListener('click', (event) => {request.abort();});
 | 
			
		||||
 | 
			
		||||
    // Setup load handling (after the request completed)
 | 
			
		||||
    request.addEventListener('load', (event) => {
 | 
			
		||||
      for (let listener of this.eventListeners['requestLoad']) {
 | 
			
		||||
        listener(event);
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 400) {
 | 
			
		||||
        let responseJson = JSON.parse(request.responseText);
 | 
			
		||||
        for (let [inputName, inputErrors] of Object.entries(responseJson.errors)) {
 | 
			
		||||
          let inputFieldElement = this.formElement
 | 
			
		||||
            .querySelector(`input[name$="${inputName}"], select[name$="${inputName}"]`)
 | 
			
		||||
            .closest('.input-field');
 | 
			
		||||
          for (let inputError of inputErrors) {
 | 
			
		||||
            let errorHelperTextElement = Utils.HTMLToElement(
 | 
			
		||||
              `<span class="helper-text error-color-text" data-helper-type="error">${inputError}</span>`
 | 
			
		||||
            );
 | 
			
		||||
            inputFieldElement.appendChild(errorHelperTextElement);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 500) {
 | 
			
		||||
        app.flash('Internal Server Error', 'error');
 | 
			
		||||
      }
 | 
			
		||||
      modal.close();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Setup progress handling
 | 
			
		||||
    let progressBarElement = modalElement.querySelector('.progress > .determinate');
 | 
			
		||||
    request.upload.addEventListener('progress', (event) => {
 | 
			
		||||
      let progress = Math.floor(100 * event.loaded / event.total);
 | 
			
		||||
      progressBarElement.style.width = `${progress}%`;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    request.open(this.formElement.method, this.formElement.action);
 | 
			
		||||
    request.setRequestHeader('Accept', 'application/json');
 | 
			
		||||
    let formData = new FormData(this.formElement);
 | 
			
		||||
    switch (this.formElement.enctype) {
 | 
			
		||||
      case 'application/x-www-form-urlencoded': {
 | 
			
		||||
        let urlSearchParams = new URLSearchParams(formData);
 | 
			
		||||
        request.send(urlSearchParams);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 'multipart/form-data': {
 | 
			
		||||
        request.send(formData);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 'text/plain': {
 | 
			
		||||
        throw 'enctype "text/plain" is not supported';
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default: {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -16,142 +16,3 @@ Forms.autoInit = () => {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Forms.BaseForm = class BaseForm {
 | 
			
		||||
  static htmlClass;
 | 
			
		||||
 | 
			
		||||
  constructor(formElement) {
 | 
			
		||||
    this.formElement = formElement;
 | 
			
		||||
    this.eventListeners = {
 | 
			
		||||
      'requestLoad': []
 | 
			
		||||
    };
 | 
			
		||||
    this.afterRequestListeners = [];
 | 
			
		||||
 | 
			
		||||
    for (let selectElement of this.formElement.querySelectorAll('select')) {
 | 
			
		||||
      selectElement.removeAttribute('required');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.formElement.addEventListener('submit', (event) => {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      this.submit(event);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addEventListener(eventType, listener) {
 | 
			
		||||
    if (eventType in this.eventListeners) {
 | 
			
		||||
      this.eventListeners[eventType].push(listener);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw `Unknown event type ${eventType}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  submit(event) {
 | 
			
		||||
    let request = new XMLHttpRequest();
 | 
			
		||||
    let modalElement = Utils.HTMLToElement(
 | 
			
		||||
      `
 | 
			
		||||
        <div class="modal">
 | 
			
		||||
          <div class="modal-content">
 | 
			
		||||
            <h4><i class="material-icons left">file_upload</i>Submitting...</h4>
 | 
			
		||||
            <div class="progress">
 | 
			
		||||
              <div class="determinate" style="width: 0%"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="modal-footer">
 | 
			
		||||
            <a class="action-button btn red waves-effect waves-light modal-close" data-action="cancel">Cancel</a>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      `
 | 
			
		||||
    );
 | 
			
		||||
    document.querySelector('#modals').appendChild(modalElement);
 | 
			
		||||
    let modal = M.Modal.init(
 | 
			
		||||
      modalElement,
 | 
			
		||||
      {
 | 
			
		||||
        dismissible: false,
 | 
			
		||||
        onCloseEnd: () => {
 | 
			
		||||
          modal.destroy();
 | 
			
		||||
          modalElement.remove();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
    modal.open();
 | 
			
		||||
 | 
			
		||||
    // Remove all previous helper text elements that indicate errors
 | 
			
		||||
    let errorHelperTextElements = this.formElement
 | 
			
		||||
      .querySelectorAll('.helper-text[data-helper-text-type="error"]');
 | 
			
		||||
    for (let errorHelperTextElement of errorHelperTextElements) {
 | 
			
		||||
      errorHelperTextElement.remove();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if select elements are filled out properly
 | 
			
		||||
    for (let selectElement of this.formElement.querySelectorAll('select')) {
 | 
			
		||||
      if (selectElement.value === '') {
 | 
			
		||||
        let inputFieldElement = selectElement.closest('.input-field');
 | 
			
		||||
        let errorHelperTextElement = Utils.HTMLToElement(
 | 
			
		||||
          '<span class="helper-text error-color-text" data-helper-text-type="error">Please select an option.</span>'
 | 
			
		||||
        );
 | 
			
		||||
        inputFieldElement.appendChild(errorHelperTextElement);
 | 
			
		||||
        inputFieldElement.querySelector('.select-dropdown').classList.add('invalid');
 | 
			
		||||
        modal.close();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Setup abort handling
 | 
			
		||||
    let cancelElement = modalElement.querySelector('.action-button[data-action="cancel"]');
 | 
			
		||||
    cancelElement.addEventListener('click', (event) => {request.abort();});
 | 
			
		||||
 | 
			
		||||
    // Setup load handling (after the request completed)
 | 
			
		||||
    request.addEventListener('load', (event) => {
 | 
			
		||||
      for (let listener of this.eventListeners['requestLoad']) {
 | 
			
		||||
        listener(event);
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 400) {
 | 
			
		||||
        let responseJson = JSON.parse(request.responseText);
 | 
			
		||||
        for (let [inputName, inputErrors] of Object.entries(responseJson.errors)) {
 | 
			
		||||
          let inputFieldElement = this.formElement
 | 
			
		||||
            .querySelector(`input[name$="${inputName}"], select[name$="${inputName}"]`)
 | 
			
		||||
            .closest('.input-field');
 | 
			
		||||
          for (let inputError of inputErrors) {
 | 
			
		||||
            let errorHelperTextElement = Utils.HTMLToElement(
 | 
			
		||||
              `<span class="helper-text error-color-text" data-helper-type="error">${inputError}</span>`
 | 
			
		||||
            );
 | 
			
		||||
            inputFieldElement.appendChild(errorHelperTextElement);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 500) {
 | 
			
		||||
        app.flash('Internal Server Error', 'error');
 | 
			
		||||
      }
 | 
			
		||||
      modal.close();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Setup progress handling
 | 
			
		||||
    let progressBarElement = modalElement.querySelector('.progress > .determinate');
 | 
			
		||||
    request.upload.addEventListener('progress', (event) => {
 | 
			
		||||
      let progress = Math.floor(100 * event.loaded / event.total);
 | 
			
		||||
      progressBarElement.style.width = `${progress}%`;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    request.open(this.formElement.method, this.formElement.action);
 | 
			
		||||
    request.setRequestHeader('Accept', 'application/json');
 | 
			
		||||
    let formData = new FormData(this.formElement);
 | 
			
		||||
    switch (this.formElement.enctype) {
 | 
			
		||||
      case 'application/x-www-form-urlencoded': {
 | 
			
		||||
        let urlSearchParams = new URLSearchParams(formData);
 | 
			
		||||
        request.send(urlSearchParams);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 'multipart/form-data': {
 | 
			
		||||
        request.send(formData);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 'text/plain': {
 | 
			
		||||
        throw 'enctype "text/plain" is not supported';
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default: {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
ResourceDisplays.CorpusDisplay = class CorpusDisplay extends ResourceDisplays.BaseDisplay {
 | 
			
		||||
ResourceDisplays.CorpusDisplay = class CorpusDisplay extends ResourceDisplays.ResourceDisplay {
 | 
			
		||||
  static htmlClass = 'corpus-display';
 | 
			
		||||
 | 
			
		||||
  constructor(displayElement) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@ var ResourceDisplays = {};
 | 
			
		||||
ResourceDisplays.autoInit = () => {
 | 
			
		||||
  for (let propertyName in ResourceDisplays) {
 | 
			
		||||
    let property = ResourceDisplays[propertyName];
 | 
			
		||||
    // Call autoInit of all properties that are subclasses of `ResourceDisplays.BaseDisplay`.
 | 
			
		||||
    // This does not include `ResourceDisplays.BaseDisplay` itself.
 | 
			
		||||
    if (property.prototype instanceof ResourceDisplays.BaseDisplay) {
 | 
			
		||||
    // Call autoInit of all properties that are subclasses of `ResourceDisplays.ResourceDisplay`.
 | 
			
		||||
    // This does not include `ResourceDisplays.ResourceDisplay` itself.
 | 
			
		||||
    if (property.prototype instanceof ResourceDisplays.ResourceDisplay) {
 | 
			
		||||
      // Check if the static `htmlClass` property is defined.
 | 
			
		||||
      if (property.htmlClass === undefined) {return;}
 | 
			
		||||
      // Gather all HTML elements that have the `this.htmlClass` class
 | 
			
		||||
@@ -15,51 +15,4 @@ ResourceDisplays.autoInit = () => {
 | 
			
		||||
      for (let displayElement of displayElements) {new property(displayElement);}
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResourceDisplays.BaseDisplay = class BaseDisplay {
 | 
			
		||||
  static htmlClass;
 | 
			
		||||
 | 
			
		||||
  constructor(displayElement) {
 | 
			
		||||
    this.displayElement = displayElement;
 | 
			
		||||
    this.userId = this.displayElement.dataset.userId;
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
    if (this.userId) {
 | 
			
		||||
      app.subscribeUser(this.userId)
 | 
			
		||||
        .then((response) => {
 | 
			
		||||
          app.socket.on('PATCH', (patch) => {
 | 
			
		||||
            if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      app.getUser(this.userId)
 | 
			
		||||
        .then((user) => {
 | 
			
		||||
          this.init(user);
 | 
			
		||||
          this.isInitialized = true;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init(user) {throw 'Not implemented';}
 | 
			
		||||
 | 
			
		||||
  onPatch(patch) {throw 'Not implemented';}
 | 
			
		||||
 | 
			
		||||
  setElement(element, value) {
 | 
			
		||||
    switch (element.tagName) {
 | 
			
		||||
      case 'INPUT': {
 | 
			
		||||
        element.value = value;
 | 
			
		||||
        M.updateTextFields();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default: {
 | 
			
		||||
        element.innerText = value;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setElements(elements, value) {
 | 
			
		||||
    for (let element of elements) {
 | 
			
		||||
      this.setElement(element, value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
ResourceDisplays.JobDisplay = class JobDisplay extends ResourceDisplays.BaseDisplay {
 | 
			
		||||
ResourceDisplays.JobDisplay = class JobDisplay extends ResourceDisplays.ResourceDisplay {
 | 
			
		||||
  static htmlClass = 'job-display';
 | 
			
		||||
 | 
			
		||||
  constructor(displayElement) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								app/static/js/resource-displays/resource-display.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/static/js/resource-displays/resource-display.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
ResourceDisplays.ResourceDisplay = class ResourceDisplay {
 | 
			
		||||
  static htmlClass;
 | 
			
		||||
 | 
			
		||||
  constructor(displayElement) {
 | 
			
		||||
    this.displayElement = displayElement;
 | 
			
		||||
    this.userId = this.displayElement.dataset.userId;
 | 
			
		||||
    this.isInitialized = false;
 | 
			
		||||
    if (this.userId) {
 | 
			
		||||
      app.subscribeUser(this.userId)
 | 
			
		||||
        .then((response) => {
 | 
			
		||||
          app.socket.on('PATCH', (patch) => {
 | 
			
		||||
            if (this.isInitialized) {this.onPatch(patch);}
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
      app.getUser(this.userId)
 | 
			
		||||
        .then((user) => {
 | 
			
		||||
          this.init(user);
 | 
			
		||||
          this.isInitialized = true;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init(user) {throw 'Not implemented';}
 | 
			
		||||
 | 
			
		||||
  onPatch(patch) {throw 'Not implemented';}
 | 
			
		||||
 | 
			
		||||
  setElement(element, value) {
 | 
			
		||||
    switch (element.tagName) {
 | 
			
		||||
      case 'INPUT': {
 | 
			
		||||
        element.value = value;
 | 
			
		||||
        M.updateTextFields();
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default: {
 | 
			
		||||
        element.innerText = value;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setElements(elements, value) {
 | 
			
		||||
    for (let element of elements) {
 | 
			
		||||
      this.setElement(element, value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class AdminUserList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let adminUserListElement of document.querySelectorAll('.admin-user-list:not(.no-autoinit)')) {
 | 
			
		||||
      new AdminUserList(adminUserListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.AdminUserList = class AdminUserList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'admin-user-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -108,4 +104,4 @@ class AdminUserList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class CorpusFileList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let corpusFileListElement of document.querySelectorAll('.corpus-file-list:not(.no-autoinit)')) {
 | 
			
		||||
      new CorpusFileList(corpusFileListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.CorpusFileList = class CorpusFileList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'corpus-file-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -369,4 +365,4 @@ class CorpusFileList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class CorpusFollowerList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let corpusFollowerListElement of document.querySelectorAll('.corpus-follower-list:not(.no-autoinit)')) {
 | 
			
		||||
      new CorpusFollowerList(corpusFollowerListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.CorpusFollowerList = class CorpusFollowerList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'corpus-follower-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -196,4 +192,4 @@ class CorpusFollowerList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class CorpusList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let corpusListElement of document.querySelectorAll('.corpus-list:not(.no-autoinit)')) {
 | 
			
		||||
      new CorpusList(corpusListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.CorpusList = class CorpusList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'corpus-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -370,4 +366,4 @@ class CorpusList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,10 +1,5 @@
 | 
			
		||||
class CorpusTextInfoList extends ResourceList {
 | 
			
		||||
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let corpusTextInfoListElement of document.querySelectorAll('.corpus-text-info-list:not(.no-autoinit)')) {
 | 
			
		||||
      new CorpusTextInfoList(corpusTextInfoListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.CorpusTextInfoList = class CorpusTextInfoList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'corpus-text-info-list';
 | 
			
		||||
 | 
			
		||||
  static defaultOptions = {
 | 
			
		||||
    page: 5
 | 
			
		||||
@@ -12,7 +7,7 @@ class CorpusTextInfoList extends ResourceList {
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    let _options = Utils.mergeObjectsDeep(
 | 
			
		||||
      CorpusTextInfoList.defaultOptions,
 | 
			
		||||
      ResourceLists.CorpusTextInfoList.defaultOptions,
 | 
			
		||||
      options
 | 
			
		||||
    );
 | 
			
		||||
    super(listContainerElement, _options);
 | 
			
		||||
@@ -26,7 +21,7 @@ class CorpusTextInfoList extends ResourceList {
 | 
			
		||||
  get item() {
 | 
			
		||||
    return (values) => {
 | 
			
		||||
      return `
 | 
			
		||||
        <tr class="list-item clickable hoverable">
 | 
			
		||||
        <tr class="list-item hoverable">
 | 
			
		||||
          <td><span class="title"></span> (<span class="publishing_year"></span>)</td>
 | 
			
		||||
          <td><span class="num_tokens"></span></td>
 | 
			
		||||
          <td><span class="num_sentences"></span></td>
 | 
			
		||||
@@ -109,4 +104,4 @@ class CorpusTextInfoList extends ResourceList {
 | 
			
		||||
    clickedSortElement.style.color = '#aa9cc9';
 | 
			
		||||
    clickedSortElement.innerHTML = clickedSortElement.classList.contains('asc') ? 'arrow_drop_down' : 'arrow_drop_up';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class CorpusTokenList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let corpusTokenListElement of document.querySelectorAll('.corpus-token-list:not(.no-autoinit)')) {
 | 
			
		||||
      new CorpusTokenList(corpusTokenListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.CorpusTokenList = class CorpusTokenList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'corpus-token-list';
 | 
			
		||||
 | 
			
		||||
  static defaultOptions = {
 | 
			
		||||
    page: 7
 | 
			
		||||
@@ -11,7 +7,7 @@ class CorpusTokenList extends ResourceList {
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    let _options = Utils.mergeObjectsDeep(
 | 
			
		||||
      CorpusTokenList.defaultOptions,
 | 
			
		||||
      ResourceLists.CorpusTokenList.defaultOptions,
 | 
			
		||||
      options
 | 
			
		||||
    );
 | 
			
		||||
    super(listContainerElement, _options);
 | 
			
		||||
@@ -138,4 +134,4 @@ class CorpusTokenList extends ResourceList {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
class DetailledPublicCorpusList extends CorpusList {
 | 
			
		||||
ResourceLists.DetailedPublicCorpusList = class DetailedPublicCorpusList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'detailed-public-corpus-list';
 | 
			
		||||
 | 
			
		||||
  get item() {
 | 
			
		||||
    return (values) => {
 | 
			
		||||
      return `
 | 
			
		||||
@@ -68,4 +70,4 @@ class DetailledPublicCorpusList extends CorpusList {
 | 
			
		||||
      'current-user-is-following': Object.values(corpus.corpus_follower_associations).some(association => association.follower.id === currentUserId)
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										18
									
								
								app/static/js/resource-lists/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/static/js/resource-lists/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
var ResourceLists = {};
 | 
			
		||||
 | 
			
		||||
ResourceLists.autoInit = () => {
 | 
			
		||||
  for (let propertyName in ResourceLists) {
 | 
			
		||||
    let property = ResourceLists[propertyName];
 | 
			
		||||
    // Call autoInit of all properties that are subclasses of `ResourceLists.ResourceList`.
 | 
			
		||||
    // This does not include `ResourceLists.ResourceList` itself.
 | 
			
		||||
    if (property.prototype instanceof ResourceLists.ResourceList) {
 | 
			
		||||
      // Check if the static `htmlClass` property is defined.
 | 
			
		||||
      if (property.htmlClass === undefined) {return;}
 | 
			
		||||
      // Gather all HTML elements that have the `this.htmlClass` class
 | 
			
		||||
      // and do not have the `no-autoinit` class.
 | 
			
		||||
      let listElements = document.querySelectorAll(`.${property.htmlClass}:not(.no-autoinit)`);
 | 
			
		||||
      // Create an instance of this class for each display element.
 | 
			
		||||
      for (let listElement of listElements) {new property(listElement);}
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class JobInputList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let jobInputListElement of document.querySelectorAll('.job-input-list:not(.no-autoinit)')) {
 | 
			
		||||
      new JobInputList(jobInputListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.JobInputList = class JobInputList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'job-input-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -90,4 +86,4 @@ class JobInputList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class JobList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let jobListElement of document.querySelectorAll('.job-list:not(.no-autoinit)')) {
 | 
			
		||||
      new JobList(jobListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.JobList = class JobList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'job-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -323,4 +319,4 @@ class JobList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class JobResultList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let jobResultListElement of document.querySelectorAll('.job-result-list:not(.no-autoinit)')) {
 | 
			
		||||
      new JobResultList(jobResultListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.JobResultList = class JobResultList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'job-result-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -115,4 +111,4 @@ class JobResultList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
class PublicCorpusList extends CorpusList {
 | 
			
		||||
ResourceLists.PublicCorpusList = class PublicCorpusList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'public-corpus-list';
 | 
			
		||||
 | 
			
		||||
  get item() {
 | 
			
		||||
    return (values) => {
 | 
			
		||||
      return `
 | 
			
		||||
@@ -52,4 +54,4 @@ class PublicCorpusList extends CorpusList {
 | 
			
		||||
      <ul class="pagination"></ul>
 | 
			
		||||
    `.trim();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,23 +1,10 @@
 | 
			
		||||
class ResourceList {
 | 
			
		||||
ResourceLists.ResourceList = class ResourceList {
 | 
			
		||||
  /* 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 resource list implementations.
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    CorpusList.autoInit();
 | 
			
		||||
    CorpusFileList.autoInit();
 | 
			
		||||
    JobList.autoInit();
 | 
			
		||||
    JobInputList.autoInit();
 | 
			
		||||
    JobResultList.autoInit();
 | 
			
		||||
    SpaCyNLPPipelineModelList.autoInit();
 | 
			
		||||
    TesseractOCRPipelineModelList.autoInit();
 | 
			
		||||
    UserList.autoInit();
 | 
			
		||||
    AdminUserList.autoInit();
 | 
			
		||||
    CorpusFollowerList.autoInit();
 | 
			
		||||
    CorpusTextInfoList.autoInit();
 | 
			
		||||
    CorpusTokenList.autoInit();
 | 
			
		||||
  }
 | 
			
		||||
  static htmlClass;
 | 
			
		||||
 | 
			
		||||
  static defaultOptions = {
 | 
			
		||||
    page: 5,
 | 
			
		||||
@@ -36,7 +23,7 @@ class ResourceList {
 | 
			
		||||
    }
 | 
			
		||||
    let _options = Utils.mergeObjectsDeep(
 | 
			
		||||
      {item: this.item, valueNames: this.valueNames},
 | 
			
		||||
      ResourceList.defaultOptions,
 | 
			
		||||
      ResourceLists.ResourceList.defaultOptions,
 | 
			
		||||
      options
 | 
			
		||||
    );
 | 
			
		||||
    this.listContainerElement = listContainerElement;
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class SpaCyNLPPipelineModelList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let spaCyNLPPipelineModelListElement of document.querySelectorAll('.spacy-nlp-pipeline-model-list:not(.no-autoinit)')) {
 | 
			
		||||
      new SpaCyNLPPipelineModelList(spaCyNLPPipelineModelListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.SpaCyNLPPipelineModelList = class SpaCyNLPPipelineModelList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'spacy-nlp-pipeline-model-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -220,4 +216,4 @@ class SpaCyNLPPipelineModelList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class TesseractOCRPipelineModelList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let tesseractOCRPipelineModelListElement of document.querySelectorAll('.tesseract-ocr-pipeline-model-list:not(.no-autoinit)')) {
 | 
			
		||||
      new TesseractOCRPipelineModelList(tesseractOCRPipelineModelListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.TesseractOCRPipelineModelList = class TesseractOCRPipelineModelList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'tesseract-ocr-pipeline-model-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -229,4 +225,4 @@ class TesseractOCRPipelineModelList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
class UserList extends ResourceList {
 | 
			
		||||
  static autoInit() {
 | 
			
		||||
    for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) {
 | 
			
		||||
      new UserList(userListElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
ResourceLists.UserList = class UserList extends ResourceLists.ResourceList {
 | 
			
		||||
  static htmlClass = 'user-list';
 | 
			
		||||
 | 
			
		||||
  constructor(listContainerElement, options = {}) {
 | 
			
		||||
    super(listContainerElement, options);
 | 
			
		||||
@@ -101,4 +97,4 @@ class UserList extends ResourceList {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								app/static/js/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/static/js/utils/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
Utils = {};
 | 
			
		||||
							
								
								
									
										89
									
								
								app/static/js/utils/utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/static/js/utils/utils.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
Utils.escape = (text) => {
 | 
			
		||||
  // https://codereview.stackexchange.com/a/126722
 | 
			
		||||
  var table = {
 | 
			
		||||
    '<': 'lt',
 | 
			
		||||
    '>': 'gt',
 | 
			
		||||
    '"': 'quot',
 | 
			
		||||
    '\'': 'apos',
 | 
			
		||||
    '&': 'amp',
 | 
			
		||||
    '\r': '#10',
 | 
			
		||||
    '\n': '#13'
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  return text.toString().replace(/[<>"'\r\n&]/g, (chr) => {
 | 
			
		||||
    return '&' + table[chr] + ';';
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Utils.unescape = (escapedText) => {
 | 
			
		||||
  var table = {
 | 
			
		||||
    'lt': '<',
 | 
			
		||||
    'gt': '>',
 | 
			
		||||
    'quot': '"',
 | 
			
		||||
    'apos': "'",
 | 
			
		||||
    'amp': '&',
 | 
			
		||||
    '#10': '\r',
 | 
			
		||||
    '#13': '\n'
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  return escapedText.replace(/&(#?\w+);/g, (match, entity) => {
 | 
			
		||||
    if (table.hasOwnProperty(entity)) {
 | 
			
		||||
      return table[entity];
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return match;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Utils.HTMLToElement = (HTMLString) => {
 | 
			
		||||
  let templateElement = document.createElement('template');
 | 
			
		||||
  templateElement.innerHTML = HTMLString.trim();
 | 
			
		||||
  return templateElement.content.firstChild;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Utils.generateElementId = (prefix='', suffix='') => {
 | 
			
		||||
  for (let i = 0; true; i++) {
 | 
			
		||||
    if (document.querySelector(`#${prefix}${i}${suffix}`) !== null) {continue;}
 | 
			
		||||
    return `${prefix}${i}${suffix}`;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Utils.isObject = (object) => {
 | 
			
		||||
  return object !== null && typeof object === 'object' && !Array.isArray(object);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Utils.mergeObjectsDeep = (...objects) => {
 | 
			
		||||
  let mergedObject = {};
 | 
			
		||||
  if (objects.length === 0) {
 | 
			
		||||
    return mergedObject;
 | 
			
		||||
  }
 | 
			
		||||
  if (!Utils.isObject(objects[0])) {throw 'Cannot merge non-object';}
 | 
			
		||||
  if (objects.length === 1) {
 | 
			
		||||
    return Utils.mergeObjectsDeep(mergedObject, objects[0]);
 | 
			
		||||
  }
 | 
			
		||||
  if (!Utils.isObject(objects[1])) {throw 'Cannot merge non-object';}
 | 
			
		||||
  for (let key in objects[0]) {
 | 
			
		||||
    if (objects[0].hasOwnProperty(key)) {
 | 
			
		||||
      if (objects[1].hasOwnProperty(key)) {
 | 
			
		||||
        if (Utils.isObject(objects[0][key]) && Utils.isObject(objects[1][key])) {
 | 
			
		||||
          mergedObject[key] = Utils.mergeObjectsDeep(objects[0][key], objects[1][key]);
 | 
			
		||||
        } else {
 | 
			
		||||
          mergedObject[key] = objects[1][key];
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        mergedObject[key] = objects[0][key];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  for (let key in objects[1]) {
 | 
			
		||||
    if (objects[1].hasOwnProperty(key)) {
 | 
			
		||||
      if (!objects[0].hasOwnProperty(key)) {
 | 
			
		||||
        mergedObject[key] = objects[1][key];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (objects.length === 2) {
 | 
			
		||||
    return mergedObject;
 | 
			
		||||
  }
 | 
			
		||||
  return Utils.mergeObjectsDeep(mergedObject, ...objects.slice(2));
 | 
			
		||||
};
 | 
			
		||||
@@ -7,9 +7,17 @@
 | 
			
		||||
{%- assets
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/app.%(version)s.js',
 | 
			
		||||
  'js/App.js',
 | 
			
		||||
  'js/Utils.js',
 | 
			
		||||
  'js/XMLtoObject.js'
 | 
			
		||||
  'js/app/index.js',
 | 
			
		||||
  'js/app/app.js'
 | 
			
		||||
%}
 | 
			
		||||
<script src="{{ ASSET_URL }}"></script>
 | 
			
		||||
{%- endassets %}
 | 
			
		||||
 | 
			
		||||
{%- assets
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/utils.%(version)s.js',
 | 
			
		||||
  'js/utils/index.js',
 | 
			
		||||
  'js/utils/utils.js'
 | 
			
		||||
%}
 | 
			
		||||
<script src="{{ ASSET_URL }}"></script>
 | 
			
		||||
{%- endassets %}
 | 
			
		||||
@@ -18,6 +26,7 @@
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/cqi.%(version)s.js',
 | 
			
		||||
  'js/cqi/index.js',
 | 
			
		||||
  'js/cqi/constants.js',
 | 
			
		||||
  'js/cqi/errors.js',
 | 
			
		||||
  'js/cqi/status.js',
 | 
			
		||||
  'js/cqi/api/index.js',
 | 
			
		||||
@@ -36,6 +45,7 @@
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/Forms.%(version)s.js',
 | 
			
		||||
  'js/forms/index.js',
 | 
			
		||||
  'js/forms/base-form.js',
 | 
			
		||||
  'js/forms/create-contribution-form.js',
 | 
			
		||||
  'js/forms/create-corpus-file-form.js',
 | 
			
		||||
  'js/forms/create-job-form.js'
 | 
			
		||||
@@ -47,6 +57,7 @@
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/resource-displays.%(version)s.js',
 | 
			
		||||
  'js/resource-displays/index.js',
 | 
			
		||||
  'js/resource-displays/resource-display.js',
 | 
			
		||||
  'js/resource-displays/corpus-display.js',
 | 
			
		||||
  'js/resource-displays/job-display.js'
 | 
			
		||||
%}
 | 
			
		||||
@@ -55,22 +66,23 @@
 | 
			
		||||
 | 
			
		||||
{%- assets
 | 
			
		||||
  filters='rjsmin',
 | 
			
		||||
  output='gen/ResourceLists.%(version)s.js',
 | 
			
		||||
  'js/ResourceLists/ResourceList.js',
 | 
			
		||||
  'js/ResourceLists/CorpusFileList.js',
 | 
			
		||||
  'js/ResourceLists/CorpusList.js',
 | 
			
		||||
  'js/ResourceLists/PublicCorpusList.js',
 | 
			
		||||
  'js/ResourceLists/JobList.js',
 | 
			
		||||
  'js/ResourceLists/JobInputList.js',
 | 
			
		||||
  'js/ResourceLists/JobResultList.js',
 | 
			
		||||
  'js/ResourceLists/SpacyNLPPipelineModelList.js',
 | 
			
		||||
  'js/ResourceLists/TesseractOCRPipelineModelList.js',
 | 
			
		||||
  'js/ResourceLists/UserList.js',
 | 
			
		||||
  'js/ResourceLists/AdminUserList.js',
 | 
			
		||||
  'js/ResourceLists/CorpusFollowerList.js',
 | 
			
		||||
  'js/ResourceLists/CorpusTextInfoList.js',
 | 
			
		||||
  'js/ResourceLists/DetailledPublicCorpusList.js',
 | 
			
		||||
  'js/ResourceLists/CorpusTokenList.js'
 | 
			
		||||
  output='gen/resource-lists.%(version)s.js',
 | 
			
		||||
  'js/resource-lists/index.js',
 | 
			
		||||
  'js/resource-lists/resource-list.js',
 | 
			
		||||
  'js/resource-lists/admin-user-list.js',
 | 
			
		||||
  'js/resource-lists/corpus-file-list.js',
 | 
			
		||||
  'js/resource-lists/corpus-follower-list.js',
 | 
			
		||||
  'js/resource-lists/corpus-list.js',
 | 
			
		||||
  'js/resource-lists/corpus-text-info-list.js',
 | 
			
		||||
  'js/resource-lists/corpus-token-list.js',
 | 
			
		||||
  'js/resource-lists/detailed-public-corpus-list.js',
 | 
			
		||||
  'js/resource-lists/job-input-list.js',
 | 
			
		||||
  'js/resource-lists/job-list.js',
 | 
			
		||||
  'js/resource-lists/job-result-list.js',
 | 
			
		||||
  'js/resource-lists/public-corpus-list.js',
 | 
			
		||||
  'js/resource-lists/spacy-nlp-pipeline-model-list.js',
 | 
			
		||||
  'js/resource-lists/tesseract-ocr-pipeline-model-list.js',
 | 
			
		||||
  'js/resource-lists/user-list.js'
 | 
			
		||||
%}
 | 
			
		||||
<script src="{{ ASSET_URL }}"></script>
 | 
			
		||||
{%- endassets %}
 | 
			
		||||
@@ -106,7 +118,7 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
  // TODO: Implement an app.run method and use this for all of the following
 | 
			
		||||
  const app = new App();
 | 
			
		||||
  const app = new App.App();
 | 
			
		||||
  {%- if current_user.is_authenticated %}
 | 
			
		||||
  const currentUserId = {{ current_user.hashid|tojson }};
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +153,7 @@
 | 
			
		||||
    {alignment: 'right', constrainWidth: false, coverTrigger: false}
 | 
			
		||||
  );
 | 
			
		||||
  ResourceDisplays.autoInit();
 | 
			
		||||
  ResourceList.autoInit();
 | 
			
		||||
  ResourceLists.autoInit();
 | 
			
		||||
  Forms.autoInit();
 | 
			
		||||
 | 
			
		||||
  // Display flashed messages
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let corpusListElement = document.querySelector('#corpus-list');
 | 
			
		||||
  let corpusList = new CorpusList(corpusListElement);
 | 
			
		||||
  let corpusList = new ResourceLists.CorpusList(corpusListElement);
 | 
			
		||||
  corpusList.add(
 | 
			
		||||
    [
 | 
			
		||||
      {% for corpus in corpora %}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let adminUserListElement = document.querySelector('#admin-user-list');
 | 
			
		||||
  let adminUserList = new AdminUserList(adminUserListElement);
 | 
			
		||||
  let adminUserList = new ResourceLists.AdminUserList(adminUserListElement);
 | 
			
		||||
  adminUserList.add(
 | 
			
		||||
    [
 | 
			
		||||
      {% for user in users %}
 | 
			
		||||
 
 | 
			
		||||
@@ -242,7 +242,7 @@
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
let publicCorpusFileList = new CorpusFileList(document.querySelector('#corpus-file-list'));
 | 
			
		||||
let publicCorpusFileList = new ResourceLists.CorpusFileList(document.querySelector('#corpus-file-list'));
 | 
			
		||||
publicCorpusFileList.add(
 | 
			
		||||
  [
 | 
			
		||||
    {% for corpus_file in corpus.files %}
 | 
			
		||||
@@ -259,7 +259,7 @@ refreshButton.addEventListener('click', () => {
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
{% if cfr.has_permission('MANAGE_FOLLOWERS') %}
 | 
			
		||||
let publicCorpusFollowerList = new CorpusFollowerList(document.querySelector('.corpus-follower-list'));
 | 
			
		||||
let publicCorpusFollowerList = new ResourceLists.CorpusFollowerList(document.querySelector('.corpus-follower-list'));
 | 
			
		||||
publicCorpusFollowerList.add(
 | 
			
		||||
  [
 | 
			
		||||
    {% for cfa in cfas %}
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,8 @@
 | 
			
		||||
          return response.text();
 | 
			
		||||
        })
 | 
			
		||||
        .then((responseText) => {return new DOMParser().parseFromString(responseText, 'application/xml');})
 | 
			
		||||
        .then((xmlDocument) => {return xmlDocument.toObject();})
 | 
			
		||||
        // .then((xmlDocument) => {return xmlDocument.toObject();})
 | 
			
		||||
        .then((xmlDocument) => {return {};})
 | 
			
		||||
        .then((feed) => {resolve(feed);});
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@
 | 
			
		||||
{% block scripts %}
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
  let userList = new UserList(document.querySelector('.user-list'));
 | 
			
		||||
  let userList = new ResourceLists.UserList(document.querySelector('.user-list'));
 | 
			
		||||
  userList.add(
 | 
			
		||||
    [
 | 
			
		||||
      {% for user in users %}
 | 
			
		||||
@@ -70,7 +70,7 @@
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    ]
 | 
			
		||||
  );
 | 
			
		||||
  let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
  let publicCorpusList = new ResourceLists.PublicCorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
  publicCorpusList.add(
 | 
			
		||||
    [
 | 
			
		||||
      {% for corpus in corpora %}
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@
 | 
			
		||||
{% block scripts %}
 | 
			
		||||
{{ super() }}
 | 
			
		||||
<script>
 | 
			
		||||
let followedCorpusList = new PublicCorpusList(document.querySelector('.followed-corpus-list'));
 | 
			
		||||
let followedCorpusList = new ResourceLists.PublicCorpusList(document.querySelector('.followed-corpus-list'));
 | 
			
		||||
followedCorpusList.add(
 | 
			
		||||
  [
 | 
			
		||||
    {% for corpus in user.followed_corpora %}
 | 
			
		||||
@@ -132,7 +132,7 @@ followedCorpusList.add(
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  ]
 | 
			
		||||
);
 | 
			
		||||
let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
let publicCorpusList = new ResourceLists.PublicCorpusList(document.querySelector('.public-corpus-list'));
 | 
			
		||||
publicCorpusList.add(
 | 
			
		||||
  [
 | 
			
		||||
    {% for corpus in user.corpora %}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,50 +6,88 @@ NO_COLOR="\033[0m"
 | 
			
		||||
CHECK_MARK="\xE2\x9C\x93"
 | 
			
		||||
CROSS_MARK="\xE2\x9D\x8C"
 | 
			
		||||
 | 
			
		||||
echo -n "Set container UID and GIDs to match the host system..."
 | 
			
		||||
 | 
			
		||||
if [[ "${NOPAQUE_UID}" == 0 ]]; then
 | 
			
		||||
if [[ "${NOPAQUE_UID}" == "0" ]]; then
 | 
			
		||||
    echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
    echo "Running as root is not allowed"
 | 
			
		||||
    exit 1
 | 
			
		||||
else
 | 
			
		||||
    echo ""
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo -n "- Updating docker GID ($(getent group docker | cut -d: -f3) -> ${DOCKER_GID})... "
 | 
			
		||||
groupmod --gid "${DOCKER_GID}" docker > /dev/null
 | 
			
		||||
if [[ "${?}" == "0" ]]; then
 | 
			
		||||
 | 
			
		||||
echo "Set container UID and GIDs to match the host system..."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##############################################################################
 | 
			
		||||
# docker GID                                                                 #
 | 
			
		||||
##############################################################################
 | 
			
		||||
if [[ "${DOCKER_GID}" == "$(getent group docker | cut -d: -f3)" ]]; then
 | 
			
		||||
    echo -n "- docker GID is already matching..."
 | 
			
		||||
    echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
else
 | 
			
		||||
    echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
    exit 1
 | 
			
		||||
    echo -n "- Updating docker GID ($(getent group docker | cut -d: -f3) -> ${DOCKER_GID})... "
 | 
			
		||||
    groupmod --gid "${DOCKER_GID}" docker > /dev/null
 | 
			
		||||
    if [[ "${?}" == "0" ]]; then
 | 
			
		||||
        echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
    else
 | 
			
		||||
        echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo -n "- Updating nopaque GID ($(id -g nopaque) -> ${NOPAQUE_GID})... "
 | 
			
		||||
groupmod --gid "${NOPAQUE_GID}" nopaque > /dev/null
 | 
			
		||||
if [[ "${?}" == "0" ]]; then
 | 
			
		||||
 | 
			
		||||
##############################################################################
 | 
			
		||||
# nopaque GID                                                                #
 | 
			
		||||
##############################################################################
 | 
			
		||||
if [[ "${NOPAQUE_GID}" == "$(id -g nopaque)" ]]; then
 | 
			
		||||
    echo -n "- nopaque GID is already matching..."
 | 
			
		||||
    echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
else
 | 
			
		||||
    echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
    exit 1
 | 
			
		||||
    echo -n "- Updating nopaque GID ($(id -g nopaque) -> ${NOPAQUE_GID})... "
 | 
			
		||||
    groupmod --gid "${NOPAQUE_GID}" nopaque > /dev/null
 | 
			
		||||
    if [[ "${?}" == "0" ]]; then
 | 
			
		||||
        HAS_NOPAQUE_GID_CHANGED=true
 | 
			
		||||
        echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
    else
 | 
			
		||||
        echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    echo -n "- Updating nopaque directory group... "
 | 
			
		||||
    chown -R :nopaque /home/nopaque
 | 
			
		||||
    if [[ "${?}" == "0" ]]; then
 | 
			
		||||
        echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
    else
 | 
			
		||||
        echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo -n "- Updating nopaque UID ($(id -u nopaque) -> ${NOPAQUE_UID})... "
 | 
			
		||||
usermod --uid "${NOPAQUE_UID}" nopaque > /dev/null
 | 
			
		||||
if [[ "${?}" == "0" ]]; then
 | 
			
		||||
 | 
			
		||||
##############################################################################
 | 
			
		||||
# nopaque UID                                                                #
 | 
			
		||||
##############################################################################
 | 
			
		||||
if [[ "${NOPAQUE_UID}" == "$(id -u nopaque)" ]]; then
 | 
			
		||||
    echo -n "- nopaque UID is already matching..."
 | 
			
		||||
    echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
else
 | 
			
		||||
    echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
    exit 1
 | 
			
		||||
    echo -n "- Updating nopaque UID ($(id -u nopaque) -> ${NOPAQUE_UID})... "
 | 
			
		||||
    usermod --uid "${NOPAQUE_UID}" nopaque > /dev/null
 | 
			
		||||
    if [[ "${?}" == "0" ]]; then
 | 
			
		||||
        echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
    else
 | 
			
		||||
        echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    echo -n "- Updating nopaque directory owner... "
 | 
			
		||||
    chown -R nopaque /home/nopaque
 | 
			
		||||
    if [[ "${?}" == "0" ]]; then
 | 
			
		||||
        echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
    else
 | 
			
		||||
        echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo -n "- Updating nopaque directory owner and group... "
 | 
			
		||||
chown -R nopaque:nopaque /home/nopaque
 | 
			
		||||
if [[ "${?}" == "0" ]]; then
 | 
			
		||||
    echo -e "${GREEN_COLOR}${CHECK_MARK}${NO_COLOR}"
 | 
			
		||||
else
 | 
			
		||||
    echo -e "${RED_COLOR}${CROSS_MARK}${NO_COLOR}"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exec gosu nopaque ./boot.sh ${@}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user