Commit 08e2c531 by Jorem Magcawas

commit all files

parent 9422f066
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Web-GDE</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>
# Web-GDE
A prototype Web General Data Entry Tool.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ending session...</title>
</head>
<body>
<div class="dv_body">
<div class="content">
<div class="panel-widget" style="display: flex;justify-content: center;padding: 100px 0;">
<div class="panel-body">
<div class="logout-icon">
<img src="https://static.vecteezy.com/system/resources/thumbnails/000/593/212/small/40_350.jpg" width="200px" height="200px" alt="logout icon" style="display: block;margin-left: auto;margin-right: auto;width: 50%;">
</div>
<h1 style="text-align: center;font-family: sans-serif; font-size: 36px;color: rgb(54,54,54); max-width: 500px; min-width: 100px;">Session has been successfully ended</h1>
<h3 style="text-align: center;font-family: sans-serif;color: rgb(54,54,54); max-width: 500px; min-width: 100px;">Thank you for using GDE Tool</h3>
<h5 style="text-align: center;font-family: sans-serif;color: rgb(54,54,54); max-width: 500px; min-width: 100px;">Please click <a href="./index.html">here</a> to return to the GDE Tool</h5>
</div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
/**
* for testing purposes, below are different files to be used showing
* some possible configurations of the file
*/
// schema with no "SECTION" key
// const SCHEMA_FILE = "./src/sample_schema/no_section.json"
// schema with missing fields (fieldLabel, validation)
// const SCHEMA_FILE = "./src/sample_schema/missing_fields.json"
// schema with 20 fields
// const SCHEMA_FILE = "./src/sample_schema/20_field_schema.json"
// schema with 15 fields
// const SCHEMA_FILE = "./src/sample_schema/15_field_schema.json"
// schema with 10 fields
const SCHEMA_FILE = "./src/sample_schema/10_field_schema.json"
// schema with 5 fields and all possible collections
// const SCHEMA_FILE = "./src/sample_schema/5_field_schema.json"
// const SCHEMA_FILE = "./src/sample_schema/Sample_Schema.json"
const INPUT_FILES = ["./input/Ong, Mae Janica - Application Form.TIFF","./input/Magalona, Rowell James - Application Form.TIFF","./input/Felizardo, Brylle Theodure - Application Form.TIFF","./input/Laxamana, Conrad John - Application Form.TIFF"] // list of URLs
const OUTPUT_FILES = "../../output/"
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <link rel="stylesheet" type="text/css" href="gde.css"> -->
<!-- CSS for the fields -->
<link rel="stylesheet" href="style.css">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.rawgit.com/seikichi/tiff.js/master/tiff.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.8.0/jszip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.8.0/xlsx.js"></script>
<script src="https://requirejs.org/docs/release/2.3.5/minified/require.js"></script>
<script src="./src/accessFile/accessFile.js"></script>
<script src="./src/XMLWriter/Global.js" language="javascript"></script>
<script src="./src/XMLWriter/XMLWriter.js" language="javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js%22%3E"></script>
<script src="./src/XMLWriter/XML_Saver.js" language="javascript"></script>
<script src="./src/submit/submit.js" language="javascript"></script>
</head>
<script src="./src/fetchConfig/fetchConfig.js"></script>
<script>
fetchConfig();
</script>
<body>
<div id="no-config" style="display:none"> There is no config file found</div>
<div id="with-config" style="display:grid">
<!-- Creates the right pane of the window -->
<aside class="sidebar" id="sidebar">
<!-- Holds the login credentials -->
<!-- <div class="sidebar" id="username">
<button class="sidebar" id="user-settings">...</button>
<span class="sidebar" id="username-text">username@svi ()</span>
</div> -->
<!-- Holds the project description -->
<!-- <aside class="sidebar" id="project-description">
<p class="sidebar" id="column"><br />
</p>
<p class="sidebar" id="column">
</p>
</aside> -->
<!-- Space for the fields -->
<!-- <div class="sidebar" id="fields"> </div> -->
<form id='fields' style="display: flex; flex-direction:column;" onsubmit="return submitForm(event);"></form>
</aside>
<!-- Embed viewer -->
<main id="viewer">
<!-- CHANGED: commented out button that triggers the modal -->
<!-- <div> -->
<!-- CHANGED: keep img since id is used for displaying image on modal -->
<img id="TestTIFFDisplay">
<!-- <br><br> -->
<!-- Trigger/Open The Modal -->
<!-- <button id="TestBtn">Open TIFF Image</button>
</div> -->
<div id="TiffViewerModal">
<!-- Modal content -->
<div class="TiffModalContent">
<div class="bar">
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<div id="status">&nbsp;</div>
<h3 id="progress">&nbsp;</h3>
</div>
<div id="TiffModalHeader">
<!-- CHANGED: commented out close button, to avoid closing the modal -->
<!-- <span class="TiffModalClose">&times;</span> -->
</div>
<div id="TiffModalBody">
</div>
<div id="TiffModalFooter">
</div>
</div>
</div>
<img src="" id="tableBanner" />
<img id="myImage" crossorigin="anonymous" src="">
<img id="ImgPrev" crossorigin="anonymous" src="">
<img id="my-img" />
<img id="img" />
</main>
</div>
</body>
<script src="./config.js"></script>
<script src="./src/captureMetrics/captureMetrics.js"></script>
<script src="./src/fetchSchema/fetchSchema.js"></script><!-- fetch the schema -->
<script src="./src/validateSchema/validateSchema.js"></script> <!-- validate the schema -->
<script src="./src/validateInput/validateInput.js"></script> <!-- functions for validating inputs -->
<script src="./src/getFields/getFields.js"></script> <!-- display the input fields -->
<!-- <script src="./src/accessFile/accessFile.js"></script> -->
<script>
if (!found) { //if config file is not found, change the screen
let withConfig = document.getElementById("with-config");
let noConfig = document.getElementById("no-config");
if (withConfig.style.display === "grid") {
withConfig.style.display = "none";
noConfig.style.display = "block";
}
} else {
startMetricCapture();
displayFields("fields");
accessFile();
}
</script>
<script src="./src/endSession/endSession.js"></script>
<script src="./src/tiffViewer/tiffViewer.js"></script>
<script src="./src/highlight/highlight.js"></script>
</html>
\ No newline at end of file
function JSettings()
{
this.IE=document.all?true:false;
this.MouseX=_JSettings_MouseX;
this.MouseY=_JSettings_MouseY;
this.SrcElement=_JSettings_SrcElement;
this.Parent=_JSettings_Parent;
this.RunOnLoad=_JSettings_RunOnLoad;
this.FindParent=_JSettings_FindParent;
this.FindChild=_JSettings_FindChild;
this.FindSibling=_JSettings_FindSibling;
this.FindParentTag=_JSettings_FindParentTag;
}
function _JSettings_MouseX(e)
{return this.IE?event.clientX:e.clientX;}
function _JSettings_MouseY(e)
{return this.IE?event.clientY:e.clientY;}
function _JSettings_SrcElement(e)
{return this.IE?event.srcElement:e.target;}
function _JSettings_Parent(Node)
{return this.IE?Node.parentNode:Node.parentElement;}
function _JSettings_RunOnLoad(Meth){var Prev=(window.onload)?window.onload:function(){};window.onload=function(){Prev();Meth();};}
function _JSettings_FindParent(Node, Attrib, Value)
{var Root = document.getElementsByTagName("BODY")[0];
Node = Node.parentNode; while (Node != Root && Node.getAttribute(Attrib) != Value){Node=Node.parentNode;}
if (Node.getAttribute(Attrib) == Value) {return Node;} else {return null;}}
function _JSettings_FindParentTag(Node, TagName)
{var Root = document.getElementsByTagName("BODY")[0];
TagName=TagName.toLowerCase();
Node = Node.parentNode; while (Node != Root && Node.tagName.toLowerCase() != TagName){Node=Node.parentNode;}
if (Node.tagName.toLowerCase() == TagName) {return Node;} else {return null;}}
function _JSettings_FindChild(Node, Attrib, Value)
{
if (Node.getAttribute)
if (Node.getAttribute(Attrib) == Value) return Node;
var I=0;
var Ret = null;
for (I=0;I<Node.childNodes.length;I++)
{
Ret = FindChildByAttrib(Node.childNodes[I]);
if (Ret) return Ret;
}
return null;
}
function _JSettings_FindSibling(Node, Attrib, Value)
{
var Nodes=Node.parentNode.childNodes;
var I=0;
for (I=0;I<Nodes.length;I++)
{
if (Nodes[I].getAttribute)
{
if (Nodes[I].getAttribute(Attrib) == Value)
{return Nodes[I];}
}
}
return null;
}
var Settings = new JSettings();
\ No newline at end of file
function XMLWriter()
{
this.XML=[];
this.Nodes=[];
this.State="";
this.FormatXML = function(Str)
{
if (Str)
return Str.replace(/&/g, "&amp;").replace(/\"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
return ""
}
this.BeginNode = function(Name)
{
if (!Name) return;
if (this.State=="beg") this.XML.push(">");
this.State="beg";
this.Nodes.push(Name);
this.XML.push("<"+Name);
}
this.EndNode = function()
{
if (this.State=="beg")
{
this.XML.push("/>");
this.Nodes.pop();
}
else if (this.Nodes.length>0)
this.XML.push("</"+this.Nodes.pop()+">");
this.State="";
}
this.Attrib = function(Name, Value)
{
if (this.State!="beg" || !Name) return;
this.XML.push(" "+Name+"=\""+this.FormatXML(Value)+"\"");
}
this.WriteString = function(Value)
{
if (this.State=="beg") this.XML.push(">");
this.XML.push(this.FormatXML(Value));
this.State="";
}
this.Node = function(Name, Value)
{
if (!Name) return;
if (this.State=="beg") this.XML.push(">");
this.XML.push((Value=="" || !Value)?"<"+Name+"/>":"<"+Name+">"+this.FormatXML(Value)+"</"+Name+">");
this.State="";
}
this.Close = function()
{
while (this.Nodes.length>0)
this.EndNode();
this.State="closed";
}
this.ToString = function(){return this.XML.join("");}
}
\ No newline at end of file
function WriteForm(e,metrics)
{
try
{
const myArray = Object.values(metrics);
localStorage.setItem("submit", "1");
var Frm=Settings.SrcElement(e);
var XML=new XMLWriter();
XML.WriteString('<?xml version="1.0" encoding="UTF-8" standalone="no"?>');
XML.BeginNode(Frm.name);
var Nodes=Frm.elements;
XML.Node("Image_Source_Path",File_Path);
XML.Node("No_of_Keystrokes",JSON.stringify(myArray[0]));
XML.Node("Processing_Time_Seconds",JSON.stringify(myArray[1]));
for (var i=0;i<Nodes.length;i++){
XML.Node(Nodes[i].id, Nodes[i].value);
console.log(Nodes[i]);
}
XML.EndNode();
XML.Close();
var final_xml=XML.ToString().replace(/</g,"\n<").replace(/&lt;/g,"\<").replace(/&quot;/g,'"').replace(/&gt;/g,">").replace(/\n<\//g,"</").replace(/<\/xml>/g,"\n</xml>");
var myFile = new File([final_xml], File_Name + ".xml", {type: "text/plain;charset=utf-8"});
// saveAs(myFile);
let formData = new FormData();
formData.append("file", myFile);
fetch('./src/XMLWriter/upload.php', {
method: "POST",
body: formData
});
}
catch(Err)
{
alert("Error: " + Err.description);
}
return false;
}
<?php
/* Get the name of the uploaded file */
$filename = $_FILES['file']['name'];
/* Choose where to save the uploaded file */
$location = "../../output/".$filename;
/* Save the uploaded file to the local filesystem */
if ( move_uploaded_file($_FILES['file']['tmp_name'], $location) ) {
echo 'Success';
} else {
echo 'Failure';
}
?>
\ No newline at end of file
var File_Name;
var File_Path;
function accessFile() {
var button = 0;
var size = 0;
const elStatus = document.getElementById('status');
function status(text) {
elStatus.innerHTML = text;
}
const progressBar = document.getElementById('progressBar');
const elProgress = document.getElementById('progress');
function progress({ loaded, total }) {
// elProgress.innerHTML = Math.round(loaded * .000001) + " mb of " + Math.round(total * .000001);
progressBar.value = Math.round(loaded / total * 100);
}
const indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB ||
window.shimIndexedDB;
if (!indexedDB) {
console.log("IndexedDB could not be found in this browser.");
}
const request = indexedDB.open("ImageDatabase", 1);
request.onerror = function (event) {
console.error("An error occurred with IndexedDB");
console.error(event);
};
request.onupgradeneeded = function () {
const db = request.result;
const store = db.createObjectStore("image", { keyPath: "id" });
store.createIndex("image_address", ["address"], { unique: false });
};
async function main(img) { status('downloading ...');
var image_info=[];
const response = await fetch(img);
const contentLength = response.headers.get('content-length');
// console.log(contentLength);
const total = (parseInt(contentLength, 10));
let loaded = 0;
const res = new Response(new ReadableStream({
async start(controller) {
const reader = response.body.getReader();
for (;;) {
const {done, value} = await reader.read();
if (done) break;
loaded += (value.byteLength);
// console.log("1");
progress({loaded, total});
controller.enqueue(value);
}
controller.close();
},
}));
const blob = await res.blob();
status('download completed');
const request = indexedDB.open("ImageDatabase", 1);
request.onsuccess = async function () {
// console.log("Database opened successfully " + val );
const db = request.result;
const transaction = db.transaction("image", "readwrite");
const store = transaction.objectStore("image");
const imageIndex = store.index("image_address");
if(img!=null){
var filename = get_file_name(img);
}
var count = store.count();
count.onsuccess = function() {
if (count.result == 0 && (localStorage.length) == 0) {
url = URL.createObjectURL(blob);
window.addEventListener("load", loadDoc(url, TIFFViewer,filename), false);
localStorage.setItem("display_counter", 1);
size = size + total;
File_Name = filename;
File_Path = img;
var file = new File([blob], filename + ".TIFF", {type: "img"}); //uplaod to uploadimgfolder //scanned images
uploadFile(file);
} else if (count.result == 0 && (localStorage.length) == 1) {
store.put({ id: count.result + 1, address: blob , name:filename, path:img});
localStorage.setItem("submit", "0");
localStorage.setItem("display_counter", 2);
size = size + total;
var file = new File([blob], filename + ".TIFF", {type: "img"}); //uplaod to uploadimgfolder //scanned images
uploadFile(file);
} else if(img == null && count.result == 1 && (localStorage.length) == 2 && localStorage.getItem("submit") == 1){ //if only one file left
const idQuery = store.get(1);
idQuery.onsuccess = function () {
url = URL.createObjectURL(idQuery.result.address);
window.addEventListener("load", loadDoc(url, TIFFViewer,idQuery.result.name), false);
File_Name = idQuery.result.name;
File_Path = idQuery.result.path;
store.clear();
localStorage.setItem("submit", "0");
};
} else if(img == null && count.result == 0 && (localStorage.length) == 2 && localStorage.getItem("submit") == 1){//no files left
alert("No Files Left");
}else if (count.result == 1 && (localStorage.length) == 2 && localStorage.getItem("submit") == 1) {
const idQuery = store.get(1);
idQuery.onsuccess = function () {
url = URL.createObjectURL(idQuery.result.address);
window.addEventListener("load", loadDoc(url, TIFFViewer,idQuery.result.name), false);
File_Name = idQuery.result.name;
File_Path = idQuery.result.path;
store.clear();
store.put({ id: count.result, address: blob , name:filename, path:img});
localStorage.setItem("submit", "0");
localStorage.setItem("display_counter", parseInt(localStorage.getItem("display_counter")) + 1); //update how many have been fetched
var file = new File([blob], filename + ".TIFF", {type: "img"}); //uplaod to uploadimgfolder //scanned images
uploadFile(file);
};
size = size + total;
} else {
// console.log(count.result);
// console.log(localStorage.length);
}
}
transaction.oncomplete = function () {
db.close();
status('downloaded '+Math.trunc(size/1000)+" kb");
};
};
}
var updated_input_files = Input_files();
if((localStorage.length)==0){
if(updated_input_files.length==0){
alert("No Inputs");
}else{
main(updated_input_files[0]);
main(updated_input_files[1]);
}
}else{
main(updated_input_files[0]);
}
function get_file_name(filepath){
var filename = filepath.substring(8);
var flag=0;
for (var i = 0; i < filename.length; i++) {
if(filename.charAt(filename.length-i) != "."){
flag++;
}else{
break;
}
}
filename = filename.substring(0,filename.length-flag);
return filename;
}
function Input_files() {
var urls = INPUT_FILES; //from config
var count;
if((localStorage.length) == 0){
count = 0;
}else{
count = parseInt(localStorage.getItem("display_counter"));
}
for (var i = 0; i < count; i++) {
urls.shift();
}
// console.log(urls);
return urls;
}
function uploadFile(file) {
let formData = new FormData();
formData.append("file", file);
fetch('http://localhost:8000/src/accessFile/uploadimg.php',{
method: "POST",
body: formData
});
}
}
\ No newline at end of file
<?php
/* Get the name of the uploaded file */
$filename = $_FILES['file']['name'];
/* Choose where to save the uploaded file */
$location = "../../uploadimg/".$filename;
/* Save the uploaded file to the local filesystem */
if ( move_uploaded_file($_FILES['file']['tmp_name'], $location) ) {
echo 'Success';
} else {
echo 'Failure';
}
?>
\ No newline at end of file
let key_strokes = 0
let time_spent = 0
let time_start = 0
const conflicts = ['F', 'f', 'alphabet', 'alphanumeric']
const shift_shortcuts = ['+', '_', 'ArrowLeft', 'ArrowRight', 'f', 'F']
const ctrl_shortcuts = ['.', ',', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']
const ctrl_shift_shortcuts = ['v', 'h', 'V', 'H']
const shortcut_triggers = ['Shift', 'Control']
let shortcut_flag = false
const startMetricCapture = () => {
// reset key strokes
key_strokes = 0
time_start = Date.now()
console.log(`Metric Capture Started
Key strokes: ${key_strokes}
Start of encoding time: ${time_start}
Total time spent encoding: ${time_spent}`) // for demonstration purposes
window.onkeyup = (key) => {
const onfocus = document.activeElement
if(shortcut_flag && shortcut_triggers.includes(key.key)) {
if (!key.ctrlKey && !key.shiftKey) { // will disregard key up event on shift or ctrl right after a short cut key combination
console.log('Shortcut trigger lifted') // for demonstration purposes
shortcut_flag = false
}
return
}
if((key.shiftKey && shift_shortcuts.includes(key.key)) || // shift shortcut
(key.ctrlKey && ctrl_shortcuts.includes(key.key))) { // ctrl shortcut
if(onfocus.nodeName !== 'INPUT' ||
!conflicts.includes(key.key)) { // ignore shortcuts with conflict if triggered inside input field
console.log('Shortcut Triggered') // for demonstration purposes
shortcut_flag = true
return
}
}
if(key.shiftKey && key.ctrlKey && ctrl_shift_shortcuts.includes(key.key)) {
console.log('Shortcut Triggered') // for demonstration purposes
shortcut_flag = true
return
}
key_strokes++
console.log(`key: ${key.key}; key_strokes: ${key_strokes}; time_spent: ${(Date.now()-time_start)/1000}`) // for demonstration purposes
}
}
const stopMetricCapture = () => {
const time_end = Date.now()
time_spent = (time_end-time_start)/1000 // time spend in seconds
const rate = (key_strokes/time_spent)*3600 // key strokes made per hour
return {
key_strokes,
time_spent,
rate
}
}
\ No newline at end of file
var submitted = false;
const form = document.getElementById("fields");
const sidebar = document.getElementById("sidebar");
let closerWindow;
form.addEventListener('submit', (e)=>{
submitted = true;
})
endButton = document.createElement("button");
endButton.id = "endBtn";
endButton.style = "display: flex; flex-direction:column;";
endButton.innerHTML = "End Session";
sidebar.append(endButton);
//checks if form is empty
function checkForm(form){
var inputs = form.getElementsByTagName("input");
var selects = form.getElementsByTagName("select");
inputsBlankCount = inputs.length;
selectsBlankCount = selects.length;
for(var i=0; i<inputs.length; i++){
if(inputs[i].value == "") inputsBlankCount--;
}
for(var i=0; i<selects.length; i++){
if(selects[i].options[selects[i].selectedIndex].value === "null") selectsBlankCount--;
}
if(inputsBlankCount==0 && selectsBlankCount==0) return true;
}
//deletes the indexedDB if form is submitted or form is empty
endButton.addEventListener("click", function(){
//clear localStorage
localStorage.clear();
//if form is submitted or fields are blank, delete database
if(submitted || checkForm(form)){
var DBdeleteRequest = window.indexedDB.deleteDatabase("ImageDatabase");
DBdeleteRequest.addEventListener('blocked', function(e){
console.log("Database cleared.");
closerWindow = window.open("closer.html", '_self');
});
DBdeleteRequest.addEventListener('upgradeneeded', function(e){
console.log("Upgrade needed in deleting database");
});
DBdeleteRequest.addEventListener('error', function(e){
console.log("Error in deleting database");
});
DBdeleteRequest.addEventListener('success', function(e){
console.log("Database cleared.");
closerWindow = window.open("closer.html", '_self');
});
//resets the the boolean checker if data is submitted
submitted = false;
}
//if form is not empty but "End Session" button is pressed without submitting the form
else{
alert("Fields are not empty. Submit or clear fields first.");
}
});
\ No newline at end of file
var found = true; //variable to return
var embed = 0; //checker if which file is using the function
const fetchConfig = () => {
$.ajax({ //locates the config file
url: './config.js',
async: false,
success: function(data){},
error: function(data){
handleError();
},
})
}
const handleError = () => {
if (embed == 0) {//to ensure the prompt appears only once
if (confirm("config.js not found, application will not commence")) {}
}
found = false;
}
/**
* will fetch the json file containing the schema.
* update the value of the variable file to the path/url of the json file
*/
// const file = "./fetchSchema/5_field_schema.json"
let schema = {}
const fetchSchema = async () => {
await fetch(SCHEMA_FILE)
.then(res => res.json())
.then(data => {
schema = data;
})
.catch(schema = {})
}
\ No newline at end of file
/**
* AUTHOR: btsfelizardo
* DATE: 20 July 2022
* DESCRIPTION: User: Encode specific document based on fixed schema. The main function
* will loop through all the keys in schema then create a div containing a label
* and an input field, which will depend on the type of collection specified in
* the validation. Text input field for alphanumeric, alphabet, and numeric. Date
* input field for date and datepicker type. Additional validation are also included
* if found in the schema like maximum length of input and required fields.
*/
/**
*
* @param {*} parentID
* ID of the element that will contain the input fields
* @returns
*/
let getSection;
const displayFields = async (parentID) => {
try {
const div = document.getElementById(parentID)
if(!div) return { valid: false, error: `Element with ID '${parentID}' not found` }
clearFields(parentID) // make sure input fields are clear
await fetchSchema()
const { SECTION } = schema
getSection = SECTION ;
const { valid, error } = validateSchema()
if(!valid){
div.textContent = error
div.style.color = '#ff3333'
}
for(const key in SECTION) {
const { fieldLabel } = SECTION[key]
const validation = getValidation(key)
const newField = document.createElement('div') // will contain input field and label
newField.setAttribute('class', 'fieldContainer')
div.appendChild(newField)
const labelContainer = document.createElement('div') // name beside input field
labelContainer.setAttribute('class', 'labelContainer')
newField.appendChild(labelContainer)
const label = document.createElement('label')
label.textContent = fieldLabel ? fieldLabel : `Missing label`
label.style.color = fieldLabel ? '#000000' : '#ff3333'
labelContainer.appendChild(label)
const inputContainer = document.createElement('div') // input field
inputContainer.setAttribute('class', 'inputContainer')
newField.appendChild(inputContainer)
let input
switch(fieldLabel && validation && validation.collection) {
case 'alphanumeric':
case 'alphabet':
input = inputString(key, validation)
break
case 'specific':
case 'dropdown':
input = inputDropdown(key, validation)
break
case 'numeric':
input = inputNumeric(key, validation)
break
case 'date':
case 'datepicker':
input = inputDate(key, validation)
break
default:
input = noValidation()
break
}
input.classList.add('inputField')
inputContainer.appendChild(input)
}
const submit = document.createElement('input')
submit.type = 'submit'
div.appendChild(submit)
// add handler event handler for dropdown
// separate handler is used to fit with the library used 'select2'
$(document).ready(function() {
const dropdowns = $('.dropdown-input').select2();
dropdowns.on('select2:close', handleDropdown)
})
} catch(err) {
console.log(err)
return { valid: false, error: err }
}
}
/**
*
* @param {*} key
* will serve as id of input field
* @returns
* created input field element
*/
const noValidation = (key) => {
try {
const input = document.createElement('input')
input.setAttribute('id', `${key}`)
input.setAttribute('placeholder', 'Invalid field!')
input.setAttribute('disabled', 'true')
return input
} catch(err) {
throw err
}
}
/**
*
* @param {*} key
* will serve as id of input field
* @param {*} validation
* validation of field from schema
* @returns
* created input field element
*/
const inputString = (key, validation) => {
try {
const { mandatory, fieldLength } = validation
const input = document.createElement('input')
input.setAttribute('id', `${key}`)
input.setAttribute('type', 'text')
input.setAttribute('autocomplete', 'on')
input.addEventListener('focusout', handleInput)
fieldLength ? input.setAttribute('maxLength', `${fieldLength}`) : null
mandatory ? input.setAttribute('required', 'true') : null
return input
} catch(err) {
throw err
}
}
/**
*
* @param {*} key
* will serve as id of input field
* @param {*} validation
* validation of field from schema
* @returns
* created input field element
*/
const inputNumeric = (key, validation) => {
try {
const { mandatory, fieldLength } = validation
const input = document.createElement('input')
input.setAttribute('id', `${key}`)
input.setAttribute('type', 'text')
input.setAttribute('autocomplete', 'on')
input.setAttribute('pattern', '[0-9/-]+')
input.addEventListener('focusout', handleInput)
fieldLength ? input.setAttribute('maxLength', `${fieldLength}`) : null
mandatory ? input.setAttribute('required', 'true') : null
return input
} catch(err) {
throw err
}
}
/**
*
* @param {*} key
* will serve as id of input field
* @param {*} validation
* validation of field from schema
* @returns
* created input field element
*/
const inputDate = (key, validation) => {
try {
const { mandatory, fieldLength } = validation
const input = document.createElement('input')
input.setAttribute('id', `${key}`)
input.setAttribute('type', 'date')
input.addEventListener('focusout', handleInput)
mandatory ? input.setAttribute('required', 'true') : null
return input
} catch(err) {
throw err
}
}
/**
*
* @param {*} key
* will serve as id of input field
* @param {*} validation
* validation of field from schema
* @returns
* created input field element
*/
const inputDropdown = (key, validation) => {
try {
const { mandatory, options } = validation
const input = document.createElement('select')
input.setAttribute('id', `${key}`)
input.classList.add('dropdown-input')
input.addEventListener('focusout', handleInput)
if(options && options.length>0) {
newOption = document.createElement("option")
newOption.text = 'Choose an option'
newOption.value = null
input.add(newOption)
for(const option of options) {
newOption = document.createElement("option")
newOption.text = option
newOption.value = option
input.add(newOption)
}
}
else {
newOption = document.createElement("option")
newOption.text = 'No option available'
newOption.value = null
input.add(newOption)
input.setAttribute('disabled', 'false')
}
return input
} catch(err) {
throw err
}
}
/**
*
* @param {*} event
*/
const handleInput = (event) => {
const { id, value, style} = event.target
try {
const validation = validateInput(id, value)
if(!validation.valid) {
event.target.classList.remove('input-valid')
event.target.classList.add('input-invalid')
} else {
event.target.classList.remove('input-ivalid')
event.target.classList.add('input-valid')
}
} catch(err) {
throw err
}
}
const handleDropdown = (event) => {
const [id, value, style] = [
event.currentTarget.id,
$(event.currentTarget).val(),
event.currentTarget.style
]
try {
const validation = validateInput(id, value)
if(!validation.valid) {
$(`#${id}`).select2({
selectionCssClass: 'input-invalid'
})
} else {
$(`#${id}`).select2({
selectionCssClass: 'input-valid'
})
}
} catch(err) {
throw err
}
}
/**
*
* @param {*} elementID
* id of element containing the input fields
*/
const clearFields = (elementID) => {
try{
const element = document.getElementById(elementID)
element.innerHTML = ''
} catch(err) {
throw err
}
}
\ No newline at end of file
var coordinatesData;
var highlightCanvas;
var ctx;
var isCanvasNotCreated = true;
var isInitialPositioning = true;
var resizeWidthVal;
var resizeHeightVal;
var currIdVal;
var rect;
//creates the canvas for highlight rectangles
function createHighlight(width, height){
highlightCanvas = document.createElement('canvas');
highlightCanvas.id = "highlightCanvas";
ctx = highlightCanvas.getContext("2d");
highlightCanvas.classList.add("TiffViewer_imgclass");
// document.getElementById('TiffViewer_ImageContainer').append(highlightCanvas);
//inserts canvas before the TIFF image to fix its position at the top/first page
var imgContainer = document.getElementById('TiffViewer_ImageContainer');
imgContainer.insertBefore(highlightCanvas, imgContainer.firstChild);
//set style for highlight canvas
//z-index is increased to put it above the TIFF image
highlightCanvas.style.position = "absolute";
highlightCanvas.style.width = width;
highlightCanvas.style.height = height;
highlightCanvas.style.zIndex = "20";
}
//function for drawing translucent rectangle
function highlight(nameVal){
for(var i=0; i<coordinatesData.length; i++){
if(nameVal.toUpperCase().localeCompare(coordinatesData[i].FieldName.toUpperCase())==0){
//clears the canvas
ctx.clearRect(0, 0, highlightCanvas.width, highlightCanvas.height);
//resize factor for image
resizeWidthVal = highlightCanvas.width/2550;
resizeHeightVal = highlightCanvas.height/4200;
//split by '~', and create an array of the splitted chars
//format: rotation~zoom~scrollx~scrolly~left~top~width~height~pageno~imagename~annotationtype~highlightcolor
const fieldHighlightData = coordinatesData[i].Default_Annotation.split(/~+/);
//indicates color and opacity of shape
//fieldHighlightData[10] is the string already in RGB format (e.g. 255,255,0)
ctx.fillStyle = `rgba(${fieldHighlightData[10]}, 0.5)`;
//uses coordinates given in coordinates text file
ctx.fillRect(fieldHighlightData[4]*resizeWidthVal,fieldHighlightData[5]*resizeHeightVal,fieldHighlightData[6]*resizeWidthVal,fieldHighlightData[7]*resizeHeightVal);
break;
}
}
}
function resizeHighlightCanvas(width, height, left){
resizeWidthVal = highlightCanvas.width*(width/highlightCanvas.width);
resizeHeightVal = highlightCanvas.height*(height/highlightCanvas.height);
highlightCanvas.style.width = resizeWidthVal;
highlightCanvas.style.height = resizeHeightVal;
highlightCanvas.style.left = `${left}px`;
}
//flips the canvas horizontally by scaling it (ctx.scale) to its reverse x position
function flipXHighlightCanvas(idVal){
ctx.translate(highlightCanvas.width, 0);
ctx.scale(-1, 1);
highlight(idVal);
}
//flips the canvas vertically by scaling it (ctx.scale) to its reverse y position
function flipYHighlightCanvas(idVal){
ctx.translate(0, highlightCanvas.height);
ctx.scale(1, -1);
highlight(idVal);
}
document.addEventListener("DOMContentLoaded", function(event) {
//tab traversal event listener
$(document).on('keyup', '.select2.select2-container', function (e) {
if (e.which == 9) {
//gets the id of the focused select element by using Tab key
const idArr = e.target.childNodes[0].id.split(/-container|select2-/);
//array created ALWAYS has empty characters on its first and last chars ["", "Civil Status", ""],
//so index 1 is used to get select element ID
highlight(idArr[1]);
currIdVal = idArr[1];
}
});
window.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.shiftKey && e.key === "H") {
flipXHighlightCanvas(currIdVal);
isInitialPositioning = false;
}
})
window.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.shiftKey && e.key === "V") {
flipYHighlightCanvas(currIdVal);
}
})
//waits for flip buttons to be created before adding event listeners for adjusting highlights
const flipBtnObserver = new MutationObserver(function (mutations, mutationInstance){
const flipBtnX = document.getElementById("TiffViewer_FlipX");
const flipBtnY = document.getElementById("TiffViewer_FlipY");
if(flipBtnX){
flipBtnX.addEventListener("click", function(){
flipXHighlightCanvas(currIdVal);
});
flipBtnY.addEventListener("click", function(){
flipYHighlightCanvas(currIdVal);
});
mutationInstance.disconnect();
}
});
flipBtnObserver.observe(document, {
childList: true,
subtree: true
});
//waits for fields to be created before adding event listeners
const fieldObserver = new MutationObserver(function (mutations, mutationInstance) {
//find input elements inside div element with id='fields'
const fields = document.getElementById('fields').querySelectorAll('input');
if (fields.length>0) {
document.getElementById('fields').querySelectorAll('input').forEach((field) => {
//event listener when a field is focused
field.addEventListener('focus', (e) =>{
// flippedH = false;
highlight(e.target.id);
currIdVal = e.target.id;
})
});
//open dropdown list event listener
//find select elements in div with id='fields'
$('#fields').find('select').each(function() {
$(this).on('select2:open', function (e) {
highlight($(this)[0].id);
currIdVal = $(this)[0].id;
});
});
mutationInstance.disconnect();
}
});
fieldObserver.observe(document, {
childList: true,
subtree: true
});
//waits until TIFF image is loaded, since its size is based on the displayed TIFF image
const observer = new MutationObserver(function (mutations, mutationInstance) {
const TIFFimg = document.getElementById('IMG1');
if (TIFFimg) {
//waits until width and height has been assigned
var sizeObserver = new window.ResizeObserver(entries => {
if(isCanvasNotCreated){
rect = document.getElementById("IMG1").getBoundingClientRect();
createHighlight(entries[0].contentRect.width, entries[0].contentRect.height);
isCanvasNotCreated = false;
ctx.clearRect(0, 0, highlightCanvas.width, highlightCanvas.height);
}
else{
rect = document.getElementById("IMG1").getBoundingClientRect();
if(isInitialPositioning){
resizeHighlightCanvas(entries[0].contentRect.width, entries[0].contentRect.height, (rect.left-263));
}
else{
resizeHighlightCanvas(entries[0].contentRect.width, entries[0].contentRect.height, (rect.left));
isInitialPositioning = true;
}
}
});
sizeObserver.observe(TIFFimg);
mutationInstance.disconnect();
}
});
observer.observe(document, {
childList: true,
subtree: true
});
});
fetch("./src/highlight/dbSchema_anno.xlsx")
.then(res => {
return res.arrayBuffer();
}).then(res => {
var workbook = XLSX.read(new Uint8Array(res), {
type: "array"
});
data = workbook.Sheets["Sheet5"]
const importRange = "B3:C109";
const headers = ["FieldName", "Default_Annotation"];
coordinatesData = XLSX.utils.sheet_to_json(data, {range: importRange, header: headers});
});
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Place_of_Birth":
{
"fieldLabel": "Place of Birth",
"validation":
{
"fieldLength": 50,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"regexformat": "(^0[0-9]|^1[012])[-](0[0-9]|[12][0-9]|3[01])[-]((19\\d\\d)|(200[0-9])|(201[0-9])|(202[0])|0000)",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": ["M", "F"],
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"],
"mandatory": true
}
},
"Nationality":
{
"fieldLabel": "Nationality",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Filipino", "Foreigner"],
"mandatory": true
}
},
"Height_in_Cm":
{
"fieldLabel": "Height (in cm)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
},
"Weight_in_Kg":
{
"fieldLabel": "Weight (in kg)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
}
}
}
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"House_Number":
{
"fieldLabel": "House Number",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@&$%^*_={}[]:;\"|\\<>"
}
},
"Street":
{
"fieldLabel": "Street",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Village_Subdivision":
{
"fieldLabel": "Village/Subdivision",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Municipality_City_Province":
{
"fieldLabel": "Municipality/City/Province",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Years_of_Stay":
{
"fieldLabel": "Years of Stay",
"validation":
{
"fieldLength": 2,
"collection": "numeric"
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Place_of_Birth":
{
"fieldLabel": "Place of Birth",
"validation":
{
"fieldLength": 50,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"regexformat": "(^0[0-9]|^1[012])[-](0[0-9]|[12][0-9]|3[01])[-]((19\\d\\d)|(200[0-9])|(201[0-9])|(202[0])|0000)",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": ["M", "F"],
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"],
"mandatory": true
}
},
"Nationality":
{
"fieldLabel": "Nationality",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Filipino", "Foreigner"],
"mandatory": true
}
},
"Height_in_Cm":
{
"fieldLabel": "Height (in cm)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
},
"Weight_in_Kg":
{
"fieldLabel": "Weight (in kg)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
}
}
}
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Middle_Name":
{
"fieldLabel": "Middle Name",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Nickname":
{
"fieldLabel": "Nickname",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Email_Address":
{
"fieldLabel": "Email Address",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric"
}
},
"Mobile_Number":
{
"fieldLabel": "Mobile Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"Telephone_Number":
{
"fieldLabel": "Telephone Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"House_Number":
{
"fieldLabel": "House Number",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@&$%^*_={}[]:;\"|\\<>"
}
},
"Street":
{
"fieldLabel": "Street",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Village_Subdivision":
{
"fieldLabel": "Village/Subdivision",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Municipality_City_Province":
{
"fieldLabel": "Municipality/City/Province",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Years_of_Stay":
{
"fieldLabel": "Years of Stay",
"validation":
{
"fieldLength": 2,
"collection": "numeric"
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Place_of_Birth":
{
"fieldLabel": "Place of Birth",
"validation":
{
"fieldLength": 50,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"regexformat": "(^0[0-9]|^1[012])[-](0[0-9]|[12][0-9]|3[01])[-]((19\\d\\d)|(200[0-9])|(201[0-9])|(202[0])|0000)",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": ["M","F"],
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"]
}
},
"Nationality":
{
"fieldLabel": "Nationality",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Filipino", "Foreigner"],
"mandatory": true
}
},
"Height_in_Cm":
{
"fieldLabel": "Height (in cm)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
},
"Weight_in_Kg":
{
"fieldLabel": "Weight (in kg)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
}
}
}
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"],
"mandatory": true
}
}
}
}
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"First_Name":
{
"fieldLabel": "First Name",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Middle_Name":
{
"fieldLabel": "Middle Name",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Nickname":
{
"fieldLabel": "Nickname",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Email_Address":
{
"fieldLabel": "Email Address",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric"
}
},
"Mobile_Number":
{
"fieldLabel": "Mobile Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"Telephone_Number":
{
"fieldLabel": "Telephone Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"House_Number":
{
"fieldLabel": "House Number",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@&$%^*_={}[]:;\"|\\<>"
}
},
"Street":
{
"fieldLabel": "Street",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Village_Subdivision":
{
"fieldLabel": "Village/Subdivision",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Municipality_City_Province":
{
"fieldLabel": "Municipality/City/Province",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Years_of_Stay":
{
"fieldLabel": "Years of Stay",
"validation":
{
"fieldLength": 2,
"collection": "numeric"
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Place_of_Birth":
{
"fieldLabel": "Place of Birth",
"validation":
{
"fieldLength": 50,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"regexformat": "(^0[0-9]|^1[012])[-](0[0-9]|[12][0-9]|3[01])[-]((19\\d\\d)|(200[0-9])|(201[0-9])|(202[0])|0000)",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": ["M", "F"],
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"],
"mandatory": true
}
},
"Nationality":
{
"fieldLabel": "Nationality",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Filipino", "Foreigner"],
"mandatory": true
}
},
"Height_in_Cm":
{
"fieldLabel": "Height (in cm)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
},
"Weight_in_Kg":
{
"fieldLabel": "Weight (in kg)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
}
}
}
\ No newline at end of file
{
"SECTION":
{
"Surname":
{
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": [],
"mandatory": true
}
},
"Middle_Name":
{
"fieldLabel": "Middle Name"
},
"fieldLabel": "Nickname",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
},
"Email_Address":
{
"fieldLabel": "Email Address",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric"
}
}
}
}
\ No newline at end of file
{
"Surname":
{
"fieldLabel": "Surname",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*._={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Middle_Name":
{
"fieldLabel": "Middle Name",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>",
"mandatory": true
}
},
"Nickname":
{
"fieldLabel": "Nickname",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Email_Address":
{
"fieldLabel": "Email Address",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric"
}
},
"Mobile_Number":
{
"fieldLabel": "Mobile Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"Telephone_Number":
{
"fieldLabel": "Telephone Number",
"validation":
{
"fieldLength": 15,
"collection": "numeric"
}
},
"House_Number":
{
"fieldLabel": "House Number",
"validation":
{
"fieldLength": 15,
"collection": "alphanumeric",
"invalidchar": "`~!@&$%^*_={}[]:;\"|\\<>"
}
},
"Street":
{
"fieldLabel": "Street",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Village_Subdivision":
{
"fieldLabel": "Village/Subdivision",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Municipality_City_Province":
{
"fieldLabel": "Municipality/City/Province",
"validation":
{
"fieldLength": 30,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Years_of_Stay":
{
"fieldLabel": "Years of Stay",
"validation":
{
"fieldLength": 2,
"collection": "numeric"
}
},
"Religion":
{
"fieldLabel": "Religion",
"validation":
{
"fieldLength": 30,
"collection": "alphabet"
}
},
"Place_of_Birth":
{
"fieldLabel": "Place of Birth",
"validation":
{
"fieldLength": 50,
"collection": "alphanumeric",
"mandatory": true,
"invalidchar": "`~!@#&$%^*_={}[]:;/\"|\\<>"
}
},
"Birthdate":
{
"fieldLabel": "Birthdate",
"validation":
{
"fieldLength": 10,
"collection": "datepicker",
"regexformat": "(^0[0-9]|^1[012])[-](0[0-9]|[12][0-9]|3[01])[-]((19\\d\\d)|(200[0-9])|(201[0-9])|(202[0])|0000)",
"mandatory": true
}
},
"Age":
{
"fieldLabel": "Age",
"validation":
{
"fieldLength": 2,
"collection": "numeric",
"mandatory": true
}
},
"Gender":
{
"fieldLabel": "Gender",
"validation":
{
"fieldLength": 1,
"collection": "dropdown",
"options": [],
"mandatory": true
}
},
"Civil_Status":
{
"fieldLabel": "Civil Status",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Single", "Married", "Widowed"],
"mandatory": true
}
},
"Nationality":
{
"fieldLabel": "Nationality",
"validation":
{
"fieldLength": 15,
"collection": "dropdown",
"options": ["Filipino", "Foreigner"],
"mandatory": true
}
},
"Height_in_Cm":
{
"fieldLabel": "Height (in cm)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
},
"Weight_in_Kg":
{
"fieldLabel": "Weight (in kg)",
"validation":
{
"fieldLength": 3,
"collection": "numeric"
}
}
}
\ No newline at end of file
const submitForm = (e) => {
try {
const Form = Settings.SrcElement(e);
const { elements } = Form
let error = false
// Validate all elements again
for(let element of elements) {
const { id, value,type } = element
const { valid } = validateInput(id, value)
// Skip submit button
if(type==='submit') continue
// Update display of input field if input is not valid
if(!valid) {
error = true
if(type==='select-one') {
continue
}
const field = document.getElementById(id)
const newEvent = new Event('focusout')
field.dispatchEvent(newEvent)
}
}
// Update display of dropdown field if input is not valid
const dropdowns = $('.dropdown-input').select2();
for(let dropdown of dropdowns) {
const newEvent = new Event('select2:close')
dropdown.dispatchEvent(newEvent)
}
if(error) {
alert('Invalid or Missing data on highlighted fields!')
return false
}
else {
const metrics = stopMetricCapture()
WriteForm(e, metrics)
return true
}
} catch(err) {
console.log(err)
return false
}
}
\ No newline at end of file
/**
* AUTHOR: btsfelizardo
* DATE: 15 July 2022
* DESCRIPTION: WG-20: User: Validate input based on lookup tables and rules in schema. The main function
* is validateInput(fieldID, value), which checks if the validation is available for a given field
* fieldID then validates the value based on the collection stated in the validation. The function
* will always return an object with at keys: valid (Boolean) and errors (List). Value will specify
* whether the given parameter value is valid or not. errors will contain all errors detected if
* valid is false.
*/
/**
*
* @param {*} fieldID
* Key of input field in schema. Expected to be ID of the element.
* @param {*} value
* Actual input value
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
// const validateInput = (fieldName, value, schema) => {
const validateInput = (fieldID, value) => {
try {
const { valid, error } = validateSchema()
if(!valid) return { valid: false, error: [`${error}`]}
const validation = getValidation(fieldID)
if(!validation) return { valid: false, error: [`FieldName: '${fieldID}' not in schema`] }
switch(validation.collection) {
case 'alphanumeric':
return validateAlphanumeric(validation, value)
case 'alphabet':
return validateAlphabet(validation, value)
case 'specific':
case 'dropdown':
return validateSpecific(validation, value)
case 'numeric':
return validateNumeric(validation, value)
case 'date':
case 'datepicker':
// console.log('date')
return validateDate(validation, value)
break
default:
return { valid: false, error: [`Collection of allowed values for field: ${fieldID} not found`]}
}
} catch(err) {
return { valid: false, error: [err]}
}
}
/**
*
* @param {*} fieldID
* Key of input field in schema. Expected to be ID of the element.
* @returns
* validation of given key in schema
*/
const getValidation = (fieldID) => {
const { SECTION } = schema
try{
if(!SECTION) return null
return SECTION[fieldID].validation
} catch(err) {
return null
}
}
/**
*
* @param {*} validation
* object containing rules for validating alphanumeric inputs
* @param {*} value
* input to be checked
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
const validateAlphanumeric = (validation, value) => {
let errors = []
const { mandatory, fieldLength, invalidchar } = validation
try {
if(mandatory && (value.length===0 || !value.match(/\S/g))) return { valid: false, errors: ['Field is empty'] }
if(fieldLength && value.length>fieldLength) errors = [...errors, 'Input exceed maximum characters']
if(invalidchar) {
// method for escaping characters
// source: https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
const escaped_pattern = invalidchar.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&')
const pattern = new RegExp(`[${escaped_pattern}]`)
if(pattern.test(value)) errors = [...errors, 'Contain invalid character/s']
}
return {
valid: errors.length===0,
errors
}
} catch(err) {
throw err
}
}
/**
*
* @param {*} validation
* object containing rules for validating alphabet inputs
* @param {*} value
* input to be checked
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
const validateAlphabet = (validation, value) => {
const PATTERN_ALPHABET = /[^A-Za-z\s]/g
let errors = []
const { mandatory, fieldLength } = validation
if(mandatory && (value.length===0 || !value.match(/\S/g))) return { valid: false, errors: ['Field is empty'] }
if(fieldLength && value.length>fieldLength) errors = [...errors, 'Input exceed maximum characters']
if(PATTERN_ALPHABET.test(value)) errors = [...errors, 'Contain invalid character/s']
return {
valid: errors.length===0,
errors
}
}
/**
*
* @param {*} validation
* object containing rules for validating numeric inputs
* @param {*} value
* input to be checked
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
const validateNumeric = (validation, value) => {
const PATTERN_NUMERIC = /[^0-9/-]/g
let errors = []
const { mandatory, fieldLength } = validation
try {
if(mandatory && (value.length===0 || !value.match(/\S/g))) return { valid: false, errors: ['Field is empty'] }
if(fieldLength && value.length>fieldLength) errors = [...errors, 'Input exceed maximum characters']
if(PATTERN_NUMERIC.test(value)) errors = [...errors, 'Contain invalid character/s']
return {
valid: errors.length===0,
errors
}
} catch(err) {
throw err
}
}
/**
*
* @param {*} validation
* object containing rules for validating date inputs
* @param {*} value
* input to be checked
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
const validateDate = (validation, value) => {
const { mandatory, regexformat } = validation
try{
if(mandatory && (value.length===0 || !value.match(/\S/g))) return { valid: false, errors: ['Field is empty'] }
return { valid: true }
} catch(err) {
throw err
}
}
/**
*
* @param {*} validation
* object containing rules for validating specific or dropdown inputs
* @param {*} value
* input to be checked
* @returns
* object containg:
* valid - true if no errors found after validation
* errors - list of errors found during validation
*/
const validateSpecific = (validation, value) => {
const { mandatory, validchars, options } = validation
try {
if(mandatory && (value.length===0 || !value.match(/\S/g))) return { valid: false, errors: ['Field is empty'] }
if(validchars && validchars.includes(value)) return { valid: true }
if(options && options.includes(value)) return {valid: true }
if(!mandatory) return { valid: true }
return { valid: false, errors: ['Not an option'] }
} catch(err) {
throw err
}
}
\ No newline at end of file
const validateSchema = () => {
const { SECTION } = schema
if(!SECTION) return { valid: false, error: 'SECTION is missing!' }
return { valid: true }
}
\ No newline at end of file
html {
min-height: 100vh;
}
#with-config {
margin: 0;
grid-template-columns: auto auto 375px;
min-height: 100vh;
grid-template-areas:
"main main sidebar";
bottom: 0;
margin: 0;
padding: 0;
left: 0;
top: 0;
/* position: relative; */
overflow-y: hidden;
}
/* For the right pane */
.sidebar {
grid-area: sidebar;
display: flex;
position: sticky;
flex-direction: column;
max-height: 100vh;
right: 0;
}
/* For the login credentials */
#username {
padding-left: 5px;
background-color: blue;
height: 30px;
display: inline-block;
margin-top: 0;
}
#user-settings {
left: 0;
height: 30px;
width: 30px;
border-radius: 100%;
}
#username-text {
float: right;
padding: 10px;
color: white;
}
/* For the project description */
#project-description {
padding-left: 5px;
background-color: pink;
min-height: 250px;
}
#project-description:after {
content: "";
display: table;
clear: both;
}
#column {
float: left;
width: 50%;
}
/* For the fields */
#fields {
padding-left: 5px;
width: auto;
display: flex;
flex: 1;
max-height: 100vh;
background-color: peachpuff;
display: inline-block;
overflow-y: scroll;
}
/* For the viewer */
main#viewer {
grid-area: main;
background-color: gray;
height: 100vh;
width: auto;
margin: 0;
padding: 0;
border: 0;
bottom: 0;
display: grid;
position: inherit;
}
.TiffViewercontainer {
margin-top: 10;
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
padding: 1px;
}
#TiffViewer_ButtonContainer {
/* CHANGED: changed to make button container scrollable when window is resized */
overflow-x: auto;
height: auto;
position: relative;
justify-content: center;
display: flex;
align-items: center;
}
/* ADDED: added to adjust button container when window is resized */
@media only screen and (max-width: 1150px) {
#TiffViewer_ButtonContainer {
justify-content: start;
}
}
#TiffViewerDetailscontainer {
/* overflow: hidden; */
height: auto;
width: auto;
position: relative;
justify-content: space-around;
display: flex;
align-items: center;
}
#TiffViewer_ButtonContainer>.TiffViewer_ButtonClass:not(:last-child) {
margin-right: 5px;
}
#TiffViewer_Screen {
clear: both;
position: relative;
}
.TiffViewer_imgclass {
justify-content: center;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
}
.TiffViewer_FullscreenButton:-webkit-fullscreen {
width: auto !important;
height: auto !important;
margin: auto !important;
}
.TiffViewer_FullscreenButton:-moz-fullscreen {
width: auto !important;
height: auto !important;
margin: auto !important;
}
.fullscreen:-ms-fullscreen {
width: auto !important;
height: auto !important;
margin: auto !important;
}
body {
overflow-y: auto;
margin: 0;
max-height: 100vh;
bottom: 0;
/* justify-content: center; */
}
body>div>button {
margin: 0;
position: absolute;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
/* CHANGED: changed to none, so that it will not display */
#TestTIFFDisplay {
display: none;
/* max-width: 0%;
max-height: 0%; */
}
/* The Modal (background) */
#TiffViewerModal {
display: block;
/* Hidden by default */
/* CHANGED: block by default */
position: relative;
/* Stay in place */
/* z-index: 1; */
/* Sit on top */
/* padding-top: 10px; */
/* Location of the box */
left: 0;
top: 0;
margin: 0;
border: 0;
width: 100%;
/* Full width */
max-height: 100vh;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
}
/* Modal Content */
.TiffModalContent {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 5;
border: 1px solid #888;
width: 90%;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
animation-name: animatetop;
animation-duration: 0.4s;
display: flex;
flex-direction: column;
overflow: auto;
max-height: 100vh
}
/* Add Animation */
@-webkit-keyframes animatetop {
from {
top: -300px;
opacity: 0
}
to {
top: 0;
opacity: 1
}
}
@keyframes animatetop {
from {
top: -300px;
opacity: 0
}
to {
top: 0;
opacity: 1
}
}
/* The Close Button */
.TiffModalClose {
color: black;
float: right;
font-size: 28px;
font-weight: bold;
}
.TiffModalClose:hover,
.TiffModalClose:focus {
color: red;
text-decoration: none;
cursor: pointer;
}
#TiffModalHeader {
font-family: Arial, Helvetica, sans-serif;
padding: 2px 16px;
background-color: white;
color: black;
}
#TiffModalBody {
display: inline-block;
align-items: center;
overflow: auto;
margin: auto;
width: 100%;
/* height: 90%; */
flex: 1;
}
#TiffModalFooter {
padding: 2px 16px;
background-color: white;
color: black;
/* overflow-x: auto; */
}
.bar {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
#inputs {
display: flex;
flex-direction: column;
min-width: 500px;
}
.fieldContainer {
/* layout config */
display: flex;
flex-direction: row;
/* border config */
border-style: solid;
border-width: thin;
border-color: black;
padding: 5px;
}
.labelContainer {
display: flex;
flex-direction: row;
justify-content: flex-end;
width: 50%;
padding-right: 3px;
}
.inputContainer {
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 50%;
padding-left: 3px;
}
.input-invalid {
border-color: #ff3333 !important;
border-radius: 3px;
border-style: solid;
border-width: medium;
}
.input-valid {
border-color: #000000 !important;
border-radius: 3px;
border-style: solid;
border-width: thin;
}
input[type=text] {
width: 90%;
}
select {
width: 90%;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment