diff --git a/scripts/lib/elements.js b/scripts/lib/elements.js index 7d81c5d..de484cb 100644 --- a/scripts/lib/elements.js +++ b/scripts/lib/elements.js @@ -269,4 +269,11 @@ window.elements.Link = function (custom, inner) { let linkContainer = document.createElement("link"); return linkContainer; }); -} \ No newline at end of file +} + +window.elements.Label = function (custom, inner) { + return ezReactElements.createElement(custom, inner, () => { + let labelContainer = document.createElement("label"); + return labelContainer; + }); +}; \ No newline at end of file diff --git a/scripts/moodle/js/content.js b/scripts/moodle/js/content.js index b4e3722..45e8597 100644 --- a/scripts/moodle/js/content.js +++ b/scripts/moodle/js/content.js @@ -9,53 +9,53 @@ function get_psb() { } } var psb_list = new window.courseType.CourseCodeList(); - var dates = document.querySelectorAll(".categoryname"); - if (data.course_code_list) { - var course_code_list = - window.courseType.courseCodeListFromStorage( - data.course_code_list - ); - } else { - var course_code_list = new window.courseType.CourseCodeList(); - } - const currentDate = new Date(); - const year = currentDate.getFullYear(); - const month = currentDate.getMonth(); - let startYear, endYear; - if (month < 7) { - startYear = year-1; - endYear = year; - } else { - startYear = year; - endYear = year + 1; - } - let yearRange = `${startYear}-${endYear.toString().slice(-2)}`; - console.log({yearRange}); - for (let i = 0; i < dates.length; i++) { - let date = dates[i]; - if (date.innerText == yearRange) { - var course_div = - date.parentNode.previousElementSibling - .firstElementChild; - var title = course_div.innerText; - if (course_code_list.findCourseByTitle(title)) { - console.log("already added:", title); - continue; - } - var code = title.substring(0, 8); - var detail = title.substring(9); - var url = course_div.href; - var course = new window.courseType.Course( - title, - code, - detail, - url - ); - psb_list.addCourse(course); + var dates = document.querySelectorAll(".categoryname"); + if (data.course_code_list) { + var course_code_list = + window.courseType.courseCodeListFromStorage( + data.course_code_list + ); + } else { + var course_code_list = new window.courseType.CourseCodeList(); + } + const currentDate = new Date(); + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + let startYear, endYear; + if (month < 7) { + startYear = year - 1; + endYear = year; + } else { + startYear = year; + endYear = year + 1; + } + let yearRange = `${startYear}-${endYear.toString().slice(-2)}`; + console.log({ yearRange }); + for (let i = 0; i < dates.length; i++) { + let date = dates[i]; + if (date.innerText == yearRange) { + var course_div = + date.parentNode.previousElementSibling + .firstElementChild; + var title = course_div.innerText; + if (course_code_list.findCourseByTitle(title)) { + console.log("already added:", title); + continue; } + var code = title.substring(0, 8); + var detail = title.substring(9); + var url = course_div.href; + var course = new window.courseType.Course( + title, + code, + detail, + url + ); + psb_list.addCourse(course); } - chrome.storage.sync.set({ psb_list: psb_list }); - console.log("psb:", psb_list); + } + chrome.storage.sync.set({ psb_list: psb_list }); + console.log("psb:", psb_list); }); } @@ -259,6 +259,182 @@ function CourePage_handler() { }); } +function jumpToExamBase() { + const className = document.querySelector(".h2.mb-0").textContent.substring(0, 8); + + const currentDate = new Date(); + const year = currentDate.getFullYear(); + + const jumpTabFixed = document.createElement("li"); + jumpTabFixed.classList.add("nav-item"); + jumpTabFixed.id = "jump-to-exam-base-fixed"; + + const jumpTabFixedInner = document.createElement("a"); + jumpTabFixedInner.innerText = "Exam Base"; + jumpTabFixedInner.classList.add("nav-link"); + jumpTabFixed.appendChild(jumpTabFixedInner); + + jumpTabFixed.addEventListener("click", () => { + window.open( + "https://exambase-lib-hku-hk.eproxy.lib.hku.hk/exhibits/show/exam/home?the_key=" + className + "&the_field=crs&fromYear=" + (year - 10).toString() + "&toYear=" + year.toString() + "&the_sem1=on&the_sem2=on&the_ptype1=on&the_ptype2=on&the_no_result=20&the_sort=t", + "_blank"); + }); + + const navTabs = document.querySelectorAll("[id*='nav-tabs']")[0]; + navTabs.appendChild(jumpTabFixed); + + const jumpTab = document.createElement("div"); + jumpTab.id = "jump-to-exam-base"; + jumpTab.style.position = "fixed"; + jumpTab.style.left = "50vw"; + jumpTab.style.top = "30vh"; + jumpTab.style.cursor = "pointer"; + jumpTab.style.transition = "all 0.2s, filter 0.2s"; + jumpTab.style.zIndex = 9999; + jumpTab.innerText = "✨ Exam Base ✨"; + + let fly = true; + jumpTab.addEventListener("click", () => { + const fixedRect = jumpTabFixed.getBoundingClientRect(); + const jumpTabRect = jumpTab.getBoundingClientRect(); + fly = false; + jumpTab.style.left = `${fixedRect.left + fixedRect.width / 2 - jumpTabRect.width / 2}px`; + jumpTab.style.top = `${fixedRect.top + fixedRect.height / 2 - jumpTabRect.height / 2}px`; + jumpTab.style.transition = "all 1s ease-in-out"; + setTimeout(() => { + jumpTab.style.opacity = "0"; + jumpTabFixed.style.opacity = "1"; + }, 1000); + setTimeout(() => { + jumpTab.style.display = "none"; + window.open( + "https://exambase-lib-hku-hk.eproxy.lib.hku.hk/exhibits/show/exam/home?the_key=" + className + "&the_field=crs&fromYear=" + (year - 10).toString() + "&toYear=" + year.toString() + "&the_sem1=on&the_sem2=on&the_ptype1=on&the_ptype2=on&the_no_result=20&the_sort=t", + "_blank"); + }, 2000); + chrome.storage.sync.set({ ExamBaseFly: false }); + }); + + const style = document.createElement('style'); + style.textContent = ` + #jump-to-exam-base { + position: relative; + font-weight: bold !important; + background: linear-gradient(90deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3); + background-size: 400% 400%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + animation: rainbow 3s ease infinite, pulse 1.5s infinite alternate; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.8); + } + #jump-to-exam-base::before { + content: ""; + position: absolute; + top: -3px; + left: -3px; + right: -3px; + bottom: -3px; + border-radius: 5px; + background: linear-gradient(45deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3); + background-size: 400% 400%; + z-index: -1; + filter: blur(8px); + animation: rainbow 3s ease infinite; + opacity: 0.7; + } + #jump-to-exam-base { + position: fixed !important; + transform: scale(1.1); + z-index: 9999; + margin: 0 5px; + pointer-events: auto; + } + @keyframes rainbow { + 0% { background-position: 0% 50% } + 50% { background-position: 100% 50% } + 100% { background-position: 0% 50% } + } + @keyframes pulse { + 0% { transform: scale(1); } + 100% { transform: scale(1.1); } + } + `; + document.head.appendChild(style); + document.body.appendChild(jumpTab); + + // 随机飘动动画 + let vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + let vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + let pos = { x: vw / 2, y: vh / 3 }; + let dx = (0.5 + 0.5 * Math.random()) * 10; + let dy = (0.5 + 0.5 * Math.random()) * 10; + let benchmark = { x: dx, y: dy }; + console.log({ benchmark }); + + window.addEventListener('resize', () => { + vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + }); + + setInterval(() => { + if (fly) { + var rect = jumpTab.getBoundingClientRect(); + pos.x += dx; + pos.y += dy; + if (pos.x < 0 || pos.x > vw - rect.width) { + dx = -dx; + pos.x = Math.max(0, Math.min(pos.x, vw - rect.width)); + } + if (pos.y < 0 || pos.y > vh - rect.height) { + dy = -dy; + pos.y = Math.max(0, Math.min(pos.y, vh - rect.height)); + } + dx = Math.abs(dx) > Math.abs(benchmark.x) ? dx * 0.8 : dx; + dy = Math.abs(dy) > Math.abs(benchmark.y) ? dy * 0.8 : dy; + + jumpTab.style.left = pos.x + "px"; + jumpTab.style.top = pos.y + "px"; + } + }, 50); + + var maxSpeed = 30; + document.addEventListener('mousemove', (e) => { + var rect = jumpTab.getBoundingClientRect(); + if (Math.abs(e.clientX - (rect.left + rect.width / 2)) + Math.abs(e.clientY - (rect.top + rect.height / 2)) < 150) { + let deltaX = e.clientX - (rect.left + rect.width / 2); + let deltaY = e.clientY - (rect.top + rect.height / 2); + if (deltaX !== 0) { + dx = dx + -1 / deltaX * 150; + } + if (deltaY !== 0) { + dy = dy + -1 / deltaY * 150; + } + dx = Math.max(-maxSpeed, Math.min(dx, maxSpeed)); + dy = Math.max(-maxSpeed, Math.min(dy, maxSpeed)); + } + }); + + + // 悬停时变大旋转 + jumpTab.addEventListener('mouseenter', () => { + jumpTab.style.transform = 'scale(1.3) rotate(-10deg)'; + jumpTab.style.filter = 'brightness(1.5)'; + }); + jumpTab.addEventListener('mouseleave', () => { + jumpTab.style.transform = 'scale(1.1)'; + jumpTab.style.filter = ''; + }); + + chrome.storage.sync.get(["ExamBaseFly"], (data) => { + if (data.ExamBaseFly) { + jumpTabFixed.style.opacity = "0"; + jumpTab.style.display = "block"; + } else { + jumpTabFixed.style.opacity = "1"; + jumpTab.style.display = "none"; + } + }); +} + function popupOnStart() { chrome.runtime .sendMessage({ @@ -277,6 +453,7 @@ const route = () => { if (currentURL.includes("moodle.hku.hk/course/view.php") || (currentURL.includes("https://moodle.hku.hk/mod/") && currentURL.includes("view.php"))) { // 课程页面 CourePage_handler(); + jumpToExamBase(); } else if (currentURL.includes("moodle.hku.hk/my/courses.php")) { // my courses CourseList_handler(); @@ -290,8 +467,8 @@ const route = () => { get_psb(); initialize(); popupOnStart(); - } ; - + }; + if (currentURL.includes("https://moodle.hku.hk/course/view.php?id=")) { window.navigatorUtils.customizeCenter({ style: { diff --git a/scripts/setting/jsx/Setting.js b/scripts/setting/jsx/Setting.js index 27ec093..fa54e2a 100644 --- a/scripts/setting/jsx/Setting.js +++ b/scripts/setting/jsx/Setting.js @@ -34,7 +34,7 @@ async function saveUser(username, password) { // add data to list data_list.push(data); // save list to storage - chrome.storage.sync.set({ list: data_list }, function () {}); + chrome.storage.sync.set({ list: data_list }, function () { }); if (data_list.length === 1) { // save data to storage @@ -53,12 +53,12 @@ async function saveUser(username, password) { return await result; } -window.popup.SetUserForm = function(custom, inner) { +window.popup.SetUserForm = function (custom, inner) { async function onSubmit(e) { e.preventDefault(); var username = document.getElementById("fasthku-username").value; var password = document.getElementById("fasthku-password").value; - if (username.includes("@connect.hku.hk")){ + if (username.includes("@connect.hku.hk")) { username = username.split("@")[0]; } @@ -110,13 +110,13 @@ window.popup.SetUserForm = function(custom, inner) { type: "text", id: "fasthku-username", placeholder: "Username", - style : textInputStyle + style: textInputStyle }), window.elements.Input({ type: "password", id: "fasthku-password", placeholder: "Password", - style : textInputStyle + style: textInputStyle }), window.elements.Button({ id: "fasthku-submit", @@ -168,6 +168,40 @@ function LogoutButton(custom, inner) { }); } +async function ExamBaseButton(custom, inner) { + return window.elements.Div({}, [ + window.elements.Input( + { + type: "checkbox", + id: "ez-exam-base-checkbox", + style: { + width: "20px", + height: "20px", + marginRight: "10px", + marginTop: "5px", + }, + checked: (await window.utils.getStorage('ExamBaseFly')).ExamBaseFly || false, + onClick: async function (e) { + let isChecked = e.target.checked; + await chrome.storage.sync.set({ + ExamBaseFly: isChecked, + }); + + }, + }, [] + ), + window.elements.Label({ + htmlFor: "ez-exam-base-checkbox", + innerText: "Skyrocketing Exam Base", + style: { + fontSize: "16px", + fontWeight: "bold", + color: "#555555", + }, + }), + ]) +} + function SettingBlock(name, element) { return window.elements.Div({ @@ -198,9 +232,10 @@ window.popup.SettingPopup = async function SettingPopup(custom, inner) { width: "100%", margin: "auto", } - },[ + }, [ SettingBlock("Set User", window.popup.SetUserForm()), SettingBlock("Quick Logout", LogoutButton()), + SettingBlock("Exam Base", await ExamBaseButton()), ] ) )