Update 5 files
- /assets/index-0e5924d3.css - /app/index.js - /app/index.html - /index.html - /assets/style.css
This commit is contained in:
		
							parent
							
								
									c77ee1467e
								
							
						
					
					
						commit
						c091326ae7
					
				
					 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 | ||||
| } | ||||
| 
 | ||||
| .mainpage { | ||||
|   height: auto !important; | ||||
| } | ||||
| 
 | ||||
| body, | ||||
| html, | ||||
| #root { | ||||
|  | @ -311,15 +315,15 @@ h4 { | |||
| 
 | ||||
| .btn-secondary { | ||||
|   --tw-text-opacity: 1; | ||||
|   color: rgb(3 54 40 / var(--tw-text-opacity)); | ||||
|   background-color: #55ad95 | ||||
|   color: rgb(248 248 248 / var(--tw-text-opacity)); | ||||
|   background-color: rgb(66 165 245 / var(--tw-text-opacity)); /*55ad95*/ | ||||
| } | ||||
| 
 | ||||
| .btn-secondary:hover { | ||||
|   --tw-bg-opacity: 1; | ||||
|   background-color: rgb(85 173 149 / var(--tw-bg-opacity)); | ||||
|   background-color: #42a2f5; | ||||
|   --tw-text-opacity: 1; | ||||
|   color: rgb(248 248 248 / var(--tw-text-opacity)) | ||||
|   color: rgb(0 0 0 / var(--tw-text-opacity)) | ||||
| } | ||||
| 
 | ||||
| #left-col { | ||||
|  | @ -343,6 +347,10 @@ h4 { | |||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| #markscheme, #report, #modal { | ||||
|   z-index: 9999; | ||||
| } | ||||
| 
 | ||||
| #markscheme-box, #report-box{ | ||||
|   min-height: 100%; | ||||
|   overflow-y: scroll; | ||||
							
								
								
									
										52
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										52
									
								
								index.html
									
										
									
									
									
								
							|  | @ -1,43 +1,41 @@ | |||
| 
 | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="./favicon.svg" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Mortar & Pestle</title> | ||||
|     <link rel="stylesheet" href="./assets/index-0e5924d3.css"> | ||||
|     <script> | ||||
|         function toggleDarkMode() { | ||||
|           const body = document.body; | ||||
|           const button = document.querySelector('.btn-primary'); | ||||
|           body.classList.toggle('dark-mode'); | ||||
|        | ||||
|           if (body.classList.contains('dark-mode')) { | ||||
|             button.style.position = 'static'; | ||||
|             button.style.zIndex = 'auto'; | ||||
|           } else { | ||||
|             button.style.position = 'fixed'; | ||||
|             button.style.zIndex = '9999'; | ||||
|             button.style.right = '0'; | ||||
|           } | ||||
|         } | ||||
|           </script> | ||||
|     <meta charset="UTF-8"> | ||||
|     <link rel="icon" type="image/svg+xml" href="./favicon.svg"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>Mortar & Pestle | The Next Generation of Question Grinding</title> | ||||
|     <link rel="stylesheet" href="./assets/style.css"> | ||||
|     <meta name="robots" content="index,follow"> | ||||
|     <meta name="copyright" content="Public Domain"> | ||||
|     <meta name="keywords" content="pestle, mortar, mortar & pestle, ib questionbank, pirateib"> | ||||
|     <meta name="description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||
|     <meta property="og:locale" content="en_US"> | ||||
|     <meta property="og:type" content="website"> | ||||
|     <meta property="og:title" content="Mortar & Pestle | The Next Generation of Question Grinding"> | ||||
|     <meta property="og:description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||
|     <meta property="og:url" content="https://pestle.pages.dev/"> | ||||
|     <meta property="og:site_name" content="Mortar & Pestle"> | ||||
|     <meta name="twitter:title" content="Mortar & Pestle | The Next Generation of Question Grinding"> | ||||
|     <meta name="twitter:description" content="Practice IB Exam-Style Questions, create topic-wise tests, and much more!"> | ||||
|     <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> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     <div id="root"> | ||||
|         <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"> | ||||
|                 <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> | ||||
|             <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 class="mt-4" style="text-align:center;"> | ||||
|                 <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> | ||||
|             <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> | ||||
|  | @ -45,7 +43,7 @@ | |||
|             <div class="mt-8 font-medium text-neutralVariant"> | ||||
|                 <h2>Features</h2> | ||||
|             </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="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" | ||||
|  | @ -73,14 +71,14 @@ | |||
|                 <div class="flex shrink grow basis-0 flex-col items-center"> | ||||
|                     <div class="rounded-full bg-gray-400/30 p-2"><img src="./assets/anon.svg" | ||||
|                             alt="anonymous image"></div> | ||||
|                     <div class="font-medium text-gray-500">No trackers/cookies</div> | ||||
|                     <div class="font-medium text-gray-500">No trackers / cookies</div> | ||||
|                 </div> | ||||
|             </section> | ||||
|             <br> | ||||
|         </div> | ||||
| 
 | ||||
|         <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> | ||||
|          | ||||
|     </div> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue