javascript: (function () { /* Config variables. */ /* Set search area, where 1 = use default selection, 0 = custom element. */ var autoSelectSearchPage = 1; /* For switch condition defines searchElementsSearchPage (0 = default). */ var searchTagsSearchPage = 0; /* 0 = default */ /******************************* var searchTagsSearchPage = 1 = h1 to h4 ||*loop siblings and show hide accordingly 2 = all elements with class || 3 = all p elements || 4 = all div with p elements || 5 >= ADD PER HTML SEMANTICS || default = all p, ul, ol *loop siblings and show hide accordingly ********************************/ /* Specify where to insert search elements relative to search area, using insertAdjacentElements first property. */ var insertWhereSearchPage = "beforebegin"; /* Config DOM Selector variables */ var querySelectorsSearchPage = /* loop and check if page has querySelectors */ [".container", ".container-fluid", "#main", "main", ".main"]; /* Config DOM Custom Selector variables. */ var customSelectorSearchPage = /* loop and check if page has querySelectors */ ["CHANGE"]; /* Auto-configuration variables. */ /* use switch below and var searchTagsSearchPage to specify nested items to search in. */ var searchElementsSearchPage; /* Define nested elements to search based on searchTagsSearchPage above. */ switch(searchTagsSearchPage) { case 1: searchElementsSearchPage = "h1,h2,h3,h4"; /* use nested headings */ break; case 2: searchElementsSearchPage = "*[class]"; /* all elements class */ break; case 3: searchElementsSearchPage = "p"; /* all p elements nested in area */ break; case 4: searchElementsSearchPage = "div p"; /* div nested in search area */ break; default: searchElementsSearchPage = /* all p or span in area */ "p, ul, ol"; } var selectHierarchySearchPage = /* config based on config variables */ (searchElementsSearchPage.search(/h[1-9]/g) > -1) ? 1 : 0; /* hierarchy or all elements */ /* Global variables -defined later. */ var markersSearchPage; /* select all elements with head tag in parSearchPage */ var parSearchPage; /* parent to recurse over */ var inputSearchPage; /* search input element */ var addedSearchPage; /* switch variable */ var parSelectedSearchPage = 0; /* swithch if user selects */ /************************************* SUPPORT FUNCTIONS *************************************/ /* Loop and check if page has query selector. */ const checkSelectorSearchPage = () => { let qArr; /* use query selectors pending autoSelectSearchPage value */ /* select which query selector array */ if (autoSelectSearchPage == 1) { qArr = querySelectorsSearchPage; } else { qArr = customSelectorSearchPage; } let qArrLen = qArr.length; /* used to compare length of found elements */ let compLen = 0; /* check if page has element and use first */ for (i = 0; i < qArrLen; i++) { let curCheck = document.querySelectorAll(qArr[i]); if (curCheck[0]) { /* use element with most children */ for (l = 0; l < curCheck.length; l++) { if (curCheck[l].querySelectorAll("*").length > compLen) { compLen = curCheck[l].querySelectorAll("*").length; parSearchPage = curCheck[l]; } } break; } else { continue; } } }; /* Allow user to selecct the area to search. */ const selectParSearchPage = () => { let selectedElement, lastHover; /* temp store last style to restore */ let resLastOutline = "", resLastCursor = "", resLastBorder = ""; /* add temp event */ let highlight = (el) => { /* remove old highlight */ if (lastHover) { lastHover.style.cursor = resLastCursor; lastHover.style.border = resLastBorder; } /* store prior styles */ if (el.style.border && el.style.border != "10px solid dodgerblue") resLastOutline = el.style.border; if (el.style.cursor && el.style.cursor != "pointer") resLastCursor = el.style.cursor; lastHover = el; /* redefine */ /* stylish highlight */ el.style.border = "10px solid dodgerblue"; el.style.cursor = "pointer"; }; /* mouse over possible search area */ let handleMouseOver = (el) => { el.stopPropagation(); highlight(el.target); }; /* use clicked element as search element */ let handleClick = (el) => { /* dont' do any predefined behavior */ el.preventDefault(); /* no action other than clicked element */ el.stopPropagation(); /* define and clean selection */ selectedElement = el.target; selectedElement.style.cursor = resLastCursor; /* define global for search */ parSearchPage = selectedElement; /* continue main function */ hasParSearchPage = 1; parSelectedSearchPage = 1; cleanup(); /* stop listening once selected */ }; /* remove temp events */ let cleanup = () => { document.removeEventListener("mouseover", handleMouseOver, true); document.removeEventListener("click", handleClick, true); /* remove mouse effect */ if (lastHover) { lastHover.style.border = resLastBorder; lastHover.style.cursor = resLastCursor; } }; /* Activate selection mode */ document.addEventListener("mouseover", handleMouseOver, true); document.addEventListener("click", handleClick, true); }; /* Check if parSearchPage, else start manual selection process. */ var hasParSearchPage = 0; /* 1 if parSearchPage is found */ var initAlertSearchPage = 0; /* prevent alert from repeating */ const checkParSearchPage = () => { if (parSearchPage) { hasParSearchPage = 1; /* query found element */ parSelectedSearchPage = 1; /* continue main function */ } else { if (initAlertSearchPage == 0) { alert(` SELECT MANUALLY: \n\n The default or custom selectors were not found on page. \n Hover, and select the area to be searched manually. `); initAlertSearchPage = 1; /* turn off alert */ } /* user select search parent */ selectParSearchPage(); } }; /* Add search input box to top of page. */ const addSearchPage = () => { if (hasParSearchPage == 1) { /* ensure par was found */ /* prevent duplicate search boxes */ let prevDup = /* use class name added on first run */ document.getElementsByClassName("bookmarkletDOMSearchSearchPage"); if (!prevDup[0]) { let par = document.createElement("div"); /* hold all search elements */ let sPar = document.createElement("div"); /* hold searc and clear */ let inp = document.createElement("input"); /* search */ let sh = document.createElement("span"); /* show hide search */ let clr = document.createElement("span"); /* clear search field */ /* set parent of search style */ par.style.zIndex = "100"; /* topmost - can be hidden, not too intrusive */ par.style.background = "white"; par.style.position = "fixed"; par.style.top = "90px"; par.style.padding = "10px"; par.style.width = "200px"; par.style.height = "50px"; par.style.border = "1px solid #9b9b9b"; par.style.borderRadius = "6px"; /* allow par top position to be adjusted. */ par.addEventListener("mousedown", (el) => { let startY, startTop; startY = el.clientY; /* current y position */ startTop = /* current style */ parseInt(window.getComputedStyle(par).top, 10); let onMouseMove = (el) => { let newTop = startTop + (el.clientY - startY); /* between 40 and 150 */ newTop = Math.max(40, Math.min(150, newTop)); par.style.top = newTop + "px"; }; /* remove events */ let onMouseUp = () => { document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); }; /* add temp events */ document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }); /* prevent duplicate search boxes */ par.setAttribute("class", "bookmarkletDOMSearchSearchPage"); /* set input attributes and style */ inp.type = "text"; inp.placeholder = "Search Page"; inp.style.display = "block"; inp.style.position = "fixed"; inp.style.width = "160px"; inp.style.marginBottom = "10px"; inp.style.marginLeft = "20px"; /* data-id to select if parent needs to be reselected */ inp.setAttribute("data-id", "bookmarkletDOMSearchSearchPage"); /* style clear button */ clr.style.zIndex = "1"; clr.style.position = "fixed"; clr.style.cursor = "pointer"; clr.style.marginLeft = "164px"; clr.style.marginTop = "1px"; clr.style.padding = "4px"; clr.style.background = "#d3d3d350"; clr.innerText = "-"; /* clear mouse effects */ clr.addEventListener("mouseover", () => { clr.style.background = "#d3d3d3"; }); clr.addEventListener("mouseout", () => { clr.style.background = "#d3d3d350"; }); /* clear search */ clr.addEventListener("click", ()=> { clr.previousElementSibling.value = ""; setTimeout(function() { /* delay a bit */ performSearchPage(); /* manual show all */ }, 100); }); /* set close button attributes and style */ sh.style.position = "fixed"; sh.style.cursor = "pointer"; sh.style.border = "1px solid black"; sh.style.padding = "3px"; sh.innerText = " x "; /* show/hide effects */ sh.addEventListener("mouseover", () => { sh.style.backgroundColor = "dodgerblue"; sh.style.color = "white"; }); sh.addEventListener("mouseout", () => { sh.style.backgroundColor = ""; sh.style.color = ""; }); /* click event */ sh.addEventListener("click", () => { if (!sPar) return; /* something unexpected */ if (sPar.style.display === "none") { sh.innerText = " x "; /* indicate click will hide */ par.style.width = "200px"; /* parent width */ sPar.style.display = ""; /* show search */ } else { sh.innerText = " o "; /* indicate click will show */ par.style.width = "37px"; /* parent width */ sPar.style.display = "none"; /* hide search */ } }); /* insert search before area to be searched */ /* config var, par created */ parSearchPage.insertAdjacentElement(insertWhereSearchPage, par); par.prepend(sh); /* show hide button */ sh.insertAdjacentElement("afterend", sPar); /* search and clear search parent */ sPar.prepend(inp); /* search input element */ inp.insertAdjacentElement("afterend", clr);/* clears search */ /* set focus on search box */ inp.focus(); } /* define heading elemnets from parSearchPage */ markersSearchPage = Array.from(parSearchPage.querySelectorAll(searchElementsSearchPage)); /* define search box from created input */ inputSearchPage = /* use data-id attribute added */ document.querySelector("[data-id='bookmarkletDOMSearchSearchPage'"); /* turn on condition to continue in main function */ addedSearchPage = 1; } else { addedSearchPage = 0; } }; /* Perform search on page. */ const performSearchPage = () => { let curVal = /* current search value formatted */ inputSearchPage.value.trim().toLowerCase(); /* loop over ids and see if siblings match */ let j = 0; /* increments heading markers */ let sibID = 0; /* switch - if sibling has less heading */ let firstIter = 0; /* ensure hVal gets defined */ let hFirstIter = 0; /* ensure hRank gets defined */ let hVal; /* declare variable to check heading values */ let lVal; /* store last h tag value */ let hRank; /* compare hiarchy of h tags */ let cRank; /* current h tag */ markersSearchPage.forEach((e, n) => { if (e.nextElementSibling && e.nextElementSibling.tagName.toLowerCase() == "nav") { return; } let defHRank = () => { let tH = 0; /* temp heading variable */ if (e.tagName.toLowerCase().search(/h[1-9]/g) > -1) tH = Number(e.tagName.substr(1,)); return tH; }; let curRank = () => { let tRank = 0; /* temp rank */ /* rank current h tags */ if (selectHierarchySearchPage == 1) { tRank = defHRank(); /* inline function for heading # */ } /* final return for variable */ return tRank }; cRank = curRank(); /* set cRank */ /* ensure last h tag is ranked */ if (hFirstIter == 0) { hFirstIter = 1; hRank = curRank(); /* store heading to check against next */ } /* define whether value matches search */ if (firstIter == 0 || sibID == 0) { if (firstIter == 1) { lVal = hVal; } hVal = /* get value formatted of heading and check search */ e.innerText.toLowerCase().includes(curVal); if (firstIter == 0) { firstIter = 1; lVal = hVal; } } j = /* next element with heading tag name attribute */ markersSearchPage[n + 1] || null; /* get the elements following heading */ let el = e.nextElementSibling; /* check if next element has id, skip if so */ if (cRank < hRank) { hRank = curRank(); /* store heading to check against next */ sibID = 1; } else { /* h tags equal and use h hierarchy */ if (cRank == hRank && selectHierarchySearchPage == 1) { hRank = curRank(); /* store heading to check against next */ sibID = 1; } else { sibID = 0; } /* show or hide pending search */ e.style.display = hVal ? "" : "none"; /* loop following elements and show/hide according to search */ if (selectHierarchySearchPage == 1 || searchTagsSearchPage == 0) { while (el && el != j) { /* exists and not next heading */ el.style.display = hVal ? "" : "none"; el = el.nextElementSibling; } } } }); }; /* Run time out with function as parameter. */ const runSupportSearchPage = (cur) => { setTimeout(function() { cur(); }, 100); }; /********************************************************************************************* MAIN FUNCTION *********************************************************************************************/ /* Run each support function and add event listener. */ var stopRecurSearchPage = 0; /* stop recursing after 20-ish seconds */ function domSearchPage() { if (parSelectedSearchPage == 0) { /* check parent with elements for search */ runSupportSearchPage(checkSelectorSearchPage); } /* if parent for search continue, manual select */ runSupportSearchPage(checkParSearchPage); /* create search input box above parent */ runSupportSearchPage(addSearchPage); setTimeout( function() { /* Check if found and input field, then continue */ if (addedSearchPage == 1) { /* add event to search page */ inputSearchPage .addEventListener("input", performSearchPage); } else { stopRecurSearchPage++; if (stopRecurSearchPage < 20) { domSearchPage(); /* recurse - probably selecting */ } else { let skip; /* do nothing */ } } }, 1000); } /* Call main function. */ domSearchPage(); })();