Skip to content

Commit

Permalink
Merge branch 'main' into tips-page
Browse files Browse the repository at this point in the history
  • Loading branch information
aiden2480 committed Jun 28, 2022
2 parents dd5bba1 + 52322a7 commit b169ec6
Show file tree
Hide file tree
Showing 8 changed files with 322 additions and 58 deletions.
63 changes: 53 additions & 10 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var defaultsets = [
"会知思言立使作住生食飲休洗動働" +
"通歩待泊教始終何物自名方紙全活飯色"
},
]; // ID -1 for all kanji and -2 for favourited kanji
];


/* Set up a listener so we can receive messages from the console
Expand Down Expand Up @@ -100,22 +100,30 @@ chrome.storage.onChanged.addListener(async (changes, namespace) => {
});

/* Configuration functions called above */
/**
* Ensures that the correct browser icon is being displayed
*/
async function ensureCorrectKanjiIcon() {
var { customsets, selectedset, selectedkanji } = await chrome.storage.local.get();
if ([ customsets, selectedset, selectedkanji ].includes(undefined)) return;

setBrowserIcon(customsets[selectedset].kanji[selectedkanji], bypass=true);
}

/**
* Ensures the "Beta" badge is displayed if necessary
*/
async function ensureBetaBadge() {
// Ensure that the "Beta" badge is present if necessary

if ((await chrome.management.getSelf()).installType === "development") {
chrome.action.setBadgeText({ text: "B" });
chrome.action.setBadgeBackgroundColor({ color: "#304db6" });
}
}

