FreeDatas2HTML/src/freeDatas2HTMLParserForHTML.ts

142 lines
5.0 KiB
TypeScript

const errors=require("./errors.js");
import { ParseErrors, ParseResults, Parsers, RemoteSource, RemoteSourceSettings } from "./freeDatas2HTMLInterfaces";
import { RemoteSources } from "./freeDatas2HTMLRemoteSources";
export class ParserForHTML implements Parsers
{
private _datasRemoteSource: RemoteSource;
private _document2Parse: HTMLDocument=document;
private _parseResults: ParseResults|undefined=undefined;
private _fieldsSelector: string="table > thead > tr > th";
private _rowsSelector: string="table > tbody > tr";
private _datasSelector: string="tr > td";
// L'instance d'une autre classe que RemoteSource peut être passée au constructeur
constructor(datasRemoteSource?: RemoteSource)
{
if(datasRemoteSource !== undefined)
this._datasRemoteSource=datasRemoteSource;
else
this._datasRemoteSource=new RemoteSources({ url:"" });
}
public setRemoteSource(source: RemoteSourceSettings)
{
this._datasRemoteSource=new RemoteSources(source);
}
get datasRemoteSource() : RemoteSource
{
return this._datasRemoteSource;
}
get document2Parse() : HTMLDocument
{
return this._document2Parse;
}
set fieldsSelector(selector: string)
{
if(selector.trim() === "")
throw new Error(errors.parserSelectorsIsEmpty);
else
this._fieldsSelector=selector.trim();
}
get fieldsSelector() : string
{
return this._fieldsSelector;
}
set rowsSelector(selector: string)
{
if(selector.trim() === "")
throw new Error(errors.parserSelectorsIsEmpty);
else
this._rowsSelector=selector.trim();
}
get datasSelector() : string
{
return this._datasSelector;
}
set datasSelector(selector: string)
{
if(selector.trim() === "")
throw new Error(errors.parserSelectorsIsEmpty);
else
this._datasSelector=selector.trim();
}
get rowsSelector() : string
{
return this._rowsSelector;
}
get parseResults() : ParseResults|undefined
{
return this._parseResults;
}
public async parse(): Promise<any>
{
const parser=this;
const realFields: string[]=[], datas: {[index: string]:string}[]=[], parseErrors: ParseErrors[]=[];
// Document HTML distant ?
if(parser._datasRemoteSource.url !== "")
{
const settings: {}=parser._datasRemoteSource.getFetchSettings();
const response=await fetch(parser._datasRemoteSource.url, settings);
if (! response.ok)
throw new Error(errors.parserRemoteFail);
const responseHTML=await response.text();
const parserDOM=new DOMParser();
this._document2Parse=parserDOM.parseFromString(responseHTML, "text/html");
}
// Récupérer les noms des champs qui doivent avoir été fournis
// Ils ne peuvent être une chaîne vide ou en doublon
const fields=this._document2Parse.querySelectorAll(this._fieldsSelector);
if(fields.length === 0)
throw new Error(errors.parserElementsNotFound+this._fieldsSelector);
for(let i=0; i < fields.length; i++)
{
let checkField=(fields[i].textContent+"").trim(); // ajout de "" pour éviter erreur "TS2531: Object is possibly 'null'"
if(checkField !== "" && realFields.indexOf(checkField) === -1)
realFields.push(checkField);
else
console.error(errors.parserFieldNameFail); // lancer une exception, car risque de bugs par la suite ???
}
if(realFields.length === 0)
throw new Error(errors.parserFieldsNotFound);
// Puis récupération des données.
// Il peut n'y avoir aucune ligne si aucune donnée trouvée.
const rows=this._document2Parse.querySelectorAll(this._rowsSelector);
let datasElts;
for(let i=0; i < rows.length; i++)
{
// Les nombre de données par ligne ne devrait pas être différent du nombre de champs.
datasElts=rows[i].querySelectorAll(this._datasSelector);
if(datasElts.length !== realFields.length)
parseErrors.push({ row:i, message:errors.parserMeetErrors});
// Les chaînes vides sont par contre acceptées ici.
let dataObject: {[index: string]: string} = {}
for(let j=0; j < datasElts.length && j < realFields.length; j++)
dataObject[realFields[j]]=datasElts[j].textContent+"";
// Mais les lignes complétement vides doivent être ignorées.
if(Object.keys(dataObject).length !== 0)
datas.push(dataObject)
else
parseErrors.push({ row:i, message:errors.parserMeetErrors});
}
parser._parseResults =
{
datas: datas,
errors: parseErrors,
fields: realFields,
};
}
}