const errors=require("./errors.js"); import { DOMElement, Filters } from "./interfaces"; import { FreeDatas2HTML } from "./FreeDatas2HTML"; export class SearchEngine implements Filters { private _converter: FreeDatas2HTML; private _datasViewElt: DOMElement={ id: "", eltDOM: undefined }; private _btnTxt: string="Search"; private _fields2Search: string[]=[]; public label: string=""; public nbCharsForSearch : number=0; public placeholder: string=""; public automaticSearch: boolean=false; private _inputValue: string=""; // Injection de la classe principale, mais uniquement si des données ont été importées constructor(converter: FreeDatas2HTML, elt: DOMElement, fields?: number[]) { if(converter.fields.length === 0 || converter.datas.length === 0) throw new Error(errors.filterNeedDatas); else { this._datasViewElt=FreeDatas2HTML.checkInDOMById(elt); this._converter=converter; // Les champs sur lesquels les recherches seront lancées. // Ils doivent se trouver dans les données parsées, mais peuvent ne pas être affichés dans les données. // Un tableau vide est accepté et signifie que les recherches se feront sur tous les champs. if(fields !== undefined && fields.length !== 0) { for(let field of fields) { if(! this._converter.checkFieldExist(field)) throw new Error(errors.searchFieldNotFound); else this._fields2Search.push(this.converter.fields[field]); } } else this._fields2Search=this._converter.fields; } } get converter() : FreeDatas2HTML { return this._converter; } get datasViewElt() : DOMElement { return this._datasViewElt; } set btnTxt(txt: string) { if(txt.trim() !== "" && txt.length <= 30) this._btnTxt=txt; } get btnTxt(): string { return this._btnTxt; } get inputValue() : string { return this._inputValue; } get fields2Search() : string[] { return this._fields2Search; } // Création du champ de recherche dans le DOM. public filter2HTML() : void { if(this.nbCharsForSearch >0 && this.placeholder === "") this.placeholder="Please enter at least NB characters." // Pas de minlength ou de required, car l'envoi d'une recherche vide doit permettre d'annuler le filtre. let html=`
`; this. _datasViewElt.eltDOM!.innerHTML=html;// "!" car l'existence de "eltDOM" est testé par le constructeur // L'affichage est actualisé quand l'éventuel nombre de caractères est atteint ou quand le champ est vide, car cela permet d'annuler ce filtre. const searchInput=document.getElementById("freeDatas2HTMLSearchTxt") as HTMLInputElement, mySearch=this; searchInput.addEventListener("input", function(e) { e.preventDefault(); mySearch._inputValue=searchInput.value; let searchLength=searchInput.value.length; if(mySearch.automaticSearch && (mySearch.nbCharsForSearch === 0 || ( searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch))) mySearch._converter.refreshView(); }); /// Afficher un message quand le nombre de caractères n'est pas atteint ? const searchBtn=document.getElementById("freeDatas2HTMLSearchBtn") as HTMLInputElement; searchBtn.addEventListener("click", function(e) { e.preventDefault(); let searchLength=searchInput.value.length; if((mySearch.nbCharsForSearch === 0 || ( searchLength === 0) || (searchLength >= mySearch.nbCharsForSearch))) mySearch._converter.refreshView(); }); } public dataIsOk(data: {[index: string]:string}) : boolean { // Pas de valeur sélectionnée = pas de filtre sur ce champ if(this._inputValue.length === 0) return true; // Sinon, on cherche la valeur saisie dans les champs définis : for(let field in data) { if(this._fields2Search.indexOf(field) !== -1) { // Attention, recherche insensible à la casse, mais aux accents, etc. if(data[field].toLowerCase().indexOf(this._inputValue.toLowerCase()) !== -1) return true; } } return false; } }