/**
* Ensures that the default configuration is present if no data can be found
* within the chrome storage API.
*/
async function ensureDefaultConfiguration() {
// Create default sets
var sets = (await chrome.storage.local.get("customsets")).customsets;
Expand All @@ -126,7 +134,12 @@ async function ensureDefaultConfiguration() {
(config === undefined) && await createDefaultConfig();
}

/* Script to change the browser icon */
/**
* Sets the browser icon to the currently selected character
*
* @param {Char} kanji The character to set the browser icon to
* @param {Boolean} bypass Bypass same-kanji check
*/
function setBrowserIcon(kanji, bypass=false) {
// https://jsfiddle.net/1u37ovj9/
if (current === kanji && !bypass) return;
Expand All @@ -149,7 +162,9 @@ function setBrowserIcon(kanji, bypass=false) {
chrome.action.setIcon({ imageData }, () => console.log(`Set browser icon to %c${kanji}`, "color: #7289da"));
}

/* Creates defult configuration as required by ensureDefaultConfiguration */
/**
* Creates defult configuration as required by ensureDefaultConfiguration
*/
async function createKanjiSets() {
// {id: ..., name: ..., kanji: ..., enabled: ...}

Expand All @@ -163,6 +178,9 @@ async function createKanjiSets() {
await chrome.storage.local.set({ customsets });
}

/**
* Creates the default configuration if need be
*/
async function createDefaultConfig() {
var { videoSpeed, settingsbtn } = await chrome.storage.local.get(["videoSpeed", "settingsbtn"]);
(videoSpeed !== undefined) || await chrome.storage.local.set({ videoSpeed: 0.8 });
Expand All @@ -171,13 +189,17 @@ async function createDefaultConfig() {

/* Context menus */
chrome.storage.onChanged.addListener(async (changes, namespace) => {
if (!"customsets" in changes) return;
if (!("customsets" in changes)) return;

chrome.contextMenus.removeAll(() => {
generateContextMenus();
});
});

/**
* Generates the context menus required for each of the sets, as well as a button
* to create a new unnamed set with the selected characters.
*/
async function generateContextMenus() {
var sets = (await chrome.storage.local.get("customsets")).customsets || [];

Expand Down Expand Up @@ -212,6 +234,7 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => {

const ANY_REGEX = /[\u4E00-\u9FAF]+/g;
const match = info.selectionText.match(ANY_REGEX)?.join("");
const isPopup = tab.url == chrome.runtime.getURL("popup.html");

// Show a little x for a second if an error occured
if (!match) return await displayBadge(tab, "X", "#D9381E", 3000);
Expand All @@ -227,6 +250,11 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => {

await chrome.storage.local.set({ customsets: sets });
await displayBadge(tab, "✓", "#32CD32", 3000);

if (isPopup) await chrome.storage.local.set({
selectedunit: sets.at(-1).id,
selectedkanji: 0,
});
}

if (info.menuItemId.startsWith("addtoset")) {
Expand All @@ -237,26 +265,41 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => {

await chrome.storage.local.set({ customsets: sets });
await displayBadge(tab, "✓", "#32CD32", 3000);

if (isPopup) await chrome.storage.local.set({
selectedunit: set.id,
selectedkanji: 0,
});
}

// TODO If the ctx menu is being used from inside the extension,
// Automatically add the kanji to the currently selected set and
// load it
});

async function displayBadge(tab, text, color, milliseconds) {
/**
* Shows a badge on the extenion for a specified amount of time
*
* @param {Tab} tab The tab to show the badge for
* @param {String} text The badge text
* @param {Colour} colour The badge colour
* @param {Number} milliseconds The number of ms to show the badge for
*/
async function displayBadge(tab, text, colour, milliseconds) {
if (tab.id < 0) tab = await chrome.tabs.query({ active: true });

var current = {
color: await chrome.action.getBadgeBackgroundColor({ tabId: tab.id }),
colour: await chrome.action.getBadgeBackgroundColor({ tabId: tab.id }),
text: await chrome.action.getBadgeText({ tabId: tab.id }),
}

// Set text cross
await chrome.action.setBadgeBackgroundColor({ color });
await chrome.action.setBadgeBackgroundColor({ color: colour });
await chrome.action.setBadgeText({ text })

// Schedule return to current
return setTimeout(async () => {
await chrome.action.setBadgeBackgroundColor({ color: current.color });
await chrome.action.setBadgeBackgroundColor({ color: current.colour });
await chrome.action.setBadgeText({ text: current.text });
}, milliseconds);
}
1 change: 1 addition & 0 deletions css/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ div#infosection #exampleslist * {
white-space: nowrap;
overflow: hidden;
list-style-position: inside;
cursor: pointer;
}

/* Kanji info div */
Expand Down
2 changes: 1 addition & 1 deletion js/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ window.addEventListener("load", () => {
});

let coords = {x: 0, y: 0};
let vidloading = false; // Updated in popup.js
let vidloading = false; // Updated in popup.js
let paint = false;

function updatePosition(event) {
Expand Down
19 changes: 18 additions & 1 deletion js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ document.getElementById("tipsbutton").addEventListener("click", () => {
window.open("/tips.html");
});

// Helper function to transform date to human readable time
/**
* Helper function to transform a date to a human readable time
*
* @param {DateTime} datestamp The datestamp to process
* @returns {String} A human readable delta between the datestamp and the present
*/
function howLongAgo(datestamp) {
var delta = (new Date() - new Date(datestamp)) / 1000;
const plural = (s) => s !== 1 ? "s" : "";
Expand All @@ -68,6 +73,12 @@ function howLongAgo(datestamp) {
return `${delta} second${plural(delta)} ago`
}

/**
* Parses a minimal amount of markdown within a body of text and converts it to HTML
*
* @param {String} soup The markdown soup to be parsed
* @returns {String} The parsed soup
*/
function makeHTML(soup) {
const urls = /\[`(\w+)`]\((\S+)\)/gm; // Any URLS referencing Git commits
const bold = /\*{2}(.+)\*{2}/gm; // Text surrounded by asterisks
Expand Down Expand Up @@ -142,6 +153,12 @@ function makeHTML(soup) {
return soup;
}

/**
* Escapes certain HTML-sensitive characters within a string
*
* @param {String} str The string to process
* @returns {String} The processed string
*/
function escapeHTML(str) {
return str.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
Expand Down
100 changes: 82 additions & 18 deletions js/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async function loadKanjiIndex(index) {
vidloading = true;
video.src = "/media/loading.png";
utils.fetchKanjiDetails(kanji).then(details => {
video.src = details.video;
video.src = details.kanji.video.mp4;
})

// Update browser icon
Expand All @@ -46,7 +46,7 @@ async function loadKanjiSet(setID, index=0) {
selectedset.value = setID;
var set = await utils.fetchSetFromID(setID);

console.log("Loading set ", set);
console.debug("Loading set ", set);
selectedkanji.innerHTML = null;

// Update current unit in database
Expand All @@ -64,8 +64,10 @@ async function loadKanjiSet(setID, index=0) {
loadKanjiIndex(index);
}

/* Add event listeners for the various elements */
window.addEventListener("load", async () => {
async function windowLoad() {
// First ensure that any set is available
await utils.fetchAnySet();

// Load the custom sets into the selector menu
(await utils.fetchAllSets()).forEach(set => {
if (!set.enabled) return;
Expand All @@ -81,13 +83,59 @@ window.addEventListener("load", async () => {
document.getElementById("settings").style.visibility = settingsbtn ? "visible" : "hidden";

// Load the selected kanji once prepared
var result = await chrome.storage.local.get(["selectedset", "selectedkanji"]);
let setID = result.selectedset !== undefined ? parseInt(result.selectedset) : (await utils.fetchAnySet()).id;
let kanjiIndex = result.selectedkanji !== undefined ? parseInt(result.selectedkanji) : 0;
var result = await chrome.storage.local.get(["selectedset", "selectedkanji", "customsets"]);

/**
* Ensures that the set ID retrieved from storage has not been deleted
*
* @param {Numeric} unparsed The unparsed set ID
* @returns {Integer} The updated set ID
*/
async function getSetID(unparsed) {
let int = parseInt(unparsed);
let set = result.customsets.find(item => int && item.id == int);

// Couldn't be parsed as integer or set doesn't exist/isn't enabled
if (!int || !set || !set.enabled) {
return (await utils.fetchAnySet()).id;
}

return int;
}

/**
* Ensures that the kanji index retrieved from storage is still valid.
* It would become invalid if, for example, the set is updated to be
* only 4 chars long via the settings page although the index 5 is
* saved in the storage.
*
* @param {Integer} setID The string version of the set ID
* @param {Numeric} unparsed The string version of the kanji index
* @returns {Integer} The updated kanji index
*/
async function getKanjiIndex(setID, unparsed) {
let int = parseInt(unparsed);
let set = result.customsets.find(item => item.id == setID);

// Couldn't be parsed as integer or index out of range
if (!int || set.kanji.length < int + 1) {
return 0
}

return int;
}

let setID = await getSetID(result.selectedset);
let kanjiIndex = await getKanjiIndex(setID, result.selectedkanji)

await loadKanjiSet(setID, kanjiIndex);
selectedset.value = setID;
selectedkanji.value = kanjiIndex;
}

/* Add event listeners for the various elements */
window.addEventListener("load", async () => {
await windowLoad();
});

video.addEventListener("loadeddata", () => {
Expand Down Expand Up @@ -252,42 +300,58 @@ Array.prototype.random = function () {
return this[Math.floor(Math.random() * this.length)];
}

/* API call functions */
/**
* Loads in all the data for the character into the side panel.
* This includes sample words, onyomi/kunyomi, difficulty, and
* stroke order.
*
* @param {Char} kanji The character to populate information for
*/
async function populateInformation(kanji) {
// TODO move this to the utilities.js function
var json = await utils.fetchKanjiDetails(kanji);
if (selectedkanji.selectedOptions[0].innerText != kanji) return;

var listelem = document.getElementById("exampleslist");

// Establish readings
var on = json.onyomi_ja ? json.onyomi_ja.split("、") : [];
var kun = json.kunyomi_ja ? json.kunyomi_ja.split("、") : [];
var unparsed_on = json.kanji.onyomi.katakana;
var unparsed_kun = json.kanji.kunyomi.hiragana;

var on = unparsed_on != "n/a" ? unparsed_on.split("、") : [];
var kun = unparsed_kun != "n/a" ? unparsed_kun.split("、") : [];
var readings = on.concat(kun).join("、");

// Populate kanji details
document.getElementById("selectedkanjidetails").textContent = kanji;
document.getElementById("selectedkanjimeaning").textContent = json.kmeaning;
document.getElementById("strokecount").innerText = json.kstroke;
document.getElementById("grade").innerText = json.kgrade;
document.getElementById("selectedkanjimeaning").textContent = json.kanji.meaning.english;
document.getElementById("strokecount").innerText = json.kanji.strokes.count;
document.getElementById("grade").innerText = json.references.grade;
document.getElementById("onkunyomi").innerText = readings;

// Add title to reading parent element
var parent = document.getElementById("onkunyomi").parentElement;
parent.title = `おん${json.onyomi_ja || "none"}\nくん${json.kunyomi_ja || "none"}\n\n`;
parent.title = `Onyomi${on.join("、") || "none"}\nKunyomi ${kun.join("、") || "none"}\n\n`;
parent.title += "Onyomi are in katakana, while kunyomi are in hiragana";

// Populate examples
listelem.textContent = null;
(json.examples || []).splice(0, 6).map(item => {
let elem = document.createElement("li");
let reading = document.createElement("b");
let meaning = document.createTextNode(item[1]);
reading.innerText = item[0];
let meaning = document.createTextNode(item.meaning.english);
reading.innerText = item.japanese;

elem.appendChild(reading);
elem.appendChild(meaning);
elem.title = item[0] + item[1];
elem.title = item.japanese + item.meaning.english;

// Only play audio if the mouse wasn't dragged and it's left click
let audio = new Audio(item.audio.mp3)
let drag = false;

elem.addEventListener("mousedown", () => drag = false);
elem.addEventListener("mousemove", () => drag = true);
elem.addEventListener("mouseup", (e) => (e.button == 0 && !drag) && audio.play());

listelem.appendChild(elem);
});
Expand Down

0 comments on commit b169ec6

Please sign in to comment.