diff --git a/package-lock.json b/package-lock.json index e959fba..e20323d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "desmos-unlocked", - "version": "1.0.6", + "version": "1.0.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "desmos-unlocked", - "version": "1.0.6", + "version": "1.0.7", "license": "MIT", "dependencies": { "git-hooks-plus": "^1.0.1", diff --git a/package.json b/package.json index 034a3e0..1a0ce59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "desmos-unlocked", - "version": "1.0.6", + "version": "1.0.7", "description": "Browser extension for better user control of the Desmos graphing calculator configuration ", "main": "background.js", "scripts": { @@ -9,13 +9,13 @@ "code:prettier": "prettier --write '**/**/*.{ts,js,html,css,json}'", "code:check": "./node_modules/.bin/tsc --noEmit && prettier --check '**/**/*.{ts,js,html,css,json}' && eslint '**/**/*.ts'", "app:chrome": "rimraf dist && cross-env BROWSER='chrome' webpack --config webpack/webpack.prod.js", - "app:chrome-dev": "rimraf dist && cross-env BROWSER='chrome' webpack --config webpack/webpack.dev.js --watch", + "app:chrome-dev": "cross-env BROWSER='chrome' webpack --config webpack/webpack.dev.js --watch", "app:edge": "rimraf dist && cross-env BROWSER='edge' webpack --config webpack/webpack.prod.js", - "app:edge-dev": "rimraf dist && cross-env BROWSER='edge' webpack --config webpack/webpack.dev.js --watch", + "app:edge-dev": "cross-env BROWSER='edge' webpack --config webpack/webpack.dev.js --watch", "app:opera": "rimraf dist && cross-env BROWSER='opera' webpack --config webpack/webpack.prod.js", - "app:opera-dev": "rimraf dist && cross-env BROWSER='opera' webpack --config webpack/webpack.dev.js --watch", + "app:opera-dev": "cross-env BROWSER='opera' webpack --config webpack/webpack.dev.js --watch", "app:firefox": "rimraf dist && cross-env BROWSER='firefox' webpack --config webpack/webpack.prod.js", - "app:firefox-dev": "rimraf dist && cross-env BROWSER='firefox' webpack --config webpack/webpack.dev.js --watch" + "app:firefox-dev": "cross-env BROWSER='firefox' webpack --config webpack/webpack.dev.js --watch" }, "repository": { "type": "git", diff --git a/public/chrome_manifest.json b/public/chrome_manifest.json index a8ae77b..7ebd509 100644 --- a/public/chrome_manifest.json +++ b/public/chrome_manifest.json @@ -1,6 +1,6 @@ { "name": "Desmos Unlocked", - "version": "1.0.6", + "version": "1.0.7", "description": "Browser extension for better user control of the Desmos graphing calculator configuration", "permissions": ["storage", "declarativeNetRequest", "declarativeNetRequestWithHostAccess", "webRequest"], "manifest_version": 3, diff --git a/public/edge_manifest.json b/public/edge_manifest.json index a8ae77b..7ebd509 100644 --- a/public/edge_manifest.json +++ b/public/edge_manifest.json @@ -1,6 +1,6 @@ { "name": "Desmos Unlocked", - "version": "1.0.6", + "version": "1.0.7", "description": "Browser extension for better user control of the Desmos graphing calculator configuration", "permissions": ["storage", "declarativeNetRequest", "declarativeNetRequestWithHostAccess", "webRequest"], "manifest_version": 3, diff --git a/public/firefox_manifest.json b/public/firefox_manifest.json index 25359b9..c3aad97 100644 --- a/public/firefox_manifest.json +++ b/public/firefox_manifest.json @@ -1,6 +1,6 @@ { "name": "Desmos Unlocked", - "version": "1.0.6", + "version": "1.0.7", "description": "Browser extension for better user control of the Desmos graphing calculator configuration", "permissions": ["https://*.desmos.com/*", "storage", "webRequest", "webRequestBlocking"], "manifest_version": 2, diff --git a/public/opera_manifest.json b/public/opera_manifest.json index a8ae77b..7ebd509 100644 --- a/public/opera_manifest.json +++ b/public/opera_manifest.json @@ -1,6 +1,6 @@ { "name": "Desmos Unlocked", - "version": "1.0.6", + "version": "1.0.7", "description": "Browser extension for better user control of the Desmos graphing calculator configuration", "permissions": ["storage", "declarativeNetRequest", "declarativeNetRequestWithHostAccess", "webRequest"], "manifest_version": 3, diff --git a/public/popup.css b/public/popup.css index 2a08c5c..eabb555 100644 --- a/public/popup.css +++ b/public/popup.css @@ -224,3 +224,9 @@ h2 { min-height: 0; height: 0.8em; } + +.header-toggle { + display: grid; + grid-template-columns: 260px 100px; + align-items: center; +} diff --git a/public/popup.html b/public/popup.html index 84b9025..dd088b7 100644 --- a/public/popup.html +++ b/public/popup.html @@ -62,6 +62,40 @@

