unknown
system
let bookInfo=null const pageData={}// probably should integrate into book data let variables={} function searchBook(){ fetch(`/feeds/posts/default?alt=json&label=chapter&v=2&orderby=relevance&max-results=100&q=label%3Achapter+${encodeURIComponent(tag("search").value)}&start-index=1&rewriteforssl=true`) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { //console.log(data); tag("search-results").replaceChildren() if(data.feed.entry){ for(entry of data.feed.entry){ buildChapterSearchResult(entry) } }else{ // add no results found message if needed const chapterResultDiv = document.createElement("div"); chapterResultDiv.appendChild(document.createTextNode("No Results Found")) tag("search-results").appendChild(chapterResultDiv) } }) } function findLink(links){ // takes a set of links from a blogger feed and returns the one with labeled "alternate" for(const link of links){ if(link.rel==='alternate'){ return link } } } function buildChapterSearchResult(entry){ //console.log(entry) const tempDiv = document.createElement("div"); tempDiv.innerHTML=entry.content.$t const chapterResultLink = document.createElement("a"); chapterResultLink.className="chapter-result-link" chapterResultLink.href=findLink(entry.link).href.split("/").pop() chapterResultLink.style.color="black" const chapterResultDiv = document.createElement("div"); chapterResultDiv.className="search-result" const resultTitleDiv = document.createElement("div"); resultTitleDiv.className = "search-result-title" resultTitleDiv.appendChild(document.createTextNode(entry.title.$t)) chapterResultDiv.appendChild(resultTitleDiv) const searchTerm = tag("search").value for(const result of findPhraseWithContext(tempDiv.innerText, searchTerm,5)){ console.log("result:",result) const regex = new RegExp(searchTerm, "gi"); const markedResult = result.replace(regex, `${searchTerm}`); const resultLineDiv = document.createElement("div"); resultLineDiv.className = "search-result-line" resultLineDiv.innerHTML = markedResult chapterResultDiv.appendChild(resultLineDiv) } chapterResultLink.appendChild(chapterResultDiv) tag("search-results").appendChild(chapterResultLink) } function entryHasLabel(entry, label){ for(const category of entry.category){ if(category.term===label){ return true } } return false } function findPhraseWithContext(text, phrase, contextCount = 10) { const words = text.split(/\s+/); const phraseWords = phrase.toLowerCase().split(/\s+/); const results = []; for (let i = 0; i <= words.length - phraseWords.length; i++) { // Check if the next sequence of words matches the phrase let match = true; for (let j = 0; j < phraseWords.length; j++) { const cleanWord = words[i + j].replace(/[^\w\s]/g, "").toLowerCase(); if (cleanWord !== phraseWords[j]) { match = false; break; } } if (match) { // Get context: 10 words before the phrase start, 10 words after the phrase end const start = Math.max(0, i - contextCount); const end = i + phraseWords.length + contextCount; const snippet = words.slice(start, end).join(" "); results.push(snippet); // Move index forward by phrase length to avoid overlapping sub-matches i += phraseWords.length - 1; } } return results; } function init(){ // bring in code that runs locally for debugging and testing if(location.hostname.startsWith("127.") || location.host.toLowerCase().startsWith("localhost")){ loadCrossOrigin(`${location.origin}/tools/localCode/dev.js`) }else{ // bring in the book info from the book post loadCrossOrigin(`${origin}/feeds/posts/default/-/book?alt=json-in-script&max-results=1&callback=initialize`); } } function initialize(bookInfoFeed){ bookInfo = JSON.parse(bookInfoFeed.feed.entry[0].content.$t) console.log("bookInfo",bookInfo) setVariables() buildMenu() configureBook() // set up searching the full content of book tag("search").addEventListener("keydown", function(event) { if (event.key === "Enter") { event.preventDefault(); searchBook(); } }); tag("search-button").addEventListener("click", searchBook) // Set a function onscroll - this will activate if the user scrolls //dims the buttons when the user scrolls window.onscroll = setDimness window.addEventListener('hashchange', function() { if(window.location.hash){ scroll_to(window.location.hash.substring(1)) } else { showSection(1) } }); window.addEventListener('resize', setTopMargin); setTopMargin() //console.log("hash", window.location.hash) if(window.location.hash){ scroll_to(window.location.hash.substring(1)) }else{ showSection(1) } } function configureBook(){ document.body.style.setProperty('--font-zoom', variables.fontZoom); } function setVariables(){ //read the variables from local storage. if not present create them and save to local storage const pathArray = window.location.pathname.split("/") variables.year=pathArray[1] variables.month=pathArray[2] const storedVariables = localStorage.getItem("book-settings") if(storedVariables===null){ // storedVariables do not yet exits variables.fontZoom=1 localStorage.setItem(`book-settings`,JSON.stringify(variables)) }else{ variables=JSON.parse(storedVariables) } //console.log("variables",variables) //console.log("storedVariables",storedVariables) } function setDimness() { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; //document.documentElement.scrollTop+document.documentElement.clientHeight,document.documentElement.scrollHeight if (scrollTop < 20 || Math.abs((scrollTop + clientHeight)-scrollHeight)<5) { dimButtons('bright') dimHeader('bright') } else { dimButtons('dim') dimHeader('dim') } //hideMenu() } function setTopMargin(){ const header = document.getElementsByTagName("header")[0] if(header){ const margin = header.offsetHeight for(section of document.querySelectorAll('.chapter-section')){ section.style.marginTop = `calc((${margin * 1.1}px * var(--font-zoom))` } } } function scroll_to(id, recordHash=true){ // Scroll to the specified element, being sure it is visible //console.log("scrollTo", id) hideMenu() let element = tag(id) if(!element){return} while (!element.className.includes('chapter-section')) { element = element.parentElement; if(!element){return} } showSection(element.id.split('-')[1],false) if(id !== element.id){ // this is not a section, scroll to it tag(id).scrollIntoView({ behavior: 'smooth', block: 'start' }); } if(recordHash){ window.location.hash = '#' + id } } function tag(id){ return document.getElementById(id) } function dimButtons(brightOrDim){ for(const button of document.querySelectorAll('.nav')){ if(brightOrDim === 'bright'){ button.classList.remove("dim-button") } else { // Dim the button button.classList.add("dim-button") } } } function dimHeader(brightOrDim){ for(const header of document.getElementsByTagName("header")){ if(brightOrDim === 'bright'){ header.classList.remove("dim-header") } else { // Dim the button header.classList.add("dim-header") } } } function showSection(section, recordHash=true){ // section can be a number or 'next' or 'prior' or 'all' let sectionsToHide = [] let sectionToShow = 1 let currentlyShowing = 0 let buttonNavigatgion = false const sections = document.querySelectorAll('.chapter-section') if(sections.length === 0){return} if(section === 'all'){ // not currently used or tested for(const elem of sections){ elem.style.display = 'block' } return } // find the section to hide for(const elem of sections){ if(elem.style.display !== 'none'){ currentlyShowing = parseInt(elem.id.split('-')[1]) //console.log("currentlyShowing", currentlyShowing) sectionsToHide.push(currentlyShowing) } } // find section to show if(isNaN(section)){ // section s string and should be 'next' or 'prior' buttonNavigatgion = true if(section === 'next'){ sectionToShow = currentlyShowing + 1 }else{ //section === 'prior' sectionToShow = currentlyShowing - 1 } }else{ // section numeric sectionToShow = section } // prevents sectionToShow from being out of bounds if (isNaN(sectionToShow)){return} if(sectionToShow < 1){ const components = window.location.pathname.split('/') priorChapter = parseInt(components[components.length - 1].split('.')[0]) - 1 if (priorChapter < 1){ window.location.href = 'toc.html' }else{ window.location.href = priorChapter + '.html' return } }else if(sectionToShow > sections.length){ // navigate to next chapter // needs to be updated to work with TOC, for now, it will guess the chapter number if(!pageData.bookend){ pageData.bookend = tag("page-data").dataset.bookend //console.log('tag("page-data").dataset.bookend',tag("page-data").dataset.bookend) } if(pageData.bookend==="true"){ message({text:"You have reached the end of this book. Thank you for using Availabooks.", title:"Book Over", buttons:[], seconds:8}) }else{ const components = window.location.pathname.split('/') nextChapter = parseInt(components[components.length - 1].split('.')[0]) + 1 window.location.href = nextChapter + '.html' } return } for(const sectionNumber of sectionsToHide ){ tag('section-' + sectionNumber).style.display = 'none' } tag('section-' + sectionToShow).style.display = 'block' if(sectionToShow===1){ window.scrollTo(0,0) }else{ window.scrollTo(0,25) if(recordHash){ window.location.hash = 'section-' + sectionToShow } } } function navigate(direction){ const path= location.pathname.replace(".","/").split("/") let nextNumber=null if(direction==="prior"){ nextNumber = parseInt(path[3])-1 if (nextNumber<1){ return// no where to go } }else{ nextNumber = parseInt(path[3])+1 if (nextNumber>window.lastChapterId){ return// no where to go } } path[3]=nextNumber+"."+path.pop() //console.log("I'm navigating",path.join("/")) window.location.href=path.join("/") return // assuming full chapter navigation // used for the navigation buttons. direction is 'next' or 'prior' targetNode = tag(direction + "-button") const parentNode = targetNode.parentNode const clonedElement = targetNode.cloneNode(true); targetNode.remove() if(direction === 'next'){ showSection('next') } else{ // direction === 'prior' showSection('prior') } parentNode.appendChild(clonedElement) } function showMenu(){ // show the menu //console.log("showing menu") let menuWidth=tag('menu').offsetWidth if (menuWidth === 0){ tag('menu').style.display = 'block' menuWidth=tag('menu').offsetWidth } tag('menu').style.left= '0' //console.log("menuWidth",menuWidth) // if(tag("menu-button").innerHTML === "close"){ // tag("menu-button").innerHTML="menu" // tag('menu').style.left= `-${menuWidth+10}px` // }else{ // tag("menu-button").innerHTML="close" // tag('menu').style.left= '0' // } } function hideMenu(){ let menuWidth=tag('menu').offsetWidth tag('menu').style.left= `-${menuWidth+10}px` } function copyThisPrompt(event){ //console.log("at copyThisPrompt") //showToast("I'm juicing!") const prior = event.target.previousElementSibling; if (!prior) return; navigator.clipboard.writeText(prior.innerText) .then(() => showToast("prompt for AI copied")) .catch(console.error); } function buildMenu(){ const html=[`