Contribute_View_DocumentCreationWindow.js
import { DocumentVisualizerWindow } from '../../Visualizer/DocumentVisualizerWindow';
import { ContributeService } from '../Service/ContributeService';
import * as THREE from 'three';
import { createLabelInput } from '@ud-viz/utils_browser';
import { CameraPositioner } from '@ud-viz/widget_camera_positioner';
/** @class */
export class DocumentCreationWindow {
/**
* Creates a new document creation window.
*
* @param {ContributeService} contributeService The contribute service to
* perform requests.
* @param {import('itowns').PlanarView} itownsView The iTowns view.
* @param {import('itowns').PlanarControls} cameraControls The planar camera controls.
* @param {DocumentVisualizerWindow} documentVisualizer The document image orienter module.
* @param {HTMLElement} parentElementVisualizer - element to add visualizer to
*/
constructor(
contributeService,
itownsView,
cameraControls,
documentVisualizer,
parentElementVisualizer
) {
// create dom ui
this.domElement = document.createElement('div');
this.form = document.createElement('form');
this.domElement.appendChild(this.form);
this.docImage = createLabelInput('File', 'file');
this.docImage.input.setAttribute('name', 'file');
this.form.appendChild(this.docImage.parent);
this.docTitle = createLabelInput('Title', 'text');
this.docTitle.input.setAttribute('name', 'title');
this.form.appendChild(this.docTitle.parent);
this.docDescription = createLabelInput('Description', 'text');
this.docDescription.input.setAttribute('name', 'description');
this.form.appendChild(this.docDescription.parent);
this.pubDate = createLabelInput('Publication date', 'date');
this.pubDate.input.setAttribute('name', 'publicationDate');
this.form.appendChild(this.pubDate.parent);
this.refDate = createLabelInput('Refering date', 'date');
this.refDate.input.setAttribute('name', 'refDate');
this.form.appendChild(this.refDate.parent);
this.source = createLabelInput('Source', 'text');
this.source.input.setAttribute('name', 'source');
this.form.appendChild(this.source.parent);
this.rightsHolder = createLabelInput('Rights holder', 'text');
this.rightsHolder.input.setAttribute('name', 'rightsHolder');
this.form.appendChild(this.rightsHolder.parent);
this.buttonPosition = document.createElement('button');
this.buttonPosition.setAttribute('type', 'button');
this.buttonPosition.innerText = 'Set Position';
this.form.appendChild(this.buttonPosition);
this.inputCreate = document.createElement('input');
this.inputCreate.setAttribute('type', 'submit');
this.inputCreate.value = 'Create';
this.inputCreate.disabled = true;
this.form.appendChild(this.inputCreate);
// end dom
this.parentElementVisualizer = parentElementVisualizer;
/**
* The contribute service to perform requests.
*
* @type {ContributeService}
*/
this.contributeService = contributeService;
/** @type {CameraPositioner} */
this.positioner = new CameraPositioner(itownsView, cameraControls);
this.positioner.addEventListener(
CameraPositioner.EVENT_POSITION_SUBMITTED,
(data) => {
this._registerPositionAndQuaternion(data.message);
}
);
/**
* The camera controls
*
* @type {*}
*/
this.controls = cameraControls;
/**
* The registered camera position for the document visualization.
*
* @type {THREE.Vector3}
*/
this.cameraPosition = null;
/**
* The registered camera orientation for the document visualization.
*
* @type {THREE.Quaternion}
*/
this.cameraQuaternion = undefined;
/**
* The document image orienter module.
*
* @type {DocumentVisualizerWindow}
*/
this.documentVisualizer = documentVisualizer;
/**
* The settings for an accurate movement of the camera. These settings
* should be used in the `PlanarControls` class.
*
* @type {{rotateSpeed: number, zoomInFactor: number, zoomOutFactor: number,
* maxPanSpeed: number, minPanSpeed: number}}
*/
this.accurateControlsSettings = {
rotateSpeed: 1.5,
zoomInFactor: 0.04,
zoomOutFactor: 0.04,
maxPanSpeed: 5.0,
minPanSpeed: 0.01,
};
/**
* The saved state of the planar controls settings. This is used to restore
* the default settings when needed.
*
* @type {{rotateSpeed: number, zoomInFactor: number, zoomOutFactor: number,
* maxPanSpeed: number, minPanSpeed: number}}
*/
this.savedControlsSettings = {};
for (const key of Object.keys(this.accurateControlsSettings)) {
this.savedControlsSettings[key] = this.controls[key];
}
// callbacks
this.form.onsubmit = () => {
this._submitCreation();
return false;
};
this.form.oninput = () => this._updateFormButtons();
this.buttonPosition.onclick = () => this._startPositioningDocument();
this._initForm();
}
dispose() {
this.positioner.domElement.remove();
this.domElement.remove();
this._exitEditMode();
this.documentVisualizer.domElement.remove();
}
// ///////////////////////
// /// DOCUMENT POSITIONER
/**
* Displays the document positioning interfaces : the window positioner and
* the document image orienter.
*
* @private
*/
_startPositioningDocument() {
this.domElement.appendChild(this.positioner.domElement);
this._enterEditMode();
const fileReader = new FileReader();
fileReader.onload = () => {
this.documentVisualizer.setImageSrc(fileReader.result);
this.parentElementVisualizer.appendChild(
this.documentVisualizer.domElement
);
};
fileReader.readAsDataURL(this.docImage.input.files[0]);
}
/**
* Change the controls settings to 'edit mode', ie. the camera moves, zooms
* and rotates slower. This allows the user to place a document with more
* accuracy.
*/
_enterEditMode() {
for (const [key, val] of Object.entries(this.accurateControlsSettings)) {
this.controls[key] = val;
}
}
/**
* Resets the controls settings to their default value.
*/
_exitEditMode() {
for (const [key, val] of Object.entries(this.savedControlsSettings)) {
this.controls[key] = val;
}
}
// ////////
// /// FORM
/**
* Sets the initial values for the form.
*
* @private
*/
_initForm() {
this.docImage.input.value = '';
this.docTitle.input.value = '';
this.source.input.value = '';
this.rightsHolder.input.value = '';
this.docDescription.input.value = '';
this.pubDate.input.value = '';
this.refDate.input.value = '';
this._updateFormButtons();
}
/**
* Checks if the form is ready to be validated. Every entry must have a
* non-empty value, and the camera position / orientation must have been set.
*
* @returns {boolean} True if the validagion succeded
* @private
*/
_formValidation() {
const data = new FormData(this.form);
for (const entry of data.entries()) {
if (!entry[1] || entry[1] === '') {
return false;
}
}
if (!this.cameraPosition || !this.cameraQuaternion) {
return false;
}
return true;
}
/**
* Update the form buttons depending on the current form state. If the
* document have been chosen, we can position it. If the form is valid,
* we can create the document.
*
* @private
*/
_updateFormButtons() {
if (this.docImage.input.value) {
this.buttonPosition.disabled = false;
} else {
this.buttonPosition.disabled = true;
}
if (this._formValidation()) {
this.inputCreate.disabled = false;
} else {
this.inputCreate.disabled = true;
}
}
/**
* Registers the camera position and orientation.
*
* @param {{
* position: THREE.Vector3,
* quaternion: THREE.Quaternion
* }} cameraState Position and orientation of the camera.
*/
_registerPositionAndQuaternion(cameraState) {
this.cameraPosition = cameraState.position;
this.cameraQuaternion = cameraState.quaternion;
this._updateFormButtons();
}
/**
* Proceeds to create the document from the form data.
*
* @private
*/
async _submitCreation() {
if (!this._formValidation()) {
return;
}
const data = new FormData(this.form);
data.append('positionX', this.cameraPosition.x);
data.append('positionY', this.cameraPosition.y);
data.append('positionZ', this.cameraPosition.z);
data.append('quaternionX', this.cameraQuaternion.x);
data.append('quaternionY', this.cameraQuaternion.y);
data.append('quaternionZ', this.cameraQuaternion.z);
data.append('quaternionW', this.cameraQuaternion.w);
try {
await this.contributeService.createDocument(data);
this.dispose();
} catch (e) {
alert(e);
}
}
}