diff --git a/src/errors.js b/src/errors.js index f1ee259..50d8fb8 100644 --- a/src/errors.js +++ b/src/errors.js @@ -1,5 +1,9 @@ module.exports = { + datasNotFound : "Aucune donnée n'a été trouvée.", elementNotFound : "Aucun élément HTML n'a été trouvé ayant comme \"id\" : ", - urlNotValid: "L'url fournie pour le fichier CSV n'a pas un format correcte : ", + needDatasElt: "Merci de fournir un id valide pour l'élément où afficher les données.", + needNaturalNumber: "Merci de fournir un nombre entier supérieur ou égal à zéro pour désigner chaque colonne.", + needUrl: "Merci de fournir l'url du fichier CSV à parser.", + parserFail: "La lecture des données du fichier a échoué.", }; \ No newline at end of file diff --git a/src/firstExample.ts b/src/firstExample.ts index e69de29..20a6892 100644 --- a/src/firstExample.ts +++ b/src/firstExample.ts @@ -0,0 +1,90 @@ +import { freeCSV2HTML } from "./freeCSV2HTML"; + +const initialise = async () => +{ + try + { + let converter=new freeCSV2HTML(); + converter.datasViewElt={ id:"datas" }; + converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv"; + await converter.run(); + } + catch(e) + { + console.error(e); + } +} +initialise(); + + + /* + public async setDatasUrl(url: string) : Promise // pas de setter classique à cause asynchrone + { + const converter=this; // nécessaire pour éviter confusion avec this à l'intérieur de XMLHttpRequest + await fetch(url).then(function(response) + { + if(response.ok) + { + converter._datasUrl=url; + } + }) + if(converter._datasUrl!=="") + return true; + else + return false; + } + + /* + let req = new XMLHttpRequest(); + req.open("GET", url); + req.addEventListener("load", function () + { + if (req.status >= 200 && req.status < 400) + { + converter._datasUrl=url; + return true; + } + else + { + console.error("Fichier non trouvé"); + return false; + } + }); + }*/ + + +/* 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*/ \ No newline at end of file diff --git a/src/freeCSV2HTML.ts b/src/freeCSV2HTML.ts index f9ffb13..aa9216b 100644 --- a/src/freeCSV2HTML.ts +++ b/src/freeCSV2HTML.ts @@ -1,44 +1,135 @@ +const Papa = require("papaparse"); + +import { papaParseDatas, papaParseErrors, papaParseMeta } from "./papaParseInterfaces"; const errors=require("./errors.js"); -interface selectors +interface domElement +{ + id: string; + eltDOM?: HTMLElement; +} +interface selectors extends domElement { colCSV: number; - idElt: string; - eltDOM?: HTMLElement; } export class freeCSV2HTML { - private _datasEltId: string = ""; - private _datasUrl: string = ""; + // Configuration : + private _datasViewElt: domElement = { id:"", eltDOM:undefined }; + private _datasSourceUrl: string = ""; private _datasSelectors: selectors[] = []; + + // Données reçues ou générées suite parsage du fichier CSV : + public parseMeta: papaParseMeta|undefined = undefined; + public parseDatas: papaParseDatas[] = []; + public parseErrors: papaParseErrors[] = []; + private datasHTML: string = ""; - set datasEltId(id: string) + set datasViewElt(elt: domElement) { - let checkContainerExist=document.getElementById(id); - if(checkContainerExist === null) - throw new Error(errors.elementNotFound+id); - else - this._datasEltId=id; + 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 datasUrl(url: string) + // Pas plus de tests car nombreux protocoles possibles pour l'url du fichier, pas uniquement http(s). À revoir. + set datasSourceUrl(url: string) { - this._datasUrl=url; + if(url.trim().length === 0) + throw new Error(errors.needUrl); + else + this._datasSourceUrl=url.trim(); } + // La validité du numéro de colonne ne peut être vérifée qu'après le parsage. set datasSelectors(selectors: selectors[]) { - let checkContainerExist: HTMLElement; + let checkContainerExist: HTMLElement|null; for(let i = 0; i < selectors.length; i++) { - checkContainerExist=document.getElementById(selectors[i].idElt); + checkContainerExist=document.getElementById(selectors[i].id); if(checkContainerExist === null) - throw new Error(errors.elementNotFound+selectors[i].idElt); + throw new Error(errors.elementNotFound+selectors[i].id); + else if(Number.isInteger(selectors[i].colCSV) === false || selectors[i].colCSV < 0) + throw new Error(errors.needNaturalNumber); else selectors[i].eltDOM=checkContainerExist; } this._datasSelectors=selectors; } + public async parse(): Promise + { + const converter=this; // nécessaire pour éviter confusion avec le "this" de la promise + return new Promise((resolve,reject) => + { + if(this._datasSourceUrl !== "" ) + { + Papa.parse(this._datasSourceUrl, + { + quoteChar:'"', // à proposer en option ? + 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, // à proposer en option ? + }); + } + else + resolve(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); + + if(await this.parse() === true) + { + if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined) + { + this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound; + return false; + } + else + { + this.datasHTML=""; + for (let i in this.parseMeta.fields) + this.datasHTML+=""; + this.datasHTML+=""; + for (let row in this.parseDatas) + { + this.datasHTML+=""; + for(let field in this.parseDatas[row]) + { + if( this.parseMeta.fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra" + this.datasHTML+=""; + } + this.datasHTML+=""; + } + this.datasHTML+="
"+this.parseMeta.fields[i]+"
"+this.parseDatas[row][field]+"
"; + this._datasViewElt.eltDOM.innerHTML=this.datasHTML; + return true; + } + } + } } \ No newline at end of file diff --git a/src/papaParseInterfaces.ts b/src/papaParseInterfaces.ts new file mode 100644 index 0000000..7f5abb1 --- /dev/null +++ b/src/papaParseInterfaces.ts @@ -0,0 +1,23 @@ +// cf. https://www.papaparse.com/docs#results + +export interface papaParseDatas +{ + [key: string]: string; +} + +export interface papaParseErrors +{ + type: string; + code: string; + message: string; + row: number; +} + +export interface papaParseMeta +{ + delimiter?: string; + linebreak?: string; + aborted?: boolean; + fields?: string[]; + truncated?: boolean; +} \ No newline at end of file