Breakout characters

+
+
+

Auto-parenthesize functions

+
+ + +
+
+
+
+

Enable shortcuts in subscripts

+
+ + +
+

diff --git a/src/background/chrome/background.ts b/src/background/chrome/background.ts index c1da6e2..874887b 100644 --- a/src/background/chrome/background.ts +++ b/src/background/chrome/background.ts @@ -7,6 +7,8 @@ chrome.runtime.onInstalled.addListener(function () { autoCommands: "keepmeKEEPME alpha beta sqrt theta Theta phi Phi pi Pi tau nthroot cbrt sum prod int ans percent infinity infty gamma Gamma delta Delta epsilon epsiv zeta eta kappa lambda Lambda mu xi Xi rho sigma Sigma chi Psi omega Omega digamma iota nu upsilon Upsilon Psi square mid parallel nparallel perp times div approx", charsThatBreakOutOfSupSub: "+-=<>*", + isAutoParenEnabled: false, + disableAutoSubstitutionInSubscripts: true, }); }); diff --git a/src/background/edge/background.ts b/src/background/edge/background.ts index c1da6e2..874887b 100644 --- a/src/background/edge/background.ts +++ b/src/background/edge/background.ts @@ -7,6 +7,8 @@ chrome.runtime.onInstalled.addListener(function () { autoCommands: "keepmeKEEPME alpha beta sqrt theta Theta phi Phi pi Pi tau nthroot cbrt sum prod int ans percent infinity infty gamma Gamma delta Delta epsilon epsiv zeta eta kappa lambda Lambda mu xi Xi rho sigma Sigma chi Psi omega Omega digamma iota nu upsilon Upsilon Psi square mid parallel nparallel perp times div approx", charsThatBreakOutOfSupSub: "+-=<>*", + isAutoParenEnabled: false, + disableAutoSubstitutionInSubscripts: true, }); }); diff --git a/src/background/firefox/background.ts b/src/background/firefox/background.ts index a8d1d5b..21d88b8 100644 --- a/src/background/firefox/background.ts +++ b/src/background/firefox/background.ts @@ -7,6 +7,8 @@ browser.runtime.onInstalled.addListener(function () { autoCommands: "keepmeKEEPME alpha beta sqrt theta Theta phi Phi pi Pi tau nthroot cbrt sum prod int ans percent infinity infty gamma Gamma delta Delta epsilon epsiv zeta eta kappa lambda Lambda mu xi Xi rho sigma Sigma chi Psi omega Omega digamma iota nu upsilon Upsilon Psi square mid parallel nparallel perp times div approx", charsThatBreakOutOfSupSub: "+-=<>*", + isAutoParenEnabled: false, + disableAutoSubstitutionInSubscripts: true, }); }); diff --git a/src/background/opera/background.ts b/src/background/opera/background.ts index c1da6e2..874887b 100644 --- a/src/background/opera/background.ts +++ b/src/background/opera/background.ts @@ -7,6 +7,8 @@ chrome.runtime.onInstalled.addListener(function () { autoCommands: "keepmeKEEPME alpha beta sqrt theta Theta phi Phi pi Pi tau nthroot cbrt sum prod int ans percent infinity infty gamma Gamma delta Delta epsilon epsiv zeta eta kappa lambda Lambda mu xi Xi rho sigma Sigma chi Psi omega Omega digamma iota nu upsilon Upsilon Psi square mid parallel nparallel perp times div approx", charsThatBreakOutOfSupSub: "+-=<>*", + isAutoParenEnabled: false, + disableAutoSubstitutionInSubscripts: true, }); }); diff --git a/src/content.ts b/src/content.ts index 2891e5a..e0a00cf 100644 --- a/src/content.ts +++ b/src/content.ts @@ -9,7 +9,12 @@ declare const cloneInto: ((toClone: MathQuillConfig, context: any) => MathQuillC async function updateConfig(changes?: browser.storage.ChangeDict) { let config: MathQuillConfig; if (typeof changes === "undefined") { - config = await browser.storage.local.get(["autoCommands", "charsThatBreakOutOfSupSub"]); + config = await browser.storage.local.get([ + "autoCommands", + "charsThatBreakOutOfSupSub", + "isAutoParenEnabled", + "disableAutoSubstitutionInSubscripts", + ]); } else { config = {}; Object.keys(changes).forEach((configOption) => (config[configOption] = changes[configOption].newValue)); diff --git a/src/globals/config.ts b/src/globals/config.ts index 93c579a..4759182 100644 --- a/src/globals/config.ts +++ b/src/globals/config.ts @@ -1,4 +1,7 @@ export interface MathQuillConfig { autoCommands?: string; charsThatBreakOutOfSupSub?: string; + isAutoParenEnabled?: boolean; + autoParenthesizedFunctions?: string; + disableAutoSubstitutionInSubscripts?: string; } diff --git a/src/popup.ts b/src/popup.ts index c7acfbf..31d3e21 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,40 +1,77 @@ import { desmosDefualtAutoCommands, basicAutoCommands, advancedAutoCommands } from "./utils/autoCommands"; import { massSet, storeConfig, populateGrid } from "./utils/utils"; -const setToDefault = document.getElementById("set-to-default"); -const setToDesmosDefault = document.getElementById("set-to-desmos-default"); -const breakoutChars = document.querySelector("#breakout textarea"); -const setChars = document.getElementById("set-chars"); - -browser.storage.local - .get("charsThatBreakOutOfSupSub") - .then((stored) => (breakoutChars.value = stored.charsThatBreakOutOfSupSub.toString())); - -setToDefault.onclick = function () { - massSet( - Array.from(document.querySelectorAll("#desmos-default .latex-item, #basic .latex-item")) - .map((item) => item.id) - .filter(function (item) { - return item !== "ge" && item !== "le" && item !== "ne" && item !== "pm" && item !== "mp"; - }), - "autoCommands" - ); -}; +document.addEventListener("DOMContentLoaded", async () => { + const breakoutChars = document.querySelector("#breakout textarea"); + const setChars = document.getElementById("set-chars"); + + browser.storage.local + .get("charsThatBreakOutOfSupSub") + .then((stored) => (breakoutChars.value = stored.charsThatBreakOutOfSupSub.toString())); + + setChars.onclick = function () { + browser.storage.local.set({ charsThatBreakOutOfSupSub: breakoutChars.value }); + }; + + const autoParen = document.querySelector("#auto-paren .onoffswitch .onoffswitch-checkbox"); + autoParen.addEventListener("click", async () => { + const stored = await browser.storage.local.get("isAutoParenEnabled"); + browser.storage.local.set({ isAutoParenEnabled: !stored.isAutoParenEnabled }); + }); + browser.storage.local.get("isAutoParenEnabled").then((stored) => { + autoParen.checked = stored.isAutoParenEnabled as boolean; + }); -setToDesmosDefault.onclick = function () { - massSet( - Array.from(document.querySelectorAll("#desmos-default .latex-item")).map((item) => item.id), - "autoCommands" + const subscriptShortcuts = document.querySelector( + "#shortcuts-in-subscripts .onoffswitch .onoffswitch-checkbox" ); -}; + subscriptShortcuts.addEventListener("click", async () => { + const stored = await browser.storage.local.get("disableAutoSubstitutionInSubscripts"); + browser.storage.local.set({ disableAutoSubstitutionInSubscripts: !stored.disableAutoSubstitutionInSubscripts }); + }); + browser.storage.local.get("disableAutoSubstitutionInSubscripts").then((stored) => { + // Note that in the UI we represent the option as "Enable shortcuts in subscripts" + // (toggle on means shortcuts enabled), while the MathQuill API has it reversed + // (disableAutoSubstitutionInSubscripts === true means shortcuts disabled). + subscriptShortcuts.checked = !stored.disableAutoSubstitutionInSubscripts as boolean; + }); + + const setToDefault = document.getElementById("set-to-default"); + const setToDesmosDefault = document.getElementById("set-to-desmos-default"); -setChars.onclick = function () { - browser.storage.local.set({ charsThatBreakOutOfSupSub: breakoutChars.value }); -}; + const setToDefaultCommon = () => { + if (autoParen.checked) { + autoParen.click(); + } + if (subscriptShortcuts.checked) { + subscriptShortcuts.click(); + } + breakoutChars.value = "+-=<>*"; + setChars.click(); + }; -// Add all the dynamically loaded nodes to the DOM using templates and give -// sliders their funcionality -async function initialize() { + setToDefault.onclick = function () { + massSet( + Array.from(document.querySelectorAll("#desmos-default .latex-item, #basic .latex-item")) + .map((item) => item.id) + .filter(function (item) { + return item !== "ge" && item !== "le" && item !== "ne" && item !== "pm" && item !== "mp"; + }), + "autoCommands" + ); + setToDefaultCommon(); + }; + + setToDesmosDefault.onclick = function () { + massSet( + Array.from(document.querySelectorAll("#desmos-default .latex-item")).map((item) => item.id), + "autoCommands" + ); + setToDefaultCommon(); + }; + + // Add all the dynamically loaded nodes to the DOM using templates and give + // sliders their funcionality const { autoCommands }: { autoCommands: string } = await browser.storage.local.get("autoCommands"); populateGrid("grid-item-template", "desmos-default", desmosDefualtAutoCommands, autoCommands); populateGrid("grid-item-template", "basic", basicAutoCommands, autoCommands); @@ -44,6 +81,4 @@ async function initialize() { document.querySelectorAll(".latex-item").forEach(function (item) { item.querySelector(".onoffswitch-checkbox").addEventListener("click", storeConfig); }); -} - -document.addEventListener("DOMContentLoaded", initialize); +}); diff --git a/src/script.ts b/src/script.ts index a386bf0..15ff257 100644 --- a/src/script.ts +++ b/src/script.ts @@ -6,9 +6,38 @@ import { MathQuillConfig } from "./globals/config"; const handler = (({ detail }: CustomEvent) => { // Have to wait for all the preload modifications to finish pollForValue(() => window.Desmos?.MathQuill?.config).then(() => { + // detail.isAutoParenEnabled could be undefined. We have to + // check if it is strictly true or false. Stay classy, JavaScript. + if (detail.isAutoParenEnabled === true) { + detail.autoParenthesizedFunctions = getAutoOperators(); + } else if (detail.isAutoParenEnabled === false) { + // The MathQuill API requires a space-delimited list + // of only letters. An empty string will throw an error. + // So we set the autoParenthesizedFunctions to this + // placeholder that is not even a function anyway (and + // is unlikely to ever be made one). + detail.autoParenthesizedFunctions = "THISISNOTAFUNCTION"; + } + delete detail.isAutoParenEnabled; window.Desmos.MathQuill.config(detail); document.removeEventListener("send-config", handler); }); }) as EventListener; document.addEventListener("send-config", handler); + +function getAutoOperators() { + // Get all the autoOperatorNames in the first editable field in the expression + // window. Then, filter out the ones that are not only letters, in order to comply + // with the MathQuill API. Then return the string of all these names. + // Another approach would be use window.require("main/mathquill-operators").getAutoOperators() + // and then remove the '|'s included in that string, but those operators returned by the + // require are the Desmos defaults. It's possible that someone might override those defaults + // (e.g. through DesModder). But then again, someone overriding the defaults might just do + // it by overriding the main/mathquill-operators module anyway 🤷. + return Object.keys( + window.Desmos.MathQuill(document.querySelector(".dcg-mq-editable-field")).__options.autoOperatorNames + ) + .filter((opName) => /^[a-zA-Z]+$/.test(opName)) + .join(" "); +}