FreeDatas2HTML/src/freeCSV2HTML.ts

237 lines
9.3 KiB
TypeScript
Raw Normal View History

const Papa = require("papaparse");
import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces";
2021-08-05 18:24:37 +02:00
const errors=require("./errors.js");
interface domElement
2021-08-05 18:24:37 +02:00
{
id: string;
2021-08-05 18:24:37 +02:00
eltDOM?: HTMLElement;
}
interface selectors extends domElement
{
colCSV: number;
name?: string;
values? : string[];
}
2021-08-05 18:24:37 +02:00
export class freeCSV2HTML
{
// Configuration :
private _datasViewElt: domElement = { id:"", eltDOM:undefined };
private _datasSourceUrl: string = "";
2021-08-05 18:24:37 +02:00
private _datasSelectors: selectors[] = [];
public parseMeta: papaParseMeta|undefined = undefined;
public parseDatas: papaParseDatas[] = [];
public parseErrors: papaParseErrors[] = [];
private datasHTML: string = "";
2021-08-05 18:24:37 +02:00
set datasViewElt(elt: domElement)
2021-08-05 18:24:37 +02:00
{
let checkContainerExist=document.getElementById(elt.id);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+elt.id);
else
{
this._datasViewElt.id=elt.id;
this._datasViewElt.eltDOM=checkContainerExist;
}
2021-08-05 18:24:37 +02:00
}
// Pas plus de tests + poussés, car nombreux protocoles possibles pour l'url du fichier. À revoir.
set datasSourceUrl(url: string)
2021-08-05 18:24:37 +02:00
{
if(url.trim().length === 0)
throw new Error(errors.needUrl);
else
this._datasSourceUrl=url.trim();
2021-08-05 18:24:37 +02:00
}
set datasSelectors(selectionElts: selectors[])
2021-08-05 18:24:37 +02:00
{
let checkContainerExist: HTMLElement|null;
for(let i = 0; i < selectionElts.length; i++)
{
checkContainerExist=document.getElementById(selectionElts[i].id);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+selectionElts[i].id);
else if(Number.isInteger( selectionElts[i].colCSV) === false || selectionElts[i].colCSV < 0)
throw new Error(errors.needNaturalNumber); // La validité réelle du numéro de colonne ne peut être vérifée qu'après le parsage.
else
selectionElts[i].eltDOM=checkContainerExist;
}
this._datasSelectors= selectionElts;
2021-08-05 18:24:37 +02:00
}
public async parse(): Promise<any>
{
const converter=this;
return new Promise((resolve,reject) =>
{
if(converter._datasSourceUrl !== "" )
{
Papa.parse(converter._datasSourceUrl,
{
quoteChar: '"',
header: true,
complete: function(results :any)
{
converter.parseMeta=results.meta;
converter.parseErrors=results.errors; // prévoir option bloquant la suite à la moindre erreur trouvée ?
converter.parseDatas=results.data;
resolve(true);
},
error:function(error :any)
{
reject(new Error(errors.parserFail));
},
download: true,
skipEmptyLines: true,
});
}
else
reject(new Error(errors.needUrl));
});
}
public async run(): Promise<any>
{
if (this._datasViewElt.eltDOM === undefined)
throw new Error(errors.needDatasElt);
else if(this._datasSourceUrl === "" )
throw new Error(errors.needUrl);
await this.parse();
if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined)
{
this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound;
return false;
}
else
{
let converter=this; // pour les situations ambigues
// Affichage initial des données du fichier
this.datasHTML=this.createDatasHTML(this.parseMeta.fields, this.parseDatas);
this._datasViewElt.eltDOM.innerHTML=this.datasHTML;
// Si demandé, création des listes permettant de filter les données
if(this._datasSelectors.length > 0)
{
// Les colonnes devant servir de filtre existent-elles dans le fichier ?
let selectorsHTML : string [] = [];
for(let i in this._datasSelectors)
{
if(this._datasSelectors[i].colCSV > (this.parseMeta.fields.length-1))
throw new Error(errors.selectorFieldNotFound);
else
{
let values=[], colName=this.parseMeta.fields[this._datasSelectors[i].colCSV];
for (let row in this.parseDatas)
{
if(values.indexOf(this.parseDatas[row][colName]) === -1)
values.push(this.parseDatas[row][colName]);
}
if(values.length > 0)
{
values.sort(); // revoir le problème des chiffres, dates, etc. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
this._datasSelectors[i].name = colName;
this._datasSelectors[i].values=values;
selectorsHTML[i]="<label for='CSVDatasSelector"+i+"'>"+colName+" : </label><select name='CSVDatasSelector"+i+"' id='CSVDatasSelector"+i+"'><option value='0'>----</option>";
for(let j in values)
selectorsHTML[i]+="<option value='"+(Number(j)+1)+"'>"+values[j]+"</option>";
selectorsHTML[i]+="</select>";
this._datasSelectors[i].eltDOM!.innerHTML=selectorsHTML[i];// je force avec "!", car l'existence de eltDOM a été testée par le setter.
// On rechargera les données lors de chaque choix de l'utilisateur
let selectElement = document.getElementById("CSVDatasSelector"+i) as HTMLInputElement;
selectElement.addEventListener('change', function(e)
{
converter.datasHTML=converter.createDatasHTML(converter.parseMeta!.fields as string[], converter.parseDatas);
converter._datasViewElt.eltDOM!.innerHTML=converter.datasHTML;
});
}
}
}
}
return true;
}
}
private createDatasHTML(fields: string[], datas: any[]) : string
{
// Je vérifie si des valeurs ont été sélectionnées pour filter les données
let checkContainerExist: HTMLSelectElement|null, filters: any[] = [];
for(let i in this._datasSelectors)
{
checkContainerExist=document.querySelector("#"+ this._datasSelectors[i].id+" select");
if(checkContainerExist != null && checkContainerExist.value != "0")
filters.push({ field: this._datasSelectors[i].name, value: this._datasSelectors[i].values![checkContainerExist.selectedIndex-1] });
}// ! si on récupère innerHTML du select, certains caractères peuvent être modifiés !
let datasHTML="<table><thead>";
for (let i in fields)
datasHTML+="<th>"+fields[i]+"</th>";
datasHTML+="</thead><tbody>";
for (let row in datas)
{
let view=true;
if(filters.length !== 0)
{
for(let i in filters)
{
if(datas[row][filters[i].field] != filters[i].value)
view=false;
}
}
if(view)
{
datasHTML+="<tr>";
for(let field in datas[row])
{
if(fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra"
datasHTML+="<td>"+datas[row][field]+"</td>";
}
datasHTML+="</tr>";
}
}
datasHTML+="</tbody></table>";
return datasHTML;
}
}
/* Exemple d'appelle complet à papaparse :
Papa.parse(url, {
delimiter :"",
newline: "",
quoteChar:'"',// à proposer en option
escapeChar:"",// idem
header:true,
transformHeader:"",
dynamicTyping:false,
preview:0,
encoding:"",
worker:true,
comments:"",
step:"",// prévoir en option pour très gros fichiers ?
complete: function(results :any)
{
console.log(results.meta);
console.log(results.data[35]);
console.log(results.errors);
},
error:function(error :any)
{
console.log(error);
},
download: true,// nécessaire pour un accès via "http..."
downloadRequestBody:false,
skipEmptyLines: true,
chunk:"",
fastMode:"",
beforeFirstChunk:"",
withCredentials:false,
transform: "", // à revoir
delimitersToGuess:""
});
* // https://www.papaparse.com/docs#config*/