const Papa = require("papaparse"); import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces"; const errors=require("./errors.js"); interface domElement { id: string; eltDOM?: HTMLElement; } interface selectors extends domElement { colCSV: number; name?: string; values? : string[]; } export class freeCSV2HTML { private _datasViewElt: domElement = { id:"", eltDOM:undefined }; private _datasSourceUrl: string = ""; private _datasSelectors: selectors[] = []; public parseMeta: papaParseMeta|undefined = undefined; public parseDatas: papaParseDatas[] = []; public parseErrors: papaParseErrors[] = []; public datasHTML: string = ""; set datasViewElt(elt: domElement) { 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; } } set datasSourceUrl(url: string) { if(url.trim().length === 0) throw new Error(errors.needUrl); else this._datasSourceUrl=url.trim(); } set datasSelectors(selectionElts: selectors[]) { 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); else selectionElts[i].eltDOM=checkContainerExist; } this._datasSelectors=selectionElts; } public async parse(): Promise { 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; 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 { if (this._datasViewElt.eltDOM === undefined) throw new Error(errors.needDatasElt); if(this._datasSourceUrl === "" ) throw new Error(errors.needUrl); await this.parse(); if(this.parseDatas.length === 0 || this.parseMeta!.fields === undefined) // je force avec "!", car l'existence de parseMeta certaine après parse(). { this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound; return false; } else { let converter=this; // 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].trim()) === -1) values.push(this.parseDatas[row][colName].trim()); // Des espaces gauche pourraient fausser le classement alphabétique. // Donc réutiliser le trim() lorsque l'on filtre l'affichage des données. } if(values.length > 0) { values.sort(); this._datasSelectors[i].name = colName; this._datasSelectors[i].values=values; selectorsHTML[i]=""; this._datasSelectors[i].eltDOM!.innerHTML=selectorsHTML[i]; // On recharge l'affichage des 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 checkSelectorExist: HTMLSelectElement|null, filters: any[] = []; for(let i in this._datasSelectors) { // Attention : je peux avoir des _datasSelectors fournis, mais pas de liste dans le DOM si aucune donnée ou autre problème. checkSelectorExist=document.querySelector("#"+ this._datasSelectors[i].id+" select"); if(checkSelectorExist != null && checkSelectorExist.value != "0") filters.push({ field: this._datasSelectors[i].name, value: this._datasSelectors[i].values![checkSelectorExist.selectedIndex-1] }); // Attention : si on récupère innerHTML du select, certains caractères peuvent être modifiés ! } // Création du tableau de données : let datasHTML=""; for (let i in fields) datasHTML+=""; datasHTML+=""; for (let row in datas) { let visible=true; if(filters.length !== 0) { for(let i in filters) { if(datas[row][filters[i].field].trim() != filters[i].value) visible=false; } } if(visible) { datasHTML+=""; for(let field in datas[row]) { // Attention : si les erreurs papaParse ne sont pas bloquantes, il peut y avoir des données en trop, avec comme nom de colonne : "__parsed_extra" if(fields.indexOf(field) !== -1) datasHTML+=""; } datasHTML+=""; } } datasHTML+="
"+fields[i]+"
"+datas[row][field]+"
"; return datasHTML; } }