Update 5 files
- /assets/index-0e5924d3.css - /app/index.js - /app/index.html - /index.html - /assets/style.css
This commit is contained in:
		
							parent
							
								
									16100dc229
								
							
						
					
					
						commit
						a7059d3929
					
				
					 4 changed files with 657 additions and 444 deletions
				
			
		
							
								
								
									
										477
									
								
								app/index.html
									
										
									
									
									
								
							
							
						
						
									
										477
									
								
								app/index.html
									
										
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										552
									
								
								app/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										552
									
								
								app/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,552 @@ | ||||||
|  | let questionid=""; | ||||||
|  | window.onload = () => resetState(); | ||||||
|  | document.addEventListener('DOMContentLoaded', () => { | ||||||
|  |     resetState(); | ||||||
|  |     jsonDataFetched = false; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function toggleModal(){document.getElementById("modal").classList.toggle("hidden")} | ||||||
|  | function toggleMS(){document.getElementById("markscheme").classList.toggle("hidden")} | ||||||
|  | function toggleR(){document.getElementById("report").classList.toggle("hidden")} | ||||||
|  | function toggleHelp(){document.getElementById("helpmenu").classList.toggle("hidden")} | ||||||
|  | function toggleDownAllQs(){document.getElementById("addalltoPDFbtn").classList.remove('hidden');document.getElementById("generatePDFbtn").classList.remove('hidden')} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function toggleDarkMode() { | ||||||
|  |     var body = document.body; | ||||||
|  |     var head = document.head; | ||||||
|  |     var toggleButton = document.getElementById("darkmodebtn"); | ||||||
|  |      | ||||||
|  |     if (localStorage.getItem("darkMode") === "disabled") { | ||||||
|  |         body.classList.add("dark-mode"); | ||||||
|  |         head.classList.add("dark-mode"); | ||||||
|  |         localStorage.setItem("darkMode", "enabled"); | ||||||
|  |         toggleButton.innerText = "Light Mode"; | ||||||
|  |     } else { | ||||||
|  |         body.classList.remove("dark-mode"); | ||||||
|  |         head.classList.remove("dark-mode"); | ||||||
|  |         localStorage.setItem("darkMode", "disabled"); | ||||||
|  |         toggleButton.innerText = "Dark Mode"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | document.addEventListener("DOMContentLoaded", () => { | ||||||
|  |     const darkModeStatus = localStorage.getItem("darkMode"); | ||||||
|  |     const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)").matches; | ||||||
|  | 
 | ||||||
|  |     if (darkModeStatus === "enabled") { | ||||||
|  |         document.body.classList.add("dark-mode"); | ||||||
|  |         document.head.classList.add("dark-mode"); | ||||||
|  |         document.getElementById("darkmodebtn").innerText = "Light Mode"; | ||||||
|  |     } else if (darkModeStatus === "disabled") { | ||||||
|  |         document.body.classList.remove("dark-mode"); | ||||||
|  |         document.head.classList.remove("dark-mode"); | ||||||
|  |         document.getElementById("darkmodebtn").innerText = "Dark Mode"; | ||||||
|  |     } else if (prefersDarkScheme) { | ||||||
|  |         document.body.classList.add("dark-mode"); | ||||||
|  |         document.head.classList.add("dark-mode"); | ||||||
|  |         document.getElementById("darkmodebtn").innerText = "Light Mode"; | ||||||
|  |         localStorage.setItem("darkMode", "enabled"); | ||||||
|  |     } else { | ||||||
|  |         document.body.classList.remove("dark-mode"); | ||||||
|  |         document.head.classList.remove("dark-mode"); | ||||||
|  |         document.getElementById("darkmodebtn").innerText = "Dark Mode"; | ||||||
|  |         localStorage.setItem("darkMode", "disabled"); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function startRandomTimerLoop() { | ||||||
|  |     var randomTime = Math.floor(11 * Math.random()) + 20; | ||||||
|  |     setTimeout(() => { | ||||||
|  |         alert("Provided by pirateIB\nhttps://pirateib.xyz"); | ||||||
|  |         startRandomTimerLoop(); | ||||||
|  |     }, 60 * randomTime * 1000); | ||||||
|  | } | ||||||
|  | window.onload = function() { | ||||||
|  |     setTimeout(() => { | ||||||
|  |         startRandomTimerLoop(); | ||||||
|  |     }, 300000); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*function generatePDF() { | ||||||
|  |   const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || []; | ||||||
|  |   if (0 === selectedQuestionIds.length) return alert("Select some questions first!"); | ||||||
|  |   const printWindow = window.open("", "_blank"); | ||||||
|  |   printWindow.document.write('\n <html>\n <head>\n <title>QuestionBank Test</title>\n <link rel="stylesheet" href="../assets/style.css">\n <style>\n @media print { body { overflow: hidden; } ::-webkit-scrollbar { display: none; } }\n </style>\n </head>\n <body>\n '); | ||||||
|  |   let concatenatedHTML = ""; | ||||||
|  |   let markschemesHTML = ''; | ||||||
|  |   selectedQuestionIds.forEach((questionId => { | ||||||
|  |     const questionDiv = document.getElementById(questionId), | ||||||
|  |       h3 = questionDiv.querySelector("h3"), | ||||||
|  |       squareContainer = questionDiv.querySelector(".square-container"); | ||||||
|  |     concatenatedHTML += h3.outerHTML + squareContainer.outerHTML | ||||||
|  |     const msDiv = document.querySelector(`[id*="markscheme-${questionId}"]`); | ||||||
|  |     const cloneMsDiv = msDiv.cloneNode(true); | ||||||
|  |     cloneMsDiv.classList.remove('hidden'); | ||||||
|  |     markschemesHTML += h3.outerHTML + cloneMsDiv.outerHTML; | ||||||
|  |   })), printWindow.document.write(`<h2>Questions</h2><br>${concatenatedHTML}<div style="page-break-after: always;"></div><h2>Markschemes</h2><br>${markschemesHTML}`), printWindow.document.write("\n </body>\n </html>\n "), printWindow.document.close(); | ||||||
|  |   setTimeout(() => { | ||||||
|  |     printWindow.print(), printWindow.onafterprint = () => printWindow.close(); | ||||||
|  |   }, 1000);}*/ | ||||||
|  | 
 | ||||||
|  | function generatePDF() { | ||||||
|  |   const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || []; | ||||||
|  |   if (0 === selectedQuestionIds.length) return alert("Select some questions first!"); | ||||||
|  | 
 | ||||||
|  |   // Prompt the user for including markschemes
 | ||||||
|  |   let includeMarkschemes = null; | ||||||
|  |   while (includeMarkschemes !== "yes" && includeMarkschemes !== "no") { | ||||||
|  |     includeMarkschemes = prompt("Do you want to include markschemes? (yes/no)").toLowerCase(); | ||||||
|  |     if (includeMarkschemes !== "yes" && includeMarkschemes !== "no") { | ||||||
|  |       alert("Please answer 'yes' or 'no'."); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const printWindow = window.open("", "_blank"); | ||||||
|  |   printWindow.document.write('\n <html>\n <head>\n <title>QuestionBank Test</title>\n <link rel="stylesheet" href="../assets/style.css">\n <style>\n @media print { body { overflow: hidden; } ::-webkit-scrollbar { display: none; } }\n </style>\n </head>\n <body>\n '); | ||||||
|  | 
 | ||||||
|  |   let concatenatedHTML = ""; | ||||||
|  |   let markschemesHTML = ''; | ||||||
|  | 
 | ||||||
|  |   selectedQuestionIds.forEach((questionId) => { | ||||||
|  |     const questionDiv = document.getElementById(questionId), | ||||||
|  |       h3 = questionDiv.querySelector("h3"), | ||||||
|  |       squareContainer = questionDiv.querySelector(".square-container"); | ||||||
|  |     concatenatedHTML += h3.outerHTML + squareContainer.outerHTML; | ||||||
|  | 
 | ||||||
|  |     if (includeMarkschemes === "yes") { | ||||||
|  |       const msDiv = document.querySelector(`[id*="markscheme-${questionId}"]`); | ||||||
|  |       const cloneMsDiv = msDiv.cloneNode(true); | ||||||
|  |       cloneMsDiv.classList.remove('hidden'); | ||||||
|  |       markschemesHTML += h3.outerHTML + cloneMsDiv.outerHTML; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   printWindow.document.write(`<h2>Questions</h2><br>${concatenatedHTML}`); | ||||||
|  |    | ||||||
|  |   if (includeMarkschemes === "yes") { | ||||||
|  |     printWindow.document.write(`<div style="page-break-after: always;"></div><h2>Markschemes</h2><br>${markschemesHTML}`); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   printWindow.document.write("\n </body>\n </html>\n "); | ||||||
|  |   printWindow.document.close(); | ||||||
|  | 
 | ||||||
|  |   setTimeout(() => { | ||||||
|  |     printWindow.print(); | ||||||
|  |     printWindow.onafterprint = () => printWindow.close(); | ||||||
|  |   }, 1000); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function addalltoPDF() { | ||||||
|  |   const buttons = document.querySelectorAll('.btn-secondary'); | ||||||
|  |   buttons.forEach(button => { | ||||||
|  |       if (button.textContent.trim() === "Add to PDF" && !button.parentElement.parentElement.classList.contains('hidden')) { | ||||||
|  |           button.click(); | ||||||
|  |       } | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | let jsonDataFetched = false; | ||||||
|  | let jsonData = null; | ||||||
|  | let currentFileName = null; | ||||||
|  | let topics = []; | ||||||
|  | 
 | ||||||
|  | const domCache = { | ||||||
|  |   rightCol: document.getElementById("right-col"), | ||||||
|  |   msbox: document.getElementById("markscheme-box"), | ||||||
|  |   reportbox: document.getElementById("report-box"), | ||||||
|  |   msbox2: document.getElementById("markscheme-box2"), | ||||||
|  |   repbox2: document.getElementById("report-box2"), | ||||||
|  |   leftCol: document.getElementById('left-col') | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | document.addEventListener('keydown', (event) => { | ||||||
|  | if (event.key === 'Escape') { | ||||||
|  |   const modal = document.getElementById('modal'); | ||||||
|  |   const helpmenu = document.getElementById('helpmenu'); | ||||||
|  |   if (modal && !modal.classList.contains('hidden')) { | ||||||
|  |     toggleModal(); | ||||||
|  |   } else if (helpmenu && !helpmenu.classList.contains('hidden'))  { | ||||||
|  |     toggleHelp(); | ||||||
|  |   } | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const fileNameMap = { | ||||||
|  |   'bioqb': 'Biology QB.json', | ||||||
|  |   'bmqb': 'Business Management QB.json', | ||||||
|  |   'chemqb': 'Chemistry QB.json', | ||||||
|  |   'compsciqb': 'Computer Science QB.json', | ||||||
|  |   'destechqb': 'Design Technology QB.json', | ||||||
|  |   'digsocqb': 'Digital Society QB.json', | ||||||
|  |   'econqb': 'Economics QB.json', | ||||||
|  |   'essqb': 'ESS QB.json', | ||||||
|  |   'geoqb': 'Geography QB.json', | ||||||
|  |   'histqb': 'History QB.json', | ||||||
|  |   'mathaaqb': 'Math AA QB.json', | ||||||
|  |   'mathaiqb': 'Math AI QB.json', | ||||||
|  |   'phyqb': 'Physics QB.json', | ||||||
|  |   'psychqb': 'Psychology QB.json', | ||||||
|  |   'sehsqb': 'SEHS QB.json' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function createSVGElement(questionid) { | ||||||
|  |   const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); | ||||||
|  |   const attributes = { | ||||||
|  |     "width": "2rem", | ||||||
|  |     "height": "2rem", | ||||||
|  |     "viewBox": "0 0 24 24", | ||||||
|  |     "fill": "none", | ||||||
|  |     "stroke": "currentColor", | ||||||
|  |     "stroke-width": "2", | ||||||
|  |     "stroke-linecap": "round", | ||||||
|  |     "stroke-linejoin": "round" | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   Object.entries(attributes).forEach(([key, value]) => { | ||||||
|  |     svg.setAttribute(key, value); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   svg.classList.add("cursor-pointer", "text-primary", "hidden"); | ||||||
|  |   svg.innerHTML = ` | ||||||
|  |         <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect> | ||||||
|  |         <line x1="9" y1="9" x2="15" y2="15"></line> | ||||||
|  |         <line x1="15" y1="9" x2="9" y2="15"></line> | ||||||
|  |     `;
 | ||||||
|  |   return svg; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function showLoading() { | ||||||
|  |     document.getElementById('loadingBanner').style.display = 'block'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function hideLoading() { | ||||||
|  |     document.getElementById('loadingBanner').style.display = 'none'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function openDatabase() { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |         const request = indexedDB.open('myDatabase', 1); | ||||||
|  | 
 | ||||||
|  |         request.onupgradeneeded = (event) => { | ||||||
|  |             const db = event.target.result; | ||||||
|  |             if (!db.objectStoreNames.contains('myStore')) { | ||||||
|  |                 db.createObjectStore('myStore'); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         request.onsuccess = (event) => { | ||||||
|  |             resolve(event.target.result); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         request.onerror = (event) => { | ||||||
|  |             reject('Error opening database:', event.target.error); | ||||||
|  |         }; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function loadJSON(filename) { | ||||||
|  |     showLoading();  | ||||||
|  |     const db = await openDatabase(); | ||||||
|  | 
 | ||||||
|  |     const cachedData = await new Promise((resolve, reject) => { | ||||||
|  |         const transaction = db.transaction('myStore', 'readonly'); | ||||||
|  |         const store = transaction.objectStore('myStore'); | ||||||
|  |         const request = store.get(filename); | ||||||
|  | 
 | ||||||
|  |         request.onsuccess = (event) => { | ||||||
|  |             resolve(event.target.result); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         request.onerror = (event) => { | ||||||
|  |             reject('Error fetching data from IndexedDB:', event.target.error); | ||||||
|  |         }; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (cachedData) { | ||||||
|  |         hideLoading(); | ||||||
|  |         processData(cachedData, filename); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |         const response = await fetch(`https://pub-59370068cd854c158959e7ca4578e5bd.r2.dev/${filename}`); | ||||||
|  |         if (!response.ok) { | ||||||
|  |             throw new Error('Network response was not ok'); | ||||||
|  |         } | ||||||
|  |         const data = await response.json(); | ||||||
|  | 
 | ||||||
|  |         await new Promise((resolve, reject) => { | ||||||
|  |             const transaction = db.transaction('myStore', 'readwrite'); | ||||||
|  |             const store = transaction.objectStore('myStore'); | ||||||
|  |             const request = store.put(data, filename); | ||||||
|  | 
 | ||||||
|  |             request.onsuccess = () => { | ||||||
|  |                 resolve(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             request.onerror = (event) => { | ||||||
|  |                 reject('Error storing data in IndexedDB:', event.target.error); | ||||||
|  |             }; | ||||||
|  |         }); | ||||||
|  |         sessionStorage.setItem('selectedQuestionIds', '[]'); | ||||||
|  | 
 | ||||||
|  |         setTimeout(() => { | ||||||
|  |             processData(data, filename); | ||||||
|  |             hideLoading(); | ||||||
|  |             sessionStorage.setItem('selectedQuestionIds', '[]'); | ||||||
|  |         }, 0); | ||||||
|  | 
 | ||||||
|  |     } catch (error) { | ||||||
|  |         console.error('Error fetching JSON:', error); | ||||||
|  |         hideLoading(); | ||||||
|  |         sessionStorage.setItem('selectedQuestionIds', '[]'); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function processData(data, filename) { | ||||||
|  |       jsonDataFetched = true; | ||||||
|  |       currentFileName = filename; | ||||||
|  | 
 | ||||||
|  |       topics = [...new Set(data.flatMap(item => item.topics))].sort(); | ||||||
|  |       renderTopics(); | ||||||
|  | 
 | ||||||
|  |       const fragment = document.createDocumentFragment(); | ||||||
|  | 
 | ||||||
|  |       data.forEach(item => { | ||||||
|  |         const { | ||||||
|  |           Question: question, | ||||||
|  |           question_id: questionid, | ||||||
|  |           Markscheme: markscheme, | ||||||
|  |           'Examiners report': report, | ||||||
|  |           topics, | ||||||
|  |           subtopics | ||||||
|  |         } = item; | ||||||
|  | 
 | ||||||
|  |         const bigQuestionBox = document.createElement("div"); | ||||||
|  |         bigQuestionBox.id = questionid; | ||||||
|  | 
 | ||||||
|  |         const allClasses = [...topics.map(t => t.trim()), | ||||||
|  |         ...subtopics.map(s => s.trim()), | ||||||
|  |           "hidden"]; | ||||||
|  |         bigQuestionBox.classList.add(...allClasses); | ||||||
|  | 
 | ||||||
|  |         const btnContainer = document.createElement("div"); | ||||||
|  |         btnContainer.classList.add("btn-container"); | ||||||
|  | 
 | ||||||
|  |         function toggleMScont(questionid) { | ||||||
|  |           const markschemeContainer = document.getElementById(`markscheme-${questionid} ${currentFileName}`); | ||||||
|  |           toggleMSSvg.classList.toggle('hidden'); | ||||||
|  |           markschemeContainer.classList.toggle('hidden'); | ||||||
|  |           activeQuestionId = markschemeContainer.classList.contains('hidden') ? null : questionid; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         function toggleRepcont(questionid) { | ||||||
|  |           const reportContainer = document.getElementById(`report-${questionid} ${currentFileName}`); | ||||||
|  |           toggleRepSvg.classList.toggle('hidden'); | ||||||
|  |           reportContainer.classList.toggle('hidden'); | ||||||
|  |           activeQuestionId = reportContainer.classList.contains('hidden') ? null : questionid; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const buttons = [ | ||||||
|  |           { text: "Markscheme", handler: () => { toggleMS(); toggleMScont(questionid); } }, | ||||||
|  |           { text: "Examiners report", handler: () => { toggleR(); toggleRepcont(questionid); } }, | ||||||
|  |           { text: "Add to PDF", handler: createPDFButtonHandler(questionid) } | ||||||
|  |         ].map(createButton); | ||||||
|  | 
 | ||||||
|  |         buttons.forEach(button => btnContainer.appendChild(button)); | ||||||
|  | 
 | ||||||
|  |         const content = ` | ||||||
|  |                     <h3>${questionid}</h3> | ||||||
|  |                     <h4><b>Topics:</b> ${topics.join(', ')}</h4> | ||||||
|  |                     <h4><b>Subtopics:</b> ${subtopics.join(', ')}</h4> | ||||||
|  |                     <div class="square-container">${question}</div> | ||||||
|  |                 `;
 | ||||||
|  | 
 | ||||||
|  |         bigQuestionBox.innerHTML = content; | ||||||
|  |         bigQuestionBox.querySelector('h3').after(btnContainer); | ||||||
|  | 
 | ||||||
|  |         if (markscheme) { | ||||||
|  |           createContainer('markscheme', questionid, filename, markscheme, domCache.msbox); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (report) { | ||||||
|  |           createContainer('report', questionid, filename, report, domCache.reportbox); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /********** Removed ID appending method since it was slowing down interaction with the X svg *********/ | ||||||
|  | 
 | ||||||
|  |         const toggleMSSvg = createSVGElement(questionid); | ||||||
|  |         //toggleMSSvg.id = `toggleMSSvg-${questionid}`;
 | ||||||
|  | 
 | ||||||
|  |         const toggleRepSvg = createSVGElement(questionid); | ||||||
|  |         //toggleRepSvg.id = `toggleRepSvg-${questionid}`;
 | ||||||
|  | 
 | ||||||
|  |         domCache.msbox2.appendChild(toggleMSSvg); | ||||||
|  |         domCache.repbox2.appendChild(toggleRepSvg); | ||||||
|  | 
 | ||||||
|  |         /*toggleMSSvg.addEventListener('click', () => { | ||||||
|  |           toggleMScont(questionid); | ||||||
|  |           toggleMS(); | ||||||
|  |         });*/ | ||||||
|  |          | ||||||
|  |         /********** Identifying by active question instead  **********/ | ||||||
|  |         let activeQuestionId = null; | ||||||
|  | 
 | ||||||
|  |         const handleToggle = () => { | ||||||
|  |           if (activeQuestionId) { | ||||||
|  |               const markschemeContainer = document.getElementById(`markscheme-${activeQuestionId} ${currentFileName}`); | ||||||
|  |               const reportContainer = document.getElementById(`report-${activeQuestionId} ${currentFileName}`); | ||||||
|  | 
 | ||||||
|  |               if (markschemeContainer && !markschemeContainer.classList.contains('hidden')) { | ||||||
|  |                 toggleMSSvg.classList.toggle('hidden'); | ||||||
|  |                 toggleMS(); | ||||||
|  |                 markschemeContainer.classList.toggle('hidden'); | ||||||
|  |                 activeQuestionId = null; | ||||||
|  |                  | ||||||
|  |               } else if (reportContainer && !reportContainer.classList.contains('hidden')) { | ||||||
|  |                 toggleRepSvg.classList.toggle('hidden'); | ||||||
|  |                 toggleR(); | ||||||
|  |                 reportContainer.classList.toggle('hidden'); | ||||||
|  |                 activeQuestionId = null; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         toggleMSSvg.addEventListener('click', handleToggle); | ||||||
|  |         toggleRepSvg.addEventListener('click', handleToggle); | ||||||
|  |         document.addEventListener('keydown', (event) => { | ||||||
|  |           if (event.key === 'Escape') { | ||||||
|  |             handleToggle(); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /*toggleRepSvg.addEventListener('click', () => { | ||||||
|  |           toggleRepcont(questionid); | ||||||
|  |           toggleR(); | ||||||
|  |         });*/ | ||||||
|  | 
 | ||||||
|  |         fragment.appendChild(bigQuestionBox); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       domCache.rightCol.appendChild(fragment); | ||||||
|  |       updateSquareContainers(); | ||||||
|  |       toggleDownAllQs(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /******** Old function for fetching JSON, deprecated in favor of IndexedDB ********/ | ||||||
|  | /*function loadJSON(filename) { | ||||||
|  |   fetch(`https://pub-59370068cd854c158959e7ca4578e5bd.r2.dev/${filename}`) // ../assets/jsonqb/
 | ||||||
|  |     .then(response => response.json()) | ||||||
|  |     .then(data => { | ||||||
|  |       jsonDataFetched = true; | ||||||
|  |       currentFileName = filename; | ||||||
|  | 
 | ||||||
|  |       topics = [...new Set(data.flatMap(item => item.topics))].sort(); | ||||||
|  |       renderTopics(); | ||||||
|  | 
 | ||||||
|  |       const fragment = document.createDocumentFragment(); | ||||||
|  | 
 | ||||||
|  |       data.forEach(item => {}); | ||||||
|  | 
 | ||||||
|  |       domCache.rightCol.appendChild(fragment); | ||||||
|  |       updateSquareContainers(); | ||||||
|  |     }) | ||||||
|  |     .catch(error => console.error('Error fetching JSON:', error)); | ||||||
|  | }*/ | ||||||
|  | 
 | ||||||
|  | function createButton({ text, handler, className = 'btn-secondary' }) { | ||||||
|  |   const button = document.createElement("button"); | ||||||
|  |   button.classList.add(className); | ||||||
|  |   button.textContent = text; | ||||||
|  |   button.addEventListener('click', handler); | ||||||
|  |   return button; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function createPDFButtonHandler(questionid) { | ||||||
|  |   return function () { | ||||||
|  |     let selectedQuestionIds = JSON.parse(sessionStorage.getItem('selectedQuestionIds')) || []; | ||||||
|  |     const index = selectedQuestionIds.indexOf(questionid); | ||||||
|  |     if (index !== -1) { | ||||||
|  |       selectedQuestionIds.splice(index, 1); | ||||||
|  |       this.style.backgroundColor = 'rgb(66 165 245)'; | ||||||
|  |       this.textContent = 'Add to PDF'; | ||||||
|  |     } else { | ||||||
|  |       selectedQuestionIds.push(questionid); | ||||||
|  |       this.style.backgroundColor = '#e03b3b'; | ||||||
|  |       this.textContent = 'Added!'; | ||||||
|  |     } | ||||||
|  |     sessionStorage.setItem('selectedQuestionIds', JSON.stringify(selectedQuestionIds)); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function createContainer(type, questionid, filename, content, parent) { | ||||||
|  |   const container = document.createElement("div"); | ||||||
|  |   container.classList.add("square-container", "hidden"); | ||||||
|  |   container.id = `${type}-${questionid} ${filename}`; | ||||||
|  |   container.innerHTML = content; | ||||||
|  |   parent.appendChild(container); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function renderTopics() { | ||||||
|  |   const fragment = document.createDocumentFragment(); | ||||||
|  | 
 | ||||||
|  |   topics.forEach(topic => { | ||||||
|  |     const label = document.createElement('label'); | ||||||
|  |     label.classList.add('topic-label'); | ||||||
|  | 
 | ||||||
|  |     const checkbox = document.createElement('input'); | ||||||
|  |     checkbox.type = 'checkbox'; | ||||||
|  |     checkbox.name = 'topic'; | ||||||
|  |     checkbox.value = topic; | ||||||
|  | 
 | ||||||
|  |     checkbox.addEventListener('change', () => { | ||||||
|  |       document.querySelectorAll(`div[class*="${topic}"]`) | ||||||
|  |         .forEach(div => div.classList.toggle('hidden')); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     label.append(checkbox, topic); | ||||||
|  |     fragment.appendChild(label); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   domCache.leftCol.appendChild(fragment); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function updateSquareContainers() { | ||||||
|  |   document.querySelectorAll('.square-container').forEach(container => { | ||||||
|  |     const firstChild = container.children[0]; | ||||||
|  |     if (firstChild?.classList.contains('question')) { | ||||||
|  |       firstChild.classList.replace('question', 'specification'); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | document.addEventListener('DOMContentLoaded', () => { | ||||||
|  |   document.addEventListener('click', event => { | ||||||
|  |     const filename = fileNameMap[event.target.id]; | ||||||
|  |     if (!filename) return; | ||||||
|  | 
 | ||||||
|  |     if (jsonDataFetched && filename !== currentFileName) { | ||||||
|  |       resetState(); | ||||||
|  |       loadJSON(filename); | ||||||
|  |     //} else if (jsonDataFetched && filename === currentFileName) {
 | ||||||
|  |       //resetState();
 | ||||||
|  |       //jsonDataFetched = false;
 | ||||||
|  |     } else if (!jsonDataFetched) { | ||||||
|  |       loadJSON(filename); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function resetState() { | ||||||
|  |   domCache.rightCol.innerHTML = ''; | ||||||
|  |   document.querySelectorAll('.topic-label').forEach(label => label.remove()); | ||||||
|  |   sessionStorage.setItem('selectedQuestionIds', '[]'); | ||||||
|  | } | ||||||
|  | @ -254,6 +254,10 @@ video { | ||||||
|   display: none |   display: none | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .mainpage { | ||||||
|  |   height: auto !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| body, | body, | ||||||
| html, | html, | ||||||
| #root { | #root { | ||||||
|  | @ -311,15 +315,15 @@ h4 { | ||||||
| 
 | 
 | ||||||
| .btn-secondary { | .btn-secondary { | ||||||
|   --tw-text-opacity: 1; |   --tw-text-opacity: 1; | ||||||
|   color: rgb(3 54 40 / var(--tw-text-opacity)); |   color: rgb(248 248 248 / var(--tw-text-opacity)); | ||||||
|   background-color: #55ad95 |   background-color: rgb(66 165 245 / var(--tw-text-opacity)); /*55ad95*/ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .btn-secondary:hover { | .btn-secondary:hover { | ||||||
|   --tw-bg-opacity: 1; |   --tw-bg-opacity: 1; | ||||||
|   background-color: rgb(85 173 149 / var(--tw-bg-opacity)); |   background-color: #42a2f5; | ||||||
|   --tw-text-opacity: 1; |   --tw-text-opacity: 1; | ||||||
|   color: rgb(248 248 248 / var(--tw-text-opacity)) |   color: rgb(0 0 0 / var(--tw-text-opacity)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #left-col { | #left-col { | ||||||
|  | @ -343,6 +347,10 @@ h4 { | ||||||
|   position: relative; |   position: relative; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #markscheme, #report, #modal { | ||||||
|  |   z-index: 9999; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #markscheme-box, #report-box{ | #markscheme-box, #report-box{ | ||||||
|   min-height: 100%; |   min-height: 100%; | ||||||
|   overflow-y: scroll; |   overflow-y: scroll; | ||||||
							
								
								
									
										50
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								index.html
									
										
									
									
									
								
							|  | @ -1,43 +1,41 @@ | ||||||
|  | 
 | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| 
 | 
 | ||||||
| <head> | <head> | ||||||
|     <meta charset="UTF-8" /> |     <meta charset="UTF-8"> | ||||||
|     <link rel="icon" type="image/svg+xml" href="./favicon.svg" /> |     <link rel="icon" type="image/svg+xml" href="./favicon.svg"> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|     <title>Mortar & Pestle</title> |     <title>Mortar & Pestle | The Next Generation of Question Grinding</title> | ||||||
|     <link rel="stylesheet" href="./assets/index-0e5924d3.css"> |     <link rel="stylesheet" href="./assets/style.css"> | ||||||
|     <script> |     <meta name="robots" content="index,follow"> | ||||||
|         function toggleDarkMode() { |     <meta name="copyright" content="Public Domain"> | ||||||
|           const body = document.body; |     <meta name="keywords" content="pestle, mortar, mortar & pestle, ib questionbank, pirateib"> | ||||||
|           const button = document.querySelector('.btn-primary'); |     <meta name="description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||||
|           body.classList.toggle('dark-mode'); |     <meta property="og:locale" content="en_US"> | ||||||
|        |     <meta property="og:type" content="website"> | ||||||
|           if (body.classList.contains('dark-mode')) { |     <meta property="og:title" content="Mortar & Pestle | The Next Generation of Question Grinding"> | ||||||
|             button.style.position = 'static'; |     <meta property="og:description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||||
|             button.style.zIndex = 'auto'; |     <meta property="og:url" content="https://pestle.pages.dev/"> | ||||||
|           } else { |     <meta property="og:site_name" content="Mortar & Pestle"> | ||||||
|             button.style.position = 'fixed'; |     <meta name="twitter:title" content="Mortar & Pestle | The Next Generation of Question Grinding"> | ||||||
|             button.style.zIndex = '9999'; |     <meta name="twitter:description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||||
|             button.style.right = '0'; |     <script>function toggleDarkMode(){var e=document.body,d=document.head,t=document.getElementById("darkmodebtn");"disabled"===(localStorage.getItem("darkMode")||"disabled")?(e.classList.add("dark-mode"),d.classList.add("dark-mode"),localStorage.setItem("darkMode","enabled"),t.innerText="Light Mode"):(e.classList.remove("dark-mode"),d.classList.remove("dark-mode"),localStorage.setItem("darkMode","disabled"),t.innerText="Dark Mode")}document.addEventListener("DOMContentLoaded",(()=>{const e=localStorage.getItem("darkMode"),d=window.matchMedia("(prefers-color-scheme: dark)").matches;"enabled"===e?(document.body.classList.add("dark-mode"),document.head.classList.add("dark-mode"),document.getElementById("darkmodebtn").innerText="Light Mode"):"disabled"===e?(document.body.classList.remove("dark-mode"),document.head.classList.remove("dark-mode"),document.getElementById("darkmodebtn").innerText="Dark Mode"):d?(document.body.classList.add("dark-mode"),document.head.classList.add("dark-mode"),document.getElementById("darkmodebtn").innerText="Light Mode",localStorage.setItem("darkMode","enabled")):(document.body.classList.remove("dark-mode"),document.head.classList.remove("dark-mode"),document.getElementById("darkmodebtn").innerText="Dark Mode",localStorage.setItem("darkMode","disabled"))}));</script> | ||||||
|           } |  | ||||||
|         } |  | ||||||
|           </script> |  | ||||||
| </head> | </head> | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
|     <div id="root"> |     <div id="root"> | ||||||
|         <div class="me-auto ms-auto flex flex-col items-center p-4 pt-3" id="landingPage"> |         <div class="me-auto ms-auto flex flex-col items-center p-4 pt-3" id="landingPage"> | ||||||
|             <header class="flex w-full items-start" style="position: fixed"> |             <header class="flex w-full items-start" style="position: fixed"> | ||||||
|                 <div style="position: fixed; right: 15px; z-index: 9999;"><button class="btn-primary" onclick="toggleDarkMode()">Dark Mode</button></div> |                 <div style="position: fixed; right: 15px; z-index: 9999;"><button id="darkmodebtn" class="btn-primary" onclick="toggleDarkMode()">Dark Mode</button></div> | ||||||
|             </header> |             </header> | ||||||
|             <div> |             <div> | ||||||
|                 <div style="text-align:center;"><h1><b>Mortar & Pestle</b></h1><br></div> |                 <div style="text-align:center;"><h1><b>Mortar & Pestle</b></h1><br></div> | ||||||
|                 <h1>The <em>Next Generation</em> of Question Grinding</h1> |                 <h1 style="text-align:center;">The <em>Next Generation</em> of Question Grinding</h1> | ||||||
|             </div><br> |             </div><br> | ||||||
|             <div class="mt-4" style="text-align:center;"> |             <div class="mt-4" style="text-align:center;"> | ||||||
|                 <h2>Practice IB Exam-Style Questions, create topic-wise tests, and much more!</h2><br> |                 <h2>Practice IB Exam-Style Questions, create topic-wise tests, and much more!</h2><br> | ||||||
|                 <h2 style="color:orangered">Warning: This site is on early beta release, and it is <b>NOT</b> optimized for mobile.</h2> |                 <h2 style="color:orangered">Warning: This site is <b>NOT</b> optimized for mobile.</h2> | ||||||
|             </div> |             </div> | ||||||
|             <br> |             <br> | ||||||
|             <div class="p-3" style="text-align:center;"><a href="app/index.html"><button class="btn-primary"><h1>Start Grinding!</h1></button></a></div> |             <div class="p-3" style="text-align:center;"><a href="app/index.html"><button class="btn-primary"><h1>Start Grinding!</h1></button></a></div> | ||||||
|  | @ -45,7 +43,7 @@ | ||||||
|             <div class="mt-8 font-medium text-neutralVariant"> |             <div class="mt-8 font-medium text-neutralVariant"> | ||||||
|                 <h2>Features</h2> |                 <h2>Features</h2> | ||||||
|             </div> |             </div> | ||||||
|             <section class="flex w-full justify-evenly pt-5"> |             <section class="flex w-full justify-evenly pt-5 text-center"> | ||||||
|                 <div class="flex shrink grow basis-0 flex-col items-center"> |                 <div class="flex shrink grow basis-0 flex-col items-center"> | ||||||
|                     <div class="rounded-full bg-secondary/30 p-2"><svg xmlns="http://www.w3.org/2000/svg" width="24" |                     <div class="rounded-full bg-secondary/30 p-2"><svg xmlns="http://www.w3.org/2000/svg" width="24" | ||||||
|                             height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" |                             height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" | ||||||
|  | @ -80,7 +78,7 @@ | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <footer class="mt-16 flex flex-col text-center"> |         <footer class="mt-16 flex flex-col text-center"> | ||||||
|             <p><a href="http://pirateib.net">Made by pirateIB</a></p> |             <p><a href="https://pirateib.xyz">Made by pirateIB</a></p> | ||||||
|         </footer> |         </footer> | ||||||
|          |          | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue