142 lines
5.0 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
} |