While I was sleeping it occurred to me that the previous class we saw previously to specify some
search words for either thread or posts could indeed be made more general (thus avoiding its mere twofold nature).
In fact, as the site expands (in the far future

) we may need
keywords to be stored for
users too. We might even use it for the
forums/categories (even though this would make sense only for sites with a large number of categories, like the
Rome ad site, which has hundreds of categories). So I believe that it may be useful to envision a field for that too in all the tables (
forums/threads/posts/users) and make it more general.
This way we can reuse the same object we used to retrieve/store the keywords of the threads and the post, for the users' keywords too. So this is the new version of the class:
Code:
'use strict';
class SearchWordsInputControl {
crea_cSearchWordsInputControl(SearchRequestWithSearchWords_Current) {
this.SearchRequestWithSearchWords_Current = SearchRequestWithSearchWords_Current;
//pannello contenitore globale
this.div_cSearchWordsInputControl_Container = document.createElement('div');
this.div_cSearchWordsInputControl_Container.style.cssText = `background-color:rgba(250,250,255, 0.3); margin-top: 10px; margin-bottom: 10px; padding:10px; border-radius:5px`;
//input delle search words
for (let w = 0; w < _NUMBER_OF_SEARCH_WORDS; w++) {
let input_SearchWord = document.createElement('input');
input_SearchWord.type = "text";
input_SearchWord.classList.add("SearchKeyword");
setup_INPUT_WITH_BORDER(input_SearchWord);
input_SearchWord.style.cssText += `width:200px; height:16px; font-size:16px; margin:5px; margin-top:15px;`;
this.div_cSearchWordsInputControl_Container.appendChild(input_SearchWord);
}
//tipo ricerca AND OR ------------------------------------------------------------------------------------------------------------------------------
let label_ContenitoreTipoRicercaAND_OR = document.createElement('label');
label_ContenitoreTipoRicercaAND_OR.innerHTML = "Tipo di ricerca: OR (almeno una parola e' presente) o AND (tutte le parole sono simultaneamente presenti)";
label_ContenitoreTipoRicercaAND_OR.style.cssText = "display:block; margin-top:10px; margin-bottom:5px; color:lime;";
//OR
let label_RicercaOR = document.createElement('label');
label_RicercaOR.innerHTML = "OR";
label_RicercaOR.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px; ";
//radio
this.input_radio_RicercaOR = document.createElement('input');
this.input_radio_RicercaOR.type = "radio";
this.input_radio_RicercaOR.name = 'SearchTypeAND_OR';
this.input_radio_RicercaOR.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_RicercaOR.appendChild(this.input_radio_RicercaOR);
//label in label per allineare
label_ContenitoreTipoRicercaAND_OR.appendChild(label_RicercaOR);
//AND
let label_RicercaAND = document.createElement('label');
label_RicercaAND.innerHTML = "AND";
label_RicercaAND.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px;";
//radio
this.input_radio_RicercaAND = document.createElement('input');
this.input_radio_RicercaAND.type = "radio";
this.input_radio_RicercaAND.name = 'SearchTypeAND_OR';
this.input_radio_RicercaAND.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_RicercaAND.appendChild(this.input_radio_RicercaAND);
//label in label per allineare
label_ContenitoreTipoRicercaAND_OR.appendChild(label_RicercaAND);
//pannello search request
this.div_cSearchWordsInputControl_Container.appendChild(label_ContenitoreTipoRicercaAND_OR);
// --------------------------------------------------------------------------------------------------------
//tipo ricerca Exact match/Like ------------------------------------------------------------------------------------------------------------------------------
let label_ContenitoreTipoRicerca_LIKE_EXACT = document.createElement('label');
label_ContenitoreTipoRicerca_LIKE_EXACT.innerHTML = "Match type";
label_ContenitoreTipoRicerca_LIKE_EXACT.style.cssText = "display:block; margin-top:10px; margin-bottom:5px; color:lime;";
//LIKE
let label_Ricerca_LIKE = document.createElement('label');
label_Ricerca_LIKE.innerHTML = "Like";
label_Ricerca_LIKE.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px; ";
//radio
this.input_radio_Ricerca_LIKE = document.createElement('input');
this.input_radio_Ricerca_LIKE.type = "radio";
this.input_radio_Ricerca_LIKE.name = 'SearchTypeLIKEorEXACT';
this.input_radio_Ricerca_LIKE.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_Ricerca_LIKE.appendChild(this.input_radio_Ricerca_LIKE);
//label in label per allineare
label_ContenitoreTipoRicerca_LIKE_EXACT.appendChild(label_Ricerca_LIKE);
//EXACT
let label_Ricerca_EXACT = document.createElement('label');
label_Ricerca_EXACT.innerHTML = "Exact Match";
label_Ricerca_EXACT.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px;";
//radio
this.input_radio_Ricerca_EXACT = document.createElement('input');
this.input_radio_Ricerca_EXACT.type = "radio";
this.input_radio_Ricerca_EXACT.name = 'SearchTypeLIKEorEXACT';
this.input_radio_Ricerca_EXACT.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_Ricerca_EXACT.appendChild(this.input_radio_Ricerca_EXACT);
//label in label per allineare
label_ContenitoreTipoRicerca_LIKE_EXACT.appendChild(label_Ricerca_EXACT);
//pannello search request
this.div_cSearchWordsInputControl_Container.appendChild(label_ContenitoreTipoRicerca_LIKE_EXACT);
// --------------------------------------------------------------------------------------------------------
}
loadSearchWordsToInterface() {
//ricarica impostazioni ricerca precedente
this.input_radio_RicercaOR.checked = this.SearchRequestWithSearchWords_Current.RicercaParoleConOR;
this.input_radio_RicercaAND.checked = !this.SearchRequestWithSearchWords_Current.RicercaParoleConOR;
this.input_radio_Ricerca_LIKE.checked = this.SearchRequestWithSearchWords_Current.RicercaParoleConLIKE;
this.input_radio_Ricerca_EXACT.checked = !this.SearchRequestWithSearchWords_Current.RicercaParoleConLIKE;
//ricarica parole precedente ricerca negli slot disponibili
let AllSearchWordInputs = this.div_cSearchWordsInputControl_Container.getElementsByClassName('SearchKeyword');
let wordIndex = 0;
Array.from(AllSearchWordInputs).forEach(SearchwordInput => {
if (this.SearchRequestWithSearchWords_Current.ParoleCercate.length > wordIndex) {
SearchwordInput.value = this.SearchRequestWithSearchWords_Current.ParoleCercate[wordIndex];
wordIndex++;
}
});
}
readSearchWordsFromInterfaceAndSearchProperties() {
//lista parole
this.SearchRequestWithSearchWords_Current.ParoleCercate = [];
let AllSearchWordInputs = this.div_cSearchWordsInputControl_Container.getElementsByClassName('SearchKeyword');
Array.from(AllSearchWordInputs).forEach(inputCheckbox => {
//solo se non vuota
let searchWord = inputCheckbox.value.trim();
if (searchWord) {
this.SearchRequestWithSearchWords_Current.ParoleCercate.push(searchWord);
}
});
//proprieta ricerca
this.SearchRequestWithSearchWords_Current.RicercaParoleConOR = this.input_radio_RicercaOR.checked;
this.SearchRequestWithSearchWords_Current.RicercaParoleConLIKE = this.input_radio_Ricerca_LIKE.checked;
}
}
where the
SearchRequestWithSearchWords object is (at the moment) defined as follows:
Code:
class SearchRequestWithSearchWords {
ParoleCercate; //array stringhe
RicercaParoleConOR;
RicercaParoleConLIKE;
//ricerca post utente specifico
ForUserProfile;
//ricerca post tutti utenti
RicercaInThread_Titles;
RicercaInThread_Keywords;
RicercaInPost_Keywords;
constructor(ForUserProfile) {
this.ParoleCercate = [];
this.RicercaParoleConOR = true;
this.RicercaParoleConLIKE = true;
this.ForUserProfile = ForUserProfile;
if (!this.ForUserProfile) {
this.RicercaInThread_Titles = true;
this.RicercaInThread_Keywords = true;
this.RicercaInPost_Keywords = true;
}
}
}
Here is how it looks on our
Site Search page:
This can also be immediately
reused in a feature to search and filter (by
keywords) all the
posts of any user on the
User Profile page.
I am just adding that. Since this new feature will also require all the search functionalities, it is useful to "take them"
out of the
Search page and make a
general class so we can
reuse it on the
User Profile page, and wherever we need it in the future in the site. Now since we have similar code on two different pages: the general post search page and the user profile page (for the search of the post of the user shown in the profile), we want to now
unify these functionalities into a unique abstract object.
(For instance, in the future we might include a search of users by keywords.)
In order
not to shoot ourselves in the balls and make our life harder when maintaining the code it is
necessary to make
now this
little effort of abstraction. This will
enormously simplify both the search and user profile page. In fact, changing the code at a
later time becomes increasingly difficult, because one does not "hold it all in mind" and must therefore make, every time a change is needed, a huge effort to simply re-enter the "mindset".
So the resulting class would hold a general (and further generalizable)
search panel, where one can specify anything and all results will remain within the panel, which could be inserted as objects anywhere.
In this new "control", we can also encapsulate the previously seen functionality to input/resume the search words.
Here is the brand-new search control:
Code:
'use strict';
class ResultEntryControl_Post {
constructor(threadPost, UserInfo_Current) {
this.threadPost = threadPost;
this.UserInfo_Current = UserInfo_Current;
}
Crea_cResultEntryControl_Post() {
this.div_PostPanel = document.createElement('div');
this.div_PostPanel.style.cssText = `background-color:rgba(255,255,255,0.2); margin:5px;`;
//avatar OP
let div_img = document.createElement("img");
div_img.src = `${this.threadPost.AvatarFileUserWithFolder}${cacheBreaker_Time()}`;
div_img.style.cssText = `display:inline-block; height:60px; margin-top:10px;`;
this.div_PostPanel.appendChild(div_img);
//ID e nick insieme
let div_TitleAndNick = document.createElement("div");
div_TitleAndNick.style.cssText = `display:inline-block; vertical-align: top; cursor:pointer; text-align:left; padding: 10px; margin-left:10px`;
//ID post/autore
let div_TitoloThread = document.createElement("div");
div_TitoloThread.innerHTML = `Post ID: ${this.threadPost.PostID} in thread ID: ${this.threadPost.ThreadID} by user ${this.threadPost.UserAuthorID}`;
div_TitoloThread.style.cssText = `font-size:12px; font-weight:bold; font-style:italic; color:${_SiteJS.COLOR_THREAD_TITLE};`;
div_TitleAndNick.appendChild(div_TitoloThread);
//nick
let div_NicknameUser = document.createElement("div");
let coloreNick;
if ((this.UserInfo_Current) && (this.threadPost.UserAuthorID === this.UserInfo_Current.UserID)) {
//se stesso
div_NicknameUser.innerHTML = `${_myUserEmoji}${this.threadPost.NicknameAuthor}`;
coloreNick = "yellow";
} else {
div_NicknameUser.innerHTML = `${this.threadPost.NicknameAuthor} `;
coloreNick = "black";
}
div_NicknameUser.style.cssText = `margin-top:5px; font-size:12px; font-weight:bold; color:${coloreNick};`;
div_TitleAndNick.appendChild(div_NicknameUser);
//storedKeywords
let div_storedKeywords = document.createElement("div");
div_storedKeywords.innerHTML = `stored keywords:"${this.threadPost.SearchKeyWords}"`;
div_storedKeywords.style.cssText = `margin-top:5px; font-size:12px; color: white;`;
div_TitleAndNick.appendChild(div_storedKeywords);
this.div_PostPanel.appendChild(div_TitleAndNick);
this.div_PostPanel.onclick = () => {
window.location.href = `${_POSTS_PAGE_NAME}?Intention=LinkToPost&PostID=${this.threadPost.PostID}`;
};
}
}
class ResultEntryControl_Thread {
constructor(forumThread, UserInfo_Current) {
this.forumThread = forumThread;
this.UserInfo_Current = UserInfo_Current;
}
Crea_cResultEntryControl_Thread() {
this.div_ThreadPanel = document.createElement('div');
this.div_ThreadPanel.style.cssText = `background-color:rgba(255,255,255,0.2); margin:5px;`;
//avatar OP
let div_img = document.createElement("img");
div_img.src = `${this.forumThread.AvatarAuthorWithFolder}${cacheBreaker_Time()}`;
div_img.style.cssText = `display:inline-block; height:60px; margin-top:10px;`;
this.div_ThreadPanel.appendChild(div_img);
//titolo e nick insieme
let div_TitleAndNick = document.createElement("div");
div_TitleAndNick.style.cssText = `display:inline-block; vertical-align: top; cursor:pointer; text-align:left; padding: 10px; margin-left:10px`;
//titolo Thread
let div_TitoloThread = document.createElement("div");
div_TitoloThread.innerHTML = `${this.forumThread.ThreadTitle}`;
div_TitoloThread.style.cssText = `font-size:16px; font-weight:bold; font-style:italic; color:#bbffdd;`;
div_TitleAndNick.appendChild(div_TitoloThread);
//nick
let div_NicknameUser = document.createElement("div");
let coloreNick;
if ((this.UserInfo_Current) && (this.forumThread.OpUserID === this.UserInfo_Current.UserID)) {
//se stesso
div_NicknameUser.innerHTML = `${_myUserEmoji}${this.forumThread.NicknameAuthor} user id: ${this.forumThread.OpUserID}`;
coloreNick = "yellow";
} else {
div_NicknameUser.innerHTML = `${this.forumThread.NicknameAuthor} user id: ${this.forumThread.OpUserID}`;
coloreNick = "black";
}
div_NicknameUser.style.cssText = `margin-top:5px; font-size:12px; font-weight:bold; color:${coloreNick};`;
//storedKeywords
let div_storedKeywords = document.createElement("div");
if (this.forumThread.SearchKeyWords) {
div_storedKeywords.innerHTML = `stored keywords:"${this.forumThread.SearchKeyWords}"`;
} else {
div_storedKeywords.innerHTML = `no keywords defined`;
}
div_storedKeywords.style.cssText = `margin-top:5px; font-size:12px; color: white;`;
div_TitleAndNick.appendChild(div_storedKeywords);
this.div_ThreadPanel.appendChild(div_TitleAndNick);
this.div_ThreadPanel.onclick = () => {
//window.location.href = `${_POSTS_PAGE_NAME}?thread=${this.forumThread.ThreadID}&ThreadPage=1`; tentativo con pagina 1 (va bottom pero, per ora lascio stare)
window.location.href = `${_POSTS_PAGE_NAME}?thread=${this.forumThread.ThreadID}`;
};
}
}
class SearchPanelWithSearchWords {
crea_cSearchPanelWithSearchWords(SearchRequestWithSearchWords_Current, ResultOfSearchWithSearchWords_Current, UserID_UserSearched, UserInfo_Current, InitialDisplay) {
this.SearchRequestWithSearchWords_Current = SearchRequestWithSearchWords_Current;
this.ResultOfSearchWithSearchWords_Current = ResultOfSearchWithSearchWords_Current;
this.UserID_UserSearched = UserID_UserSearched;
this.UserInfo_Current = UserInfo_Current; //just for visuals: to enlight posts of current user, if logged
let titolo;
if (this.SearchRequestWithSearchWords_Current.ForUserProfile) {
titolo = "Search in post of this user (by keywords if specified)";
} else {
titolo = "Search posts keywords or threads titles/keywords";
}
//pannello contenitore globale
this.div_cSearchPanelWithSearchWords_Container = document.createElement('div');
this.div_cSearchPanelWithSearchWords_Container.style.cssText = `background-color:rgba(255,255,255,0.2); padding:10px;`;
this.div_cSearchPanelWithSearchWords_Container.style.display = InitialDisplay;
//titolo search
let div_TitoloRicerca = document.createElement('div');
div_TitoloRicerca.innerHTML = titolo;
div_TitoloRicerca.style.cssText = `color:white; background-color:white; text-align: center; background-color:rgba(255,255,255,0.2);
font-size:32px; font-weight:bold; padding:10px; border-radius:5px;`;
this.div_cSearchPanelWithSearchWords_Container.appendChild(div_TitoloRicerca);
//pannello search words input --------------------------------------------------
this.mySearchWordsInputControl = new SearchWordsInputControl();
this.mySearchWordsInputControl.crea_cSearchWordsInputControl(this.SearchRequestWithSearchWords_Current);
this.div_cSearchPanelWithSearchWords_Container.appendChild(this.mySearchWordsInputControl.div_cSearchWordsInputControl_Container);
//-------------------------------------------------------------------------------
//bottone Search!
this.button_Search = document.createElement('button');
setup_BUTTON_LOOK_ANIMATION(this.button_Search, "Search!");
this.button_Search.style.cssText += "width:auto; margin-left:20px; font-size: 30px";
this.div_cSearchPanelWithSearchWords_Container.appendChild(this.button_Search);
this.button_Search.onclick = () => { this.creaNuovaSearchRequestWithSearchWords_DaInterfaccia() };
//pannello contenitore globale dei risultati ------------------------------------------------------------
this.div_ResultOfSearchWithSearchWords_Panel = document.createElement('div');
this.div_ResultOfSearchWithSearchWords_Panel.style.cssText = `background-color:rgba(255,255,255,0.2); padding:10px;`;
this.div_cSearchPanelWithSearchWords_Container.appendChild(this.div_ResultOfSearchWithSearchWords_Panel);
if (!this.SearchRequestWithSearchWords_Current.ForUserProfile) { //profile search dos not need this
//where to search ---------------------------------------------------------------------------------------------------------------------------------
let label_ContenitoreTipoRicercaPOST_THREAD = document.createElement('label');
label_ContenitoreTipoRicercaPOST_THREAD.innerHTML = "Dove cercare:";
label_ContenitoreTipoRicercaPOST_THREAD.style.cssText = "display:block; margin-top:10px; margin-bottom:5px; color:lime;";
//THREADS' TITLES
let label_RicercaTHREADS_TITLE = document.createElement('label');
label_RicercaTHREADS_TITLE.innerHTML = "Nei TITOLI delle THREADS";
label_RicercaTHREADS_TITLE.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px; ";
//radio
this.input_check_RicercaTHREADS_TITLES = document.createElement('input');
this.input_check_RicercaTHREADS_TITLES.type = "checkbox";
this.input_check_RicercaTHREADS_TITLES.name = 'SearchTypeTHREADS_POSTS';
this.input_check_RicercaTHREADS_TITLES.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_RicercaTHREADS_TITLE.appendChild(this.input_check_RicercaTHREADS_TITLES);
//label in label per allineare
label_ContenitoreTipoRicercaPOST_THREAD.appendChild(label_RicercaTHREADS_TITLE);
//THREADS' KEYWORDS
let label_RicercaTHREADS_KEYWORDS = document.createElement('label');
label_RicercaTHREADS_KEYWORDS.innerHTML = "Nelle KEYWORDS delle Threads";
label_RicercaTHREADS_KEYWORDS.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px; ";
//radio
this.input_check_RicercaTHREADS_KEYWORDS = document.createElement('input');
this.input_check_RicercaTHREADS_KEYWORDS.type = "checkbox";
this.input_check_RicercaTHREADS_KEYWORDS.name = 'SearchTypeTHREADS_POSTS';
this.input_check_RicercaTHREADS_KEYWORDS.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_RicercaTHREADS_KEYWORDS.appendChild(this.input_check_RicercaTHREADS_KEYWORDS);
//label in label per allineare
label_ContenitoreTipoRicercaPOST_THREAD.appendChild(label_RicercaTHREADS_KEYWORDS);
//POSTS KEYWORDS
let label_RicercaPOSTS = document.createElement('label');
label_RicercaPOSTS.innerHTML = "Nelle KEYWORDS dei POSTS";
label_RicercaPOSTS.style.cssText = "color:#aaffff; cursor:pointer; margin-left:10px;";
//radio
this.input_check_RicercaPOSTS_KEYWORDS = document.createElement('input');
this.input_check_RicercaPOSTS_KEYWORDS.type = "checkbox";
this.input_check_RicercaPOSTS_KEYWORDS.name = 'SearchTypeTHREADS_POSTS';
this.input_check_RicercaPOSTS_KEYWORDS.style.cssText += "width:15px; height:15px; cursor:pointer;";
label_RicercaPOSTS.appendChild(this.input_check_RicercaPOSTS_KEYWORDS);
//label in label per allineare
label_ContenitoreTipoRicercaPOST_THREAD.appendChild(label_RicercaPOSTS);
//tutti i comandi
this.div_cSearchPanelWithSearchWords_Container.appendChild(label_ContenitoreTipoRicercaPOST_THREAD);
// ---------------------------------------------------------------------------------------------------------------------------------
}
//caricamento eventuale richiesta di ricerca esistente
//reimpostazione valori su pannello ricerca
this.mySearchWordsInputControl.loadSearchWordsToInterface(this.SearchRequestWithSearchWords_Current);
if (!this.SearchRequestWithSearchWords_Current.ForUserProfile) {
this.input_check_RicercaTHREADS_TITLES.checked = this.SearchRequestWithSearchWords_Current.RicercaInThread_Titles;
this.input_check_RicercaTHREADS_KEYWORDS.checked = this.SearchRequestWithSearchWords_Current.RicercaInThread_Keywords;
this.input_check_RicercaPOSTS_KEYWORDS.checked = this.SearchRequestWithSearchWords_Current.RicercaInPost_Keywords;
}
//precedenti risultati se presenti
this.carica_ResultOfSearchWithSearchWords_SuInterfaccia();
}
//Chiamata alla pressione del bottone search!
async creaNuovaSearchRequestWithSearchWords_DaInterfaccia() { //ForUserProfile is for search on single user's posts
//fill search object from interface
this.mySearchWordsInputControl.readSearchWordsFromInterfaceAndSearchProperties();
//complete request properties with targets
if (!this.SearchRequestWithSearchWords_Current.ForUserProfile) {
//target di ricerca (non definiti per ricerca su user)
this.SearchRequestWithSearchWords_Current.RicercaInPost_Keywords = this.input_check_RicercaPOSTS_KEYWORDS.checked;
this.SearchRequestWithSearchWords_Current.RicercaInThread_Titles = this.input_check_RicercaTHREADS_TITLES.checked;
this.SearchRequestWithSearchWords_Current.RicercaInThread_Keywords = this.input_check_RicercaTHREADS_KEYWORDS.checked;
}
let requestPurpose;
let myTargetPage;
let myFormData = new FormData();
if (!this.SearchRequestWithSearchWords_Current.ForUserProfile) {
requestPurpose = "PerformSearchInAllPostsOrThreads";
myTargetPage = _SEARCH_PAGE_NAME;
if (!SearchRequestWithSearchWords_Current.ParoleCercate) {
tempNotify("No search words");
return;
}
} else {
requestPurpose = "PerformSearchInPostsOfSpecifiedUser";
myTargetPage = _PROFILE_PAGE_NAME;
myFormData.append("UserID", this.UserID_UserSearched);
//for profile search, it is fine not to have search words (will find all post of the user)
}
myFormData.append("SearchRequestWithSearchWords", JSON.stringify(this.SearchRequestWithSearchWords_Current));
myFormData.append("requestPurpose", requestPurpose);
let xhr = await SendFormData(myFormData, myTargetPage, "Requesting search");
if (xhr) {
tempNotify(`Search successful`, 30, 30, 1000);
//ricezione oggetto risposta
this.ResultOfSearchWithSearchWords_Current = receiveServerObject(xhr.response, ResultOfSearchWithSearchWords, "Obj_ResultOfSearchWithSearchWords");
this.carica_ResultOfSearchWithSearchWords_SuInterfaccia();
}
}
//Risultati ricerca
carica_ResultOfSearchWithSearchWords_SuInterfaccia() {
//reset pannello risultati
this.div_ResultOfSearchWithSearchWords_Panel.innerHTML = "";
if (!this.ResultOfSearchWithSearchWords_Current) { return }
//titolo response
let div_TitoloRispostaSearch = document.createElement('div');
div_TitoloRispostaSearch.innerHTML = "Search Response";
div_TitoloRispostaSearch.style.cssText = `display:block; color:white; background-color:yellow; text-align:center; background-color:rgba(0,0,100,0.2);
font-size:32px; font-weight:bold; padding:10px; border-radius: 5px; margin-bottom:10px; `;
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_TitoloRispostaSearch);
//show search words (debug only)
if (this.ResultOfSearchWithSearchWords_Current.SearchRequestWithSearchWords) {
this.ResultOfSearchWithSearchWords_Current.SearchRequestWithSearchWords.ParoleCercate.forEach(p => {
let div_parolaCercata = document.createElement('div');
div_parolaCercata.style.cssText = `display:inline-block; color:red; background-color:white; margin:10px`;
div_parolaCercata.innerHTML = `${p}`;
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_parolaCercata);
});
} else {
let div_parolaCercata = document.createElement('div');
div_parolaCercata.style.cssText = `display:inline-block; color:red; background-color:white;`;
div_parolaCercata.innerHTML = "List of searched words is empty";
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_parolaCercata);
}
if (this.SearchRequestWithSearchWords_Current.ForUserProfile) {
//POSTS of user in viewed profile, parole nelle keywords
let div_ContenitorePOSTS_UtenteSpecifico_ConParoleNelleKeyWords = this.creaContenitoreConListaPosts(this.ResultOfSearchWithSearchWords_Current.POSTS_UtenteSpecifico_ConParoleNelleKeyWords, `User's POSTS (with searched words in KEYWORDS) User id: ${this.UserID_UserSearched}`);
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_ContenitorePOSTS_UtenteSpecifico_ConParoleNelleKeyWords);
} else {
//Posts: parole nelle keywords
let div_ContenitorePosts_ConParoleNelleKeywords = this.creaContenitoreConListaPosts(this.ResultOfSearchWithSearchWords_Current.Posts_ConParoleNelleKeywords, "POSTS with searched words in KEYWORDS");
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_ContenitorePosts_ConParoleNelleKeywords);
//Threads: parole nel titolo
let div_ContenitoreThreads_ConParoleNelTitolo = this.creaContenitoreConListaThreads(this.ResultOfSearchWithSearchWords_Current.Threads_ConParoleNelTitolo, "THREADS with searched words in TITLE");
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_ContenitoreThreads_ConParoleNelTitolo);
//Threads: parole nelle keywords
let div_ContenitoreThreads_ConParoleNelleKeywords = this.creaContenitoreConListaThreads(this.ResultOfSearchWithSearchWords_Current.Threads_ConParoleNelleKeywords, "THREADS with searched words in KEYWORDS");
this.div_ResultOfSearchWithSearchWords_Panel.appendChild(div_ContenitoreThreads_ConParoleNelleKeywords);
}
}
//Contenitori per risultati
creaContenitoreConListaPosts(ListaPosts, titoloRisultati) {
//contenitore
let div_ContenitorePosts = document.createElement('div');
//div titolo
let div_TitoloPostResults = document.createElement('div');
div_TitoloPostResults.style.cssText = `display:block; color:white; background-color:black;
font-size:14px; font-weight:bold; margin-bottom:10px; margin-top:20px; padding:10px; border-radius:5px;`;
if (ListaPosts) {
div_TitoloPostResults.innerHTML = `${titoloRisultati} (found: ${ListaPosts.length})`;
div_ContenitorePosts.appendChild(div_TitoloPostResults);
ListaPosts.forEach(p => {
let rPost = new ResultEntryControl_Post(p, this.UserInfo_Current);
rPost.Crea_cResultEntryControl_Post();
div_ContenitorePosts.appendChild(rPost.div_PostPanel);
});
} else {
div_TitoloPostResults.innerHTML = `${titoloRisultati}: NONE`;
div_ContenitorePosts.appendChild(div_TitoloPostResults);
}
return div_ContenitorePosts;
}
creaContenitoreConListaThreads(ListaThreads, titoloRisultati) {
//contenitore
let div_ContenitoreThreads = document.createElement('div');
//div titolo
let div_TitoloThreadResults = document.createElement('div');
div_TitoloThreadResults.style.cssText = `display:block; color:white; background-color:black;
font-size:14px; font-weight:bold; margin-bottom:10px; margin-top:20px; padding:10px; border-radius:5px;`;
if (ListaThreads) {
div_TitoloThreadResults.innerHTML = `${titoloRisultati} (found: ${ListaThreads.length})`;
div_ContenitoreThreads.appendChild(div_TitoloThreadResults);
//lista Threads_ConParole
ListaThreads.forEach(t => {
let rThread = new ResultEntryControl_Thread(t, this.UserInfo_Current);
rThread.Crea_cResultEntryControl_Thread();
div_ContenitoreThreads.appendChild(rThread.div_ThreadPanel);
});
} else {
div_TitoloThreadResults.innerHTML = `${titoloRisultati}: NONE`;
div_ContenitoreThreads.appendChild(div_TitoloThreadResults);
}
return div_ContenitoreThreads;
}
}
In fact, this new reusable control simplifies enormously our code, as for instance the
Search page (we have seen previously) now reduces to simply this

:
Code:
'use strict';
showServerSideErrors(document);
setBasicPageBehavior("Site search", _SiteJS.COLOR_HOMEPAGE_BACKGROUND, _SiteJS.SITE_ICON);
//Collect server objects
let UserInfo_Current = receiveServerObject(document, UserInfo, "Obj_UserInfo");
//ricerche
let SearchRequestWithSearchWords_Current = receiveServerObject(document, SearchRequestWithSearchWords, "Obj_SearchRequestWithSearchWords");
let ResultOfSearchWithSearchWords_Current = receiveServerObject(document, ResultOfSearchWithSearchWords, "Obj_ResultOfSearchWithSearchWords");
let div_SearchRequestWithSearchWords_Panel = document.createElement('div');
div_SearchRequestWithSearchWords_Panel.style.cssText = `background-color:rgba(255,255,255,0.2); padding:10px;`;
let div_smallUserInfo_Top = crea_SMALL_USER_INFO();
document.body.appendChild(div_smallUserInfo_Top);
let div_goBottomButton = crea_GO_BOTTOM_BUTTON();
document.body.appendChild(div_goBottomButton);
let div_HomePage_button_Top = crea_HOME_BUTTON();
document.body.appendChild(div_HomePage_button_Top);
// Search panel -------------------------------------------------------------------------------------------------
//se esiste una precedente richiesta uso quella, altrimenti ne creo una nuova
if (!SearchRequestWithSearchWords_Current) { SearchRequestWithSearchWords_Current = new SearchRequestWithSearchWords(false) }
let mySearchPanelWithSearchWords = new SearchPanelWithSearchWords();
mySearchPanelWithSearchWords.crea_cSearchPanelWithSearchWords(SearchRequestWithSearchWords_Current, ResultOfSearchWithSearchWords_Current, undefined, UserInfo_Current, "block");
document.body.appendChild(mySearchPanelWithSearchWords.div_cSearchPanelWithSearchWords_Container);
// -------------------------------------------------------------------------------------------------------------
let div_smallUserInfo_bottom = crea_SMALL_USER_INFO();
document.body.appendChild(div_smallUserInfo_bottom);
let div_goTopButton = crea_GO_TOP_BUTTON();
document.body.appendChild(div_goTopButton);
let div_HomePage_button_bottom = crea_HOME_BUTTON();
document.body.appendChild(div_HomePage_button_bottom);
AggiungiAnimazioneBottoni();
which is quite elegant imho. You can see the point where the
new search control is used:
Code:
if (!SearchRequestWithSearchWords_Current) { SearchRequestWithSearchWords_Current = new SearchRequestWithSearchWords(false) }
let mySearchPanelWithSearchWords = new SearchPanelWithSearchWords();
mySearchPanelWithSearchWords.crea_cSearchPanelWithSearchWords(SearchRequestWithSearchWords_Current, ResultOfSearchWithSearchWords_Current, undefined, UserInfo_Current, "block");
document.body.appendChild(mySearchPanelWithSearchWords.div_cSearchPanelWithSearchWords_Container);
Note that this page (as the Search page) is also able to
reload both the request properties (search words, etc. ) and the results of previous searches if the users return to this page

They are in fact stored as
session objects on the server side and every time sent back to the client.
Now we can immediately use this general control also on the
User Profile page, where we can perform a search on the post of the current user under view:
With the occasion of this small refactoring, I am also adding the option to perform a "
like" (that is loose) or "
exact match" search (essentially
LIKE or
CHARINDEX/
InStr type functions, which are case insensitive, by default):
On the server side a function generating this piece of the SQL query could look like this (this is just the
WHERE part of the SELECT, relative to the search) [I am keeping into account possible
SQL dialects variants for "CHARINDEX", as I am envisioning multiple DB connectivity]:
Code:
public string CreaSQL_WHERE_PerRicercaParole(SearchRequestWithSearchWords SearchRequestWithSearchWords, string NomeCampoCercato)
{
if (SearchRequestWithSearchWords.ParoleCercate.Count < 1)
return null;
string OperatoreLogico;
if (SearchRequestWithSearchWords.RicercaParoleConOR)
{
OperatoreLogico = " OR ";
}
else
{
OperatoreLogico = " AND ";
}
bool UseLIKE = SearchRequestWithSearchWords.RicercaParoleConLIKE;
string SQL_Ricerca_WithAND;
if (UseLIKE)
{
string p = SearchRequestWithSearchWords.ParoleCercate(0);
SQL_Ricerca_WithAND = NomeCampoCercato + " LIKE '%" + p + "%' "; // case-insensitive search
for (int i = 1, loopTo = SearchRequestWithSearchWords.ParoleCercate.Count - 1; i <= loopTo; i++)
{
p = SearchRequestWithSearchWords.ParoleCercate(i);
SQL_Ricerca_WithAND += OperatoreLogico + NomeCampoCercato + " LIKE '%" + p + "%' ";
}
}
else
{
// CHARINDEX per SQL SERVER
string FunzioneSottostringaDi;
if (_USE_SQL_SERVER)
{
FunzioneSottostringaDi = "CHARINDEX"; // case-insensitive search
}
else
{
FunzioneSottostringaDi = "InStr";
} // case-insensitive search
string p = SearchRequestWithSearchWords.ParoleCercate(0);
SQL_Ricerca_WithAND = FunzioneSottostringaDi + "('" + p + "', " + NomeCampoCercato + ") > 0";
for (int i = 1, loopTo1 = SearchRequestWithSearchWords.ParoleCercate.Count - 1; i <= loopTo1; i++)
SQL_Ricerca_WithAND += OperatoreLogico + FunzioneSottostringaDi + "('" + p + "', " + NomeCampoCercato + ") > 0";
}
SQL_Ricerca_WithAND += " AND";
return SQL_Ricerca_WithAND;
}
Well, we have a starting foundation... and not very far from our
final goal ...

I hope we can go live perhaps around September (if am still on the planet) ... In the meantime, I will need to do a lot of code refactoring to make it more abstract and maintainable...