\r\n The list was moved to GitHub by Victor Felder for collaborative updating and maintenance. It has grown to become\r\n one of GitHub’s most popular repositories, with 221,000+ stars,\r\n 6,900+ commits, 1,900+ contributors, and 47,100+ forks.\r\n
\r\n\r\n
\r\n The Free Ebook Foundation now administers the repo, a not-for-profit\r\n organization devoted to promoting the creation, distribution, archiving, and sustainability of free ebooks.{\" \"}\r\n Donations to the Free Ebook Foundation are\r\n tax-deductible in the US.\r\n
\r\n Each file included in this repository is licensed under the{\" \"}\r\n CC BY License.\r\n
\r\n \r\n );\r\n}\r\n\r\nexport default Default;\r\n","import React, { useState, useEffect } from \"react\";\r\nimport axios from \"axios\";\r\nimport Fuse from \"fuse.js\";\r\n\r\nimport LangDropdown from \"./components/LangDropdown\";\r\nimport SearchBar from \"./components/SearchBar\";\r\nimport SearchResult from \"./components/SearchResult\";\r\nimport LightSwitch from \"./components/LightSwitch\";\r\nimport Default from \"./components/Default\";\r\n\r\nimport SunImg from \"./img/sun.png\";\r\nimport MoonImg from \"./img/moon.png\";\r\n\r\nconst fpb = null;\r\n\r\n// eslint-disable-next-line\r\nfunction makeBook(author, hLang, cLang, title, url) {\r\n //returns a struct with basic book info (author, human language, computer language, book title, url)\r\n return {\r\n author: author,\r\n hLang: hLang, //human language\r\n cLang: cLang, //computer language\r\n title: title,\r\n url: url,\r\n };\r\n}\r\n\r\n// eslint-disable-next-line\r\nfunction forEachBook(func, json) {\r\n //Runs func on each section, entry, and book in json, which is a list of entries\r\n if (typeof func !== \"function\") {\r\n // eslint-disable-next-line\r\n throw \"ERROR in forEachBook: parameter not a fucntion\";\r\n }\r\n\r\n for (const hLang in json) {\r\n //for each human language\r\n if (Array.isArray(hLang.sections)) {\r\n //check if sections is an array\r\n hLang.sections.forEach(\r\n (\r\n cLang //for each computer lanuage\r\n ) => {\r\n if (Array.isArray(cLang.entries)) {\r\n //verify is entries is an array\r\n cLang.entries.forEach(\r\n (\r\n book //for each book\r\n ) => {\r\n if (typeof book === \"object\") {\r\n //verify that book is an object\r\n func(json[hLang], cLang, book); //run the function\r\n }\r\n }\r\n );\r\n }\r\n }\r\n );\r\n }\r\n }\r\n}\r\n\r\n// Sorts search results by their score\r\n// eslint-disable-next-line\r\nfunction sortByScore(results) {\r\n results.sort(function (a, b) {\r\n return a.score - b.score;\r\n });\r\n return results;\r\n}\r\n\r\nfunction jsonToArray(json) {\r\n let arr = [];\r\n let sections = [];\r\n json.children[0].children.forEach((document) => {\r\n document.sections.forEach((section) => {\r\n if (!sections.includes(section.section)) sections.push(section.section);\r\n section.entries.forEach((entry) => {\r\n arr.push({\r\n author: entry.author,\r\n title: entry.title,\r\n url: entry.url,\r\n lang: document.language,\r\n section: section.section,\r\n });\r\n });\r\n section.subsections.forEach((subsection) => {\r\n subsection.entries.forEach((entry) => {\r\n arr.push({\r\n author: entry.author,\r\n title: entry.title,\r\n url: entry.url,\r\n lang: document.language,\r\n section: section.section,\r\n subsection: subsection.section,\r\n });\r\n });\r\n });\r\n });\r\n });\r\n return { arr: arr, sections: sections };\r\n}\r\n\r\nfunction App() {\r\n const [data, setData] = useState(undefined); // keeps the state of the json\r\n const [dataArray, setDataArray] = useState([]); // put everything into one array. uses more memory, but search is faster and less complex\r\n // eslint-disable-next-line\r\n const [index, setIndex] = useState([]); // used for \"table of contents\". currently unused\r\n const [loading, setLoading] = useState(true); // Determines whether to show spinner\r\n const [searchParams, setSearchParams] = useState({ title: \"\" });\r\n const [searchResults, setSearchResults] = useState([]);\r\n const [sectionResults, setSectionResults] = useState([]);\r\n const [lightMode, setLightMode] = useState(true);\r\n\r\n // eslint-disable-next-line\r\n const [error, setError] = useState(\"\");\r\n\r\n let resultsList = null; // the html string containing the search results\r\n let sectionResultsList = null;\r\n\r\n const changeParameter = (param, value) => {\r\n // Lets a child component set the value of the search term\r\n setSearchParams({ ...searchParams, [param]: value });\r\n };\r\n\r\n // fetches data the first time the page renders\r\n useEffect(() => {\r\n async function fetchData() {\r\n try {\r\n setLoading(true);\r\n let result = await axios.get(\r\n \"https://raw.githubusercontent.com/FreeEbookFoundationBot/free-programming-books-json/main/fpb.json\"\r\n );\r\n setData(result.data);\r\n let { arr, sections } = jsonToArray(result.data);\r\n setDataArray(arr);\r\n setIndex(sections);\r\n } catch (e) {\r\n // setError(\"Couldn't get data. Please try again later\")\r\n setData(fpb);\r\n let { arr, sections } = jsonToArray(fpb);\r\n setIndex(sections);\r\n setDataArray(arr);\r\n }\r\n setLoading(false);\r\n }\r\n fetchData();\r\n }, []);\r\n\r\n // fires when searchTerm changes\r\n // THIS IS THE MAIN SEARCH FUNCTION CURRENTLY\r\n useEffect(() => {\r\n if (dataArray) {\r\n // Finds most relevant titles\r\n const fuseOptions = {\r\n useExtendedSearch: true,\r\n findAllMatches: true,\r\n shouldSort: true,\r\n includeScore: true,\r\n threshold: 0.2,\r\n keys: [\"title\", \"lang.code\"],\r\n };\r\n\r\n let fuse = new Fuse(dataArray, fuseOptions);\r\n let query = [];\r\n for (const [key, value] of Object.entries(searchParams)) {\r\n if (value === null || value === \"\") continue;\r\n if (key === \"lang.code\") {\r\n query.push({ \"lang.code\": `^${value}` });\r\n continue;\r\n }\r\n query.push({ [key]: value });\r\n }\r\n let result = fuse.search({\r\n $and: query,\r\n });\r\n result = result.slice(0, 40);\r\n setSearchResults(result);\r\n\r\n let sResults = []; // section results\r\n // Finds the most relevant sections\r\n result.forEach((entry) => {\r\n let section = entry.item.section;\r\n if (!sResults.includes(section)) sResults.push(section);\r\n });\r\n setSectionResults(sResults);\r\n }\r\n }, [searchParams, dataArray]);\r\n\r\n if (loading) {\r\n // if still fetching resource\r\n return
\r\n The list was moved to GitHub by Victor Felder for collaborative updating and maintenance. It has grown to become\r\n one of GitHub’s most popular repositories, with 221,000+ stars,\r\n 6,900+ commits, 1,900+ contributors, and 47,100+ forks.\r\n
\r\n\r\n
\r\n The Free Ebook Foundation now administers the repo, a not-for-profit\r\n organization devoted to promoting the creation, distribution, archiving, and sustainability of free ebooks.{\" \"}\r\n Donations to the Free Ebook Foundation are\r\n tax-deductible in the US.\r\n
\r\n Each file included in this repository is licensed under the{\" \"}\r\n CC BY License.\r\n
\r\n \r\n );\r\n}\r\n\r\nexport default Default;\r\n","import React, { useState, useEffect } from \"react\";\r\nimport axios from \"axios\";\r\nimport Fuse from \"fuse.js\";\r\n\r\nimport LangDropdown from \"./components/LangDropdown\";\r\nimport SectDropdown from \"./components/SectDropdown\";\r\nimport SearchBar from \"./components/SearchBar\";\r\nimport SearchResult from \"./components/SearchResult\";\r\nimport LightSwitch from \"./components/LightSwitch\";\r\nimport Default from \"./components/Default\";\r\n\r\nimport SunImg from \"./img/sun.png\";\r\nimport MoonImg from \"./img/moon.png\";\r\n\r\nconst fpb = null;\r\n\r\n// eslint-disable-next-line\r\nfunction makeBook(author, hLang, cLang, title, url) {\r\n //returns a struct with basic book info (author, human language, computer language, book title, url)\r\n return {\r\n author: author,\r\n hLang: hLang, //human language\r\n cLang: cLang, //computer language\r\n title: title,\r\n url: url,\r\n };\r\n}\r\n\r\n// eslint-disable-next-line\r\nfunction forEachBook(func, json) {\r\n //Runs func on each section, entry, and book in json, which is a list of entries\r\n if (typeof func !== \"function\") {\r\n // eslint-disable-next-line\r\n throw \"ERROR in forEachBook: parameter not a fucntion\";\r\n }\r\n\r\n for (const hLang in json) {\r\n //for each human language\r\n if (Array.isArray(hLang.sections)) {\r\n //check if sections is an array\r\n hLang.sections.forEach(\r\n (\r\n cLang //for each computer lanuage\r\n ) => {\r\n if (Array.isArray(cLang.entries)) {\r\n //verify is entries is an array\r\n cLang.entries.forEach(\r\n (\r\n book //for each book\r\n ) => {\r\n if (typeof book === \"object\") {\r\n //verify that book is an object\r\n func(json[hLang], cLang, book); //run the function\r\n }\r\n }\r\n );\r\n }\r\n }\r\n );\r\n }\r\n }\r\n}\r\n\r\n// Sorts search results by their score\r\n// eslint-disable-next-line\r\nfunction sortByScore(results) {\r\n results.sort(function (a, b) {\r\n return a.score - b.score;\r\n });\r\n return results;\r\n}\r\n\r\nfunction jsonToArray(json) {\r\n let arr = [];\r\n let sections = [];\r\n json.children[0].children.forEach((document) => {\r\n document.sections.forEach((section) => {\r\n if (!sections.includes(section.section)) sections.push(section.section);\r\n section.entries.forEach((entry) => {\r\n arr.push({\r\n author: entry.author,\r\n title: entry.title,\r\n url: entry.url,\r\n lang: document.language,\r\n section: section.section,\r\n });\r\n });\r\n section.subsections.forEach((subsection) => {\r\n subsection.entries.forEach((entry) => {\r\n arr.push({\r\n author: entry.author,\r\n title: entry.title,\r\n url: entry.url,\r\n lang: document.language,\r\n section: section.section,\r\n subsection: subsection.section,\r\n });\r\n });\r\n });\r\n });\r\n });\r\n return { arr: arr, sections: sections };\r\n}\r\n\r\nfunction App() {\r\n const [data, setData] = useState(undefined); // keeps the state of the json\r\n const [dataArray, setDataArray] = useState([]); // put everything into one array. uses more memory, but search is faster and less complex\r\n // eslint-disable-next-line\r\n const [index, setIndex] = useState([]); // used for \"table of contents\". currently unused\r\n const [loading, setLoading] = useState(true); // Determines whether to show spinner\r\n const [searchParams, setSearchParams] = useState({ title: \"\" });\r\n const [searchResults, setSearchResults] = useState([]);\r\n const [sectionResults, setSectionResults] = useState([]);\r\n const [lightMode, setLightMode] = useState(true);\r\n\r\n // eslint-disable-next-line\r\n const [error, setError] = useState(\"\");\r\n\r\n let resultsList = null; // the html string containing the search results\r\n let sectionResultsList = null;\r\n\r\n const changeParameter = (param, value) => {\r\n // Lets a child component set the value of the search term\r\n setSearchParams({ ...searchParams, [param]: value });\r\n };\r\n\r\n // fetches data the first time the page renders\r\n useEffect(() => {\r\n async function fetchData() {\r\n try {\r\n setLoading(true);\r\n let result = await axios.get(\r\n \"https://raw.githubusercontent.com/FreeEbookFoundationBot/free-programming-books-json/main/fpb.json\"\r\n );\r\n setData(result.data);\r\n let { arr, sections } = jsonToArray(result.data);\r\n console.log(arr);\r\n setDataArray(arr);\r\n setIndex(sections);\r\n } catch (e) {\r\n // setError(\"Couldn't get data. Please try again later\")\r\n setData(fpb);\r\n let { arr, sections } = jsonToArray(fpb);\r\n setIndex(sections);\r\n setDataArray(arr);\r\n }\r\n setLoading(false);\r\n }\r\n fetchData();\r\n }, []);\r\n\r\n // fires when searchTerm changes\r\n // THIS IS THE MAIN SEARCH FUNCTION CURRENTLY\r\n useEffect(() => {\r\n if (dataArray) {\r\n // Finds most relevant titles\r\n const fuseOptions = {\r\n useExtendedSearch: true,\r\n findAllMatches: true,\r\n shouldSort: true,\r\n includeScore: true,\r\n threshold: 0.2,\r\n keys: [\"title\", \"lang.code\", \"section\"],\r\n };\r\n\r\n let fuse = new Fuse(dataArray, fuseOptions);\r\n let query = [];\r\n for (const [key, value] of Object.entries(searchParams)) {\r\n if (value === null || value === \"\") continue;\r\n if (key === \"lang.code\") {\r\n query.push({ \"lang.code\": `^${value}` });\r\n continue;\r\n }\r\n if (key === \"section\"){\r\n query.push({\"section\": `^${value}`});\r\n continue;\r\n }\r\n query.push({ [key]: value });\r\n }\r\n let result = fuse.search({\r\n $and: query,\r\n });\r\n result = result.slice(0, 40);\r\n setSearchResults(result);\r\n\r\n let sResults = []; // section results\r\n // Finds the most relevant sections\r\n result.forEach((entry) => {\r\n let section = entry.item.section;\r\n if (!sResults.includes(section)) sResults.push(section);\r\n });\r\n setSectionResults(sResults);\r\n }\r\n }, [searchParams, dataArray]);\r\n\r\n if (loading) {\r\n // if still fetching resource\r\n return
\r\n );\r\n}\r\n\r\nexport default App;\r\n","export default __webpack_public_path__ + \"static/media/sun.d499a97b.png\";","export default __webpack_public_path__ + \"static/media/moon.6ad80c47.png\";","const reportWebVitals = onPerfEntry => {\r\n if (onPerfEntry && onPerfEntry instanceof Function) {\r\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\r\n getCLS(onPerfEntry);\r\n getFID(onPerfEntry);\r\n getFCP(onPerfEntry);\r\n getLCP(onPerfEntry);\r\n getTTFB(onPerfEntry);\r\n });\r\n }\r\n};\r\n\r\nexport default reportWebVitals;\r\n","import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\nimport './App.css';\r\nimport App from './App';\r\nimport reportWebVitals from './reportWebVitals';\r\n\r\nReactDOM.render(\r\n \r\n \r\n ,\r\n document.getElementById('root')\r\n);\r\n\r\n// If you want to start measuring performance in your app, pass a function\r\n// to log results (for example: reportWebVitals(console.log))\r\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\r\nreportWebVitals();\r\n"],"sourceRoot":""}
\ No newline at end of file