Update 6 files
- /app/index.html - /app/index.js - /assets/style.css - /assets/jsonqb/Chemistry 2025 QB merged.json - /assets/jsonqb/Physics 2025 QB merged.json - /assets/jsonqb/Biology 2025 QB merged.json
This commit is contained in:
parent
7db98ac97c
commit
70f62f995e
6 changed files with 7822 additions and 30 deletions
|
@ -27,14 +27,6 @@
|
||||||
Loading, please wait...
|
Loading, please wait...
|
||||||
</div>
|
</div>
|
||||||
<div id="root">
|
<div id="root">
|
||||||
<header>
|
|
||||||
<div style="position: fixed; right: 15px; z-index: 9999;">
|
|
||||||
<button id="addalltoPDFbtn" class="btn-primary hidden" onclick="addalltoPDF()">Add all Qs to PDF</button>
|
|
||||||
<button id="generatePDFbtn" class="btn-primary hidden" onclick="generatePDF()">Generate PDF!</button>
|
|
||||||
<button id="darkmodebtn" class="btn-primary" onclick="toggleDarkMode()">Dark Mode</button>
|
|
||||||
<button id="helpbtn" class="btn-secondary" onclick="toggleHelp()">Help</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div id="appContainer" class="h-full w-full">
|
<div id="appContainer" class="h-full w-full">
|
||||||
<div id="left-col" class="flex flex-col bg-white p-2">
|
<div id="left-col" class="flex flex-col bg-white p-2">
|
||||||
<div class="p-3"><a href="../index.html"><button class="btn-primary">< Go Home</button></button></a></div>
|
<div class="p-3"><a href="../index.html"><button class="btn-primary">< Go Home</button></button></a></div>
|
||||||
|
@ -55,7 +47,50 @@
|
||||||
<br>
|
<br>
|
||||||
<div id="topic-list"></div>
|
<div id="topic-list"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="right-col" class="flex flex-col bg-gray-100 p-2">
|
<div id="right-wrapper">
|
||||||
|
<div id="upper-right-col" class="flex flex-col bg-gray-100 p-2">
|
||||||
|
<div class="toolbar">
|
||||||
|
<button id="addalltoPDFbtn" class="btn-primary hidden" onclick="addalltoPDF()">Add all Qs to PDF</button>
|
||||||
|
<button id="generatePDFbtn" class="btn-primary hidden" onclick="generatePDF()">Generate PDF!</button>
|
||||||
|
<button id="darkmodebtn" class="btn-primary" onclick="toggleDarkMode()">Dark Mode</button>
|
||||||
|
<button id="helpbtn" class="btn-secondary" onclick="toggleHelp()">Help</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="selectables p-2 hidden">
|
||||||
|
|
||||||
|
<label for="paper-select"><b>Paper:</b>
|
||||||
|
<select id="paper-select">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="1">Paper 1</option>
|
||||||
|
<option value="2">Paper 2</option>
|
||||||
|
<option value="3">Paper 3</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="level-select"><b>Level:</b>
|
||||||
|
<select id="level-select">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="standard">SL</option>
|
||||||
|
<option value="higher">HL</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!--<label for="topic-select"><b>Topics:</b>
|
||||||
|
<select id="topic-select">
|
||||||
|
<option value="">All</option>
|
||||||
|
</select>
|
||||||
|
</label>-->
|
||||||
|
|
||||||
|
<label for="subtopic-select" ><b>Subtopics:</b>
|
||||||
|
<select id="subtopic-select">
|
||||||
|
<option value="">All</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="right-col" class="flex flex-col bg-gray-100 p-2"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="modal" class="fixed left-0 top-0 hidden flex h-full w-full items-center justify-center bg-black/70">
|
<div id="modal" class="fixed left-0 top-0 hidden flex h-full w-full items-center justify-center bg-black/70">
|
||||||
<div class="flex h-full w-full md:h-3/4 md:w-3/4 flex-col border bg-gray-100 p-4">
|
<div class="flex h-full w-full md:h-3/4 md:w-3/4 flex-col border bg-gray-100 p-4">
|
||||||
|
@ -98,6 +133,19 @@
|
||||||
<h4 class="ms-2">IB Biology 2025 QuestionBank (split by parts)
|
<h4 class="ms-2">IB Biology 2025 QuestionBank (split by parts)
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<button id="bioqb25-merged" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="me-2">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="16"></line>
|
||||||
|
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||||
|
</svg> Add
|
||||||
|
</button>
|
||||||
|
<h4 class="ms-2">IB Biology 2025 QuestionBank (parts merged)
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
<div class="flex items-center mt-2">
|
<div class="flex items-center mt-2">
|
||||||
<button id="bmqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
<button id="bmqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
@ -137,6 +185,19 @@
|
||||||
<h4 class="ms-2">IB Chemistry 2025 QuestionBank (split by parts)
|
<h4 class="ms-2">IB Chemistry 2025 QuestionBank (split by parts)
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<button id="chemqb25-merged" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="me-2">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="16"></line>
|
||||||
|
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||||
|
</svg> Add
|
||||||
|
</button>
|
||||||
|
<h4 class="ms-2">IB Chemistry 2025 QuestionBank (parts merged)
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
<div class="flex items-center mt-2">
|
<div class="flex items-center mt-2">
|
||||||
<button id="compsciqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
<button id="compsciqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
@ -280,6 +341,19 @@
|
||||||
<h4 class="ms-2">IB Physics 2025 QuestionBank (split by parts)
|
<h4 class="ms-2">IB Physics 2025 QuestionBank (split by parts)
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<button id="phyqb25-merged" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="me-2">
|
||||||
|
<circle cx="12" cy="12" r="10"></circle>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="16"></line>
|
||||||
|
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||||
|
</svg> Add
|
||||||
|
</button>
|
||||||
|
<h4 class="ms-2">IB Physics 2025 QuestionBank (parts merged)
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
<div class="flex items-center mt-2">
|
<div class="flex items-center mt-2">
|
||||||
<button id="psychqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
<button id="psychqb" class="btn-primary flex items-center" onclick="toggleModal()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
@ -325,9 +399,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="m-0 mt-2 overflow-auto p-0 font-serif max-h-60vh">
|
<div class="m-0 mt-2 overflow-auto p-0 font-serif max-h-60vh">
|
||||||
<div class="font-sans">
|
<div class="font-sans">
|
||||||
<h3>· Why are QBv6 (2025 sciences) questions split by parts?</h3>
|
<h3>· The 2025 science questionbanks have very few questions. Will you merge the previous questionbank into it?</h3>
|
||||||
<br>
|
<br>
|
||||||
<p>Ask IB ¯\_(ツ)_/¯ Full combined version coming soon!</p>
|
<p>Soon™... it requires huge manual work because the syllabus changes are big.</p>
|
||||||
<br>
|
<br>
|
||||||
<h3>· I tried to save all the questions as PDF, and my computer got frozen!</h3>
|
<h3>· I tried to save all the questions as PDF, and my computer got frozen!</h3>
|
||||||
<br>
|
<br>
|
||||||
|
|
184
app/index.js
184
app/index.js
|
@ -11,6 +11,21 @@ function toggleMS(){document.getElementById("markscheme").classList.toggle("hidd
|
||||||
function toggleR(){document.getElementById("report").classList.toggle("hidden")}
|
function toggleR(){document.getElementById("report").classList.toggle("hidden")}
|
||||||
function toggleHelp(){document.getElementById("helpmenu").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 toggleDownAllQs(){document.getElementById("addalltoPDFbtn").classList.remove('hidden');document.getElementById("generatePDFbtn").classList.remove('hidden')}
|
||||||
|
function toggleFilters(){document.querySelector(".selectables").classList.remove('hidden')};
|
||||||
|
|
||||||
|
|
||||||
|
function checkWidth() {
|
||||||
|
const btn1 = document.getElementById('generatePDFbtn');
|
||||||
|
const btn2 = document.getElementById('addalltoPDFbtn');
|
||||||
|
if (window.innerWidth <= 480) {
|
||||||
|
btn1.style.display = 'none';
|
||||||
|
btn2.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
btn1.style.display = '';
|
||||||
|
btn2.style.display = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', checkWidth);
|
||||||
|
|
||||||
|
|
||||||
function toggleDarkMode() {
|
function toggleDarkMode() {
|
||||||
|
@ -56,7 +71,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function startRandomTimerLoop() {
|
/*function startRandomTimerLoop() {
|
||||||
var randomTime = Math.floor(11 * Math.random()) + 20;
|
var randomTime = Math.floor(11 * Math.random()) + 20;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
alert("Provided by pirateIB\nhttps://pirateib.xyz");
|
alert("Provided by pirateIB\nhttps://pirateib.xyz");
|
||||||
|
@ -67,7 +82,7 @@ window.onload = function() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
startRandomTimerLoop();
|
startRandomTimerLoop();
|
||||||
}, 300000);
|
}, 300000);
|
||||||
};
|
};*/
|
||||||
|
|
||||||
/*function generatePDF() {
|
/*function generatePDF() {
|
||||||
const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || [];
|
const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || [];
|
||||||
|
@ -94,7 +109,6 @@ function generatePDF() {
|
||||||
const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || [];
|
const selectedQuestionIds = JSON.parse(sessionStorage.getItem("selectedQuestionIds")) || [];
|
||||||
if (0 === selectedQuestionIds.length) return alert("Select some questions first!");
|
if (0 === selectedQuestionIds.length) return alert("Select some questions first!");
|
||||||
|
|
||||||
// Prompt the user for including markschemes
|
|
||||||
let includeMarkschemes = null;
|
let includeMarkschemes = null;
|
||||||
while (includeMarkschemes !== "yes" && includeMarkschemes !== "no") {
|
while (includeMarkschemes !== "yes" && includeMarkschemes !== "no") {
|
||||||
includeMarkschemes = prompt("Do you want to include markschemes? (yes/no)").toLowerCase();
|
includeMarkschemes = prompt("Do you want to include markschemes? (yes/no)").toLowerCase();
|
||||||
|
@ -177,10 +191,12 @@ if (event.key === 'Escape') {
|
||||||
const fileNameMap = {
|
const fileNameMap = {
|
||||||
'bioqb': 'Biology QB.json',
|
'bioqb': 'Biology QB.json',
|
||||||
'bioqb25-split': 'Biology 2025 QB split.json',
|
'bioqb25-split': 'Biology 2025 QB split.json',
|
||||||
|
'bioqb25-merged': 'Biology 2025 QB merged.json',
|
||||||
'bioqb25-full': 'Biology 2025 QB full.json',
|
'bioqb25-full': 'Biology 2025 QB full.json',
|
||||||
'bmqb': 'Business Management QB.json',
|
'bmqb': 'Business Management QB.json',
|
||||||
'chemqb': 'Chemistry QB.json',
|
'chemqb': 'Chemistry QB.json',
|
||||||
'chemqb25-split': 'Chemistry 2025 QB split.json',
|
'chemqb25-split': 'Chemistry 2025 QB split.json',
|
||||||
|
'chemqb25-merged': 'Chemistry 2025 QB merged.json',
|
||||||
'chemqb25-full': 'Chemistry 2025 QB full.json',
|
'chemqb25-full': 'Chemistry 2025 QB full.json',
|
||||||
'compsciqb': 'Computer Science QB.json',
|
'compsciqb': 'Computer Science QB.json',
|
||||||
'destechqb': 'Design Technology QB.json',
|
'destechqb': 'Design Technology QB.json',
|
||||||
|
@ -193,6 +209,7 @@ const fileNameMap = {
|
||||||
'mathaiqb': 'Math AI QB.json',
|
'mathaiqb': 'Math AI QB.json',
|
||||||
'phyqb': 'Physics QB.json',
|
'phyqb': 'Physics QB.json',
|
||||||
'phyqb25-split': 'Physics 2025 QB split.json',
|
'phyqb25-split': 'Physics 2025 QB split.json',
|
||||||
|
'phyqb25-merged': 'Physics 2025 QB merged.json',
|
||||||
'phyqb25-full': 'Physics 2025 QB full.json',
|
'phyqb25-full': 'Physics 2025 QB full.json',
|
||||||
'psychqb': 'Psychology QB.json',
|
'psychqb': 'Psychology QB.json',
|
||||||
'sehsqb': 'SEHS QB.json'
|
'sehsqb': 'SEHS QB.json'
|
||||||
|
@ -278,7 +295,8 @@ async function loadJSON(filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`../assets/jsonqb/${filename}`);
|
const response = await fetch(`https://pub-59370068cd854c158959e7ca4578e5bd.r2.dev/${filename}`); // ../assets/jsonqb/
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok');
|
throw new Error('Network response was not ok');
|
||||||
}
|
}
|
||||||
|
@ -298,17 +316,20 @@ async function loadJSON(filename) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
sessionStorage.setItem('selectedQuestionIds', '[]');
|
sessionStorage.setItem('selectedQuestionIds', '[]');
|
||||||
|
sessionStorage.setItem('visibleIDs', '[]');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
processData(data, filename);
|
processData(data, filename);
|
||||||
hideLoading();
|
hideLoading();
|
||||||
sessionStorage.setItem('selectedQuestionIds', '[]');
|
sessionStorage.setItem('selectedQuestionIds', '[]');
|
||||||
|
sessionStorage.setItem('visibleIDs', '[]');
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching JSON:', error);
|
console.error('Error fetching JSON:', error);
|
||||||
hideLoading();
|
hideLoading();
|
||||||
sessionStorage.setItem('selectedQuestionIds', '[]');
|
sessionStorage.setItem('selectedQuestionIds', '[]');
|
||||||
|
sessionStorage.setItem('visibleIDs', '[]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,9 +339,12 @@ function processData(data, filename) {
|
||||||
jsonDataFetched = true;
|
jsonDataFetched = true;
|
||||||
currentFileName = filename;
|
currentFileName = filename;
|
||||||
|
|
||||||
|
jsonData = data;
|
||||||
|
|
||||||
topics = [...new Set(data.flatMap(item => item.topics))].sort();
|
topics = [...new Set(data.flatMap(item => item.topics))].sort();
|
||||||
subtopics = [...new Set(data.flatMap(item => item.subtopics))].sort();
|
subtopics = [...new Set(data.flatMap(item => item.subtopics))].sort();
|
||||||
renderTopics();
|
renderTopics();
|
||||||
|
renderSubtopics();
|
||||||
|
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
@ -379,7 +403,7 @@ function processData(data, filename) {
|
||||||
const content = `
|
const content = `
|
||||||
<h3>${questionid}</h3>
|
<h3>${questionid}</h3>
|
||||||
<h4><b>Topics:</b> ${topics.join(', ')}</h4>
|
<h4><b>Topics:</b> ${topics.join(', ')}</h4>
|
||||||
<h4><b>Subtopics:</b> ${subtopics.join(', ')}</h4>
|
<h4><details><summary><b>Subtopics</b> </summary>${subtopics.join(', ')}</details></h4>
|
||||||
<div class="square-container">${question}</div>
|
<div class="square-container">${question}</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -453,6 +477,7 @@ function processData(data, filename) {
|
||||||
domCache.rightCol.appendChild(fragment);
|
domCache.rightCol.appendChild(fragment);
|
||||||
updateSquareContainers();
|
updateSquareContainers();
|
||||||
toggleDownAllQs();
|
toggleDownAllQs();
|
||||||
|
toggleFilters();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +536,29 @@ function createContainer(type, questionid, filename, content, parent) {
|
||||||
parent.appendChild(container);
|
parent.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSubtopics() {
|
||||||
|
const subtopicListContainer = document.getElementById('subtopic-select');
|
||||||
|
subtopicListContainer.innerHTML = '<option value="">All</option>';
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
subtopics.forEach(subtopic => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.innerText = subtopic;
|
||||||
|
option.value = subtopic;
|
||||||
|
fragment.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
subtopicListContainer.appendChild(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
function renderTopics() {
|
function renderTopics() {
|
||||||
|
currentFilters.level = currentFilters.paper = currentFilters.subtopic = null;
|
||||||
|
document.getElementById('level-select').value = '';
|
||||||
|
document.getElementById('paper-select').value = '';
|
||||||
|
document.getElementById('subtopic-select').value = '';
|
||||||
|
|
||||||
|
const topicListContainer = document.getElementById('topic-list');
|
||||||
|
topicListContainer.innerHTML = '';
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
topics.forEach(topic => {
|
topics.forEach(topic => {
|
||||||
|
@ -519,22 +566,127 @@ function renderTopics() {
|
||||||
label.classList.add('topic-label');
|
label.classList.add('topic-label');
|
||||||
|
|
||||||
const checkbox = document.createElement('input');
|
const checkbox = document.createElement('input');
|
||||||
checkbox.type = 'checkbox';
|
checkbox.type = 'checkbox';
|
||||||
checkbox.name = 'topic';
|
checkbox.name = 'topic';
|
||||||
checkbox.value = topic;
|
checkbox.value = topic;
|
||||||
|
|
||||||
checkbox.addEventListener('change', () => {
|
checkbox.addEventListener('change', () => {
|
||||||
document.querySelectorAll(`div[class*="${topic}"]`)
|
document.getElementById('level-select').value = '';
|
||||||
.forEach(div => div.classList.toggle('hidden'));
|
document.getElementById('paper-select').value = '';
|
||||||
});
|
document.getElementById('subtopic-select').value = '';
|
||||||
|
|
||||||
label.append(checkbox, topic);
|
currentFilters.level = null;
|
||||||
fragment.appendChild(label);
|
currentFilters.paper = null;
|
||||||
|
currentFilters.subtopic = null;
|
||||||
|
|
||||||
|
const checkedTopics = Array.from(
|
||||||
|
document.querySelectorAll('input[name="topic"]:checked')
|
||||||
|
).map(cb => cb.value);
|
||||||
|
|
||||||
|
applyAllFilters('topic', checkedTopics);
|
||||||
|
applyAllFilters('level', null);
|
||||||
|
applyAllFilters('paper', null);
|
||||||
|
applyAllFilters('subtopic', null);
|
||||||
|
|
||||||
|
const visibleIds = Array.from(
|
||||||
|
document.querySelectorAll('#right-col > div:not(.hidden)')
|
||||||
|
).map(div => div.id);
|
||||||
|
sessionStorage.setItem('visibleIDs', JSON.stringify(visibleIds));
|
||||||
|
});
|
||||||
|
|
||||||
|
label.append(checkbox, document.createTextNode(topic));
|
||||||
|
fragment.append(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
domCache.leftCol.appendChild(fragment);
|
topicListContainer.appendChild(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findRecordById(qid) {
|
||||||
|
return jsonData.find(item => item.question_id === qid) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const currentFilters = {
|
||||||
|
level: null,
|
||||||
|
paper: null,
|
||||||
|
topic: null,
|
||||||
|
subtopic: null
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyAllFilters(type, value) {
|
||||||
|
if (type === 'topic' || type === 'subtopic') {
|
||||||
|
const arr = Array.isArray(value) ? value.filter(Boolean) : [];
|
||||||
|
currentFilters[type] = arr.length > 0 ? arr : null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentFilters[type] = value || null;
|
||||||
|
}
|
||||||
|
document.querySelectorAll('#right-col > div').forEach(div => {
|
||||||
|
const qid = div.id;
|
||||||
|
const record = findRecordById(qid);
|
||||||
|
let hide = false;
|
||||||
|
|
||||||
|
// 1) Level
|
||||||
|
if (currentFilters.level) {
|
||||||
|
const ok = currentFilters.level === 'standard'
|
||||||
|
? qid.includes('.SL.')
|
||||||
|
: (qid.includes('.HL.') || qid.includes('.AHL.'));
|
||||||
|
hide ||= !ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFilters.paper) {
|
||||||
|
const pap = currentFilters.paper;
|
||||||
|
const regex = pap === '1'
|
||||||
|
? /\.1(?:[ABC])?\./
|
||||||
|
: new RegExp(`\\.${pap}\\.`);
|
||||||
|
hide ||= !regex.test(qid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFilters.topic) {
|
||||||
|
hide ||= true;
|
||||||
|
} else {
|
||||||
|
const ok = (record.topics || []).some(t =>
|
||||||
|
currentFilters.topic.includes(t)
|
||||||
|
);
|
||||||
|
hide ||= !ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFilters.subtopic) {
|
||||||
|
const ok = (record.subtopics||[])
|
||||||
|
.some(st => currentFilters.subtopic.includes(st));
|
||||||
|
hide ||= !ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.classList.toggle('hidden', hide);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.getElementById('paper-select')
|
||||||
|
.addEventListener('change', e => {
|
||||||
|
applyAllFilters('paper', e.target.value);
|
||||||
|
});
|
||||||
|
document.getElementById('level-select')
|
||||||
|
.addEventListener('change', e => {
|
||||||
|
applyAllFilters('level', e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById('subtopic-select')
|
||||||
|
.addEventListener('change', e => {
|
||||||
|
const vals = Array.from(e.target.selectedOptions)
|
||||||
|
.map(opt => opt.value)
|
||||||
|
.filter(v => v !== ''); // ← remove the "" entry
|
||||||
|
|
||||||
|
applyAllFilters('subtopic', vals);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function updateSquareContainers() {
|
function updateSquareContainers() {
|
||||||
document.querySelectorAll('.square-container').forEach(container => {
|
document.querySelectorAll('.square-container').forEach(container => {
|
||||||
const firstChild = container.children[0];
|
const firstChild = container.children[0];
|
||||||
|
@ -565,4 +717,6 @@ function resetState() {
|
||||||
domCache.rightCol.innerHTML = '';
|
domCache.rightCol.innerHTML = '';
|
||||||
document.querySelectorAll('.topic-label').forEach(label => label.remove());
|
document.querySelectorAll('.topic-label').forEach(label => label.remove());
|
||||||
sessionStorage.setItem('selectedQuestionIds', '[]');
|
sessionStorage.setItem('selectedQuestionIds', '[]');
|
||||||
}
|
sessionStorage.setItem('visibleIDs', '[]');
|
||||||
|
checkWidth()
|
||||||
|
}
|
||||||
|
|
3455
assets/jsonqb/Biology 2025 QB merged.json
Normal file
3455
assets/jsonqb/Biology 2025 QB merged.json
Normal file
File diff suppressed because one or more lines are too long
1785
assets/jsonqb/Chemistry 2025 QB merged.json
Normal file
1785
assets/jsonqb/Chemistry 2025 QB merged.json
Normal file
File diff suppressed because one or more lines are too long
2272
assets/jsonqb/Physics 2025 QB merged.json
Normal file
2272
assets/jsonqb/Physics 2025 QB merged.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -46,9 +46,8 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
height: 0;
|
height: 2px;
|
||||||
color: #000;
|
background-color: #000;
|
||||||
border-top-width: 1px
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abbr:where([title]) {
|
abbr:where([title]) {
|
||||||
|
@ -334,13 +333,66 @@ h4 {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
#right-col {
|
#right-wrapper {
|
||||||
flex: 4;
|
flex: 4;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#upper-right-col {
|
||||||
|
flex: 1;
|
||||||
|
/*overflow: hidden;*/
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right-col {
|
||||||
|
flex: 9;
|
||||||
|
/*height: 90%;
|
||||||
|
width: 80%;*/
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectables {
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.selectables {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#subtopic-select, #topic-select {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topic-list, .topic-label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topic-list label {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
#appContainer{
|
#appContainer{
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue