Community

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
tom_gibbons
Community Contributor

Math Editor keyboard shortcut? Save me from carpal tunnel...

Jump to solution

Please tell me that someone knows of a keyboard shortcut to access the equation editor from the RCE. 

I'm remediating a calculus course that was originally created in Bb. I'm recreating all of the equations, because the WIRIS content exported by Bb was so buggy/broken. There are like 15-20 mathematical expressions in every quiz question, and I'm kind of going bonkers with swapping back and forth from keyboard to mouse and back.

I'm pretty good with the math editor and keystrokes for formatting, but needing to click several times to get to the editor is getting to me. 

Labels (3)
1 Solution

Accepted Solutions
nagami
Community Member

I was also frustrated by this, so I learned how to program a Chrome Extension just to fix it. Given that I've never programmed browser extensions before, this is a lot of effort to put into a problem on the user end..

I can't pretend this is the best code in the world - I'm an amateur! I also can't guarantee that it will work for FUTURE versions of Canvas, only the version that I'm using now. But it's at least a workaround! Currently, this extension creates shortcuts for:

* Superscript

* Subscript

* Insert equation

I can make edits if people want more!

(The following procedure is, to the very best of my limited ability and knowledge, safe to use. However, you should always look over any code you get from the Internet to make sure it's not suspicious before executing it! I will explain my code as I give it to you, to help allay any fears.)

This is an involved process, so I hope you have a little time. It also requires some basic technical skills. It worked for me; please let me know if it works for you!

Instructions for adding shortcuts to the rich content editor, including equation editor shortcut: (Chrome only)

1. Make a folder on own local hard drive. (Not a cloud folder.) You can call it anything you want. I called it CustomChromeShortcuts.

2. Inside that folder, use a plain text editor or a .JSON editor to make a new file called manifest.json

(Make sure it doesn't have some kind of hidden extension; some editors will quietly save files like this as manifest.json.txt)

Note: I used an editor called Brackets, but other editors will work. It's important that your editor not add a lot of special characters to this. I think Windows Notepad or Mac Textedit will work fine, too.

3. Put exactly these contents into the manifest.json file.

{
    "name": "Canvas Custom Keyboard Shortcuts",
    "version": "1.0",
    "description": "Add custom shortcuts to Canvas",
    "manifest_version": 2,
    "permissions": ["https://*.instructure.com/*"],
    "content_scripts": [
        {
            "matches": ["https://*.instructure.com/*"],
            "match_about_blank": true,
            "js": ["js/contentScript.js"]
        }
    ],
    "web_accessible_resources": ["js/shortcutScript.js"],
    "commands":{
        "toggle-subscript": {
            "suggested_key": {
                "default": "Ctrl+Comma",
                "mac": "Command+Comma"
            },
            "description": "Canvas Rich Text Editor: toggle subscript"
        },
        "toggle-superscript": {
            "suggested_key": {
                "default": "Ctrl+Period",
                "mac": "Command+Period"
            },
            "description": "Canvas Rich Text Editor: toggle superscript"
        },
        "insert-equation":{
            "suggested_key": {
                "default": "Alt+Q",
                "mac": "Alt+Q"
            },
            "description": "Canvas Rich Text Editor: insert equation"
        }
    },
    "background":{
        "scripts": ["js/background.js"],
        "persistent": false
    }
}

IMPORTANT: If your school does not use an .instructure.com domain to run Canvas, you will need to change the two lines that mention instructure.com to match your school's domain.

This manifest file will tell Chrome the following:

a) Which default keys to use for the shortcuts; you can change them later. We define three commands: "toggle-subscript", "toggle-superscript", and "insert-equation".

b) Which files will hold our content and background scripts.

c) Which sites our script should run on (only instructure sites)

4: Inside the folder you just put manifest.json in, create another folder called js

This folder will hold Javascript.

5: Inside the js folder, make a plain text file called background.js

Put exactly these contents in the file:

chrome.commands.onCommand.addListener(function (command) {
    const commandSuffix = command.split('-')[1];
    chrome.tabs.query({active:true, currentWindow:true}, function(tabs){ 
        chrome.tabs.sendMessage(tabs[0].id, {commandSuffix});    
    });
});

(Many thanks to Karen Ying's excellent tutorial "Hack Keyboard Shortcuts into Sites with a Custom Chrome Extension" for the above code!)

background.js listens for the shortcuts we specify. When we push the shortcut key combo, it sends a message to the current Chrome tab matching the part of the command name after the hyphen. For example, if we push the toggle-superscript shortcut, which is Ctrl-Period by default, it will send a message with the word "superscript". background.js has no access to the content of webpages directly; it just passes these messages on. 

6: Also inside the .js folder, make a plain text file called contentScript.js

Put exactly these contents in the file:

var s = document.createElement('script');
s.src=chrome.extension.getURL('js/shortcutScript.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function (){
    s.parentNode.removeChild(s);
};

function detectNewMathScreen(mutations){
    var mathContainer = document.getElementById('mathquill-container');
    if (mathContainer){
        var mathDialog = document.getElementsByClassName("math-dialog")[0];        
        var mathDialogObserver = new MutationObserver(detectVisibleMathScreen);
        mathDialogObserver.observe(mathDialog, { attributeFilter: ["aria-hidden"]});        
        this.disconnect();
        clickTextArea();
    }
};

function detectVisibleMathScreen(mutations){
    for (let mutation of mutations){
        var isHidden = mutation.target.getAttribute("aria-hidden");
        if (isHidden === "false"){
            clickTextArea();
        }
    }
};

function clickTextArea(){
        var mathDialog = document.getElementsByClassName("math-dialog")[0];        
        var textArea = mathDialog.getElementsByTagName("textarea")[0];
        setTimeout(function() {textArea.click(); textArea.scrollIntoView({block: "center"});},300);
}

var mathCreateObserver = new MutationObserver(detectNewMathScreen);
mathCreateObserver.observe(document.body, { childList: true});

chrome.runtime.onMessage.addListener(function (message) {
    document.dispatchEvent(new Event(message['commandSuffix']));
})

* The first block of code here gives instructions to inject another script into any instructure page we visit, called shortcutScript.js. We'll make that next.

* The second block of code gets triggered the FIRST time you open the equation editor. It sets up an observer that trips whenever you enter the equation editor in the future, then calls the function to select the text area so that you don't have to use the mouse to click on it.

* The third block of code gets triggered for SUBSEQUENT times you open the equation editor, since now it has already been created and the page is just hiding and revealing it. It also calls the function to select the text area.

* The fourth block of code selects the text area. It is a little ugly - I can't just trigger a click on the text area because the formula bar steals the focus away immediately! I have to put in a short delay. But it works!

The next few lines set up all of the above observers to make sure they trigger when you open the math editor. 

The last few lines raise an 'event' (another kind of message) in the website that our shortcutScript.js can read.

Whew!

7: Last script! In the js directory, create a file called shortcutScript.js

This script will be injected into Instructure pages to let us access local functions, like parts of the text editor. Here is the code:

function addListener (){
    document.addEventListener('superscript', function (e){
        if (typeof(tinymce) !== 'undefined'){        
            tinymce.activeEditor.execCommand('Superscript');
        }
    });
    document.addEventListener('subscript', function (e){
            if (typeof(tinymce) !== 'undefined'){        
                tinymce.activeEditor.execCommand('Subscript');
            }
    });
    document.addEventListener('equation', function (e){
            if (typeof(tinymce) !== 'undefined'){        
                tinymce.activeEditor.execCommand('instructureEquation');
            }
    });
}
addListener();

 

Basically, this code "listens" for the events we passed over from the contentScript.js. Since it is part of the webpage itself, it can access the editor (which our contentScript cannot easily do). It calls the editor's execCommand function and triggers various cases.

I encourage anyone who is interested to make their own version of this, adding shortcuts of their own!

8: Now we're ready to load this in Chrome. Open Chrome, then go to the Extensions menu (puzzle piece). Click Manage Extensions. (If you don't have a puzzle piece menu, you may need to use the "three dots" button to reach the Manage Extensions option.) 

9: If it is not already on, turn on the Developer Mode switch in the upper right.

10. Click Load Unpacked, then select the folder where you put your manifest.json

Just to check, the directory structure should be:

yourfoldername/manifest.json
yourfoldername/js/contentScript.js
yourfoldername/js/background.js
yourfoldername/js/shortcutScript.js

11. This should let you load the extension. The next time you load an Instructure page, try using ALT-Q to open the equation editor.

If you'd rather use different keys, you can go to chrome://extensions/shortcuts and change them to whatever you like - as long as your choices don't conflict with Chrome's built-in shortcuts!

Note: if you start the extension while a Canvas page is open, you will need to refresh the page before it will work.

I hope this helps your carpal tunnel! 

View solution in original post

6 Replies
nagami
Community Member

I was also frustrated by this, so I learned how to program a Chrome Extension just to fix it. Given that I've never programmed browser extensions before, this is a lot of effort to put into a problem on the user end..

I can't pretend this is the best code in the world - I'm an amateur! I also can't guarantee that it will work for FUTURE versions of Canvas, only the version that I'm using now. But it's at least a workaround! Currently, this extension creates shortcuts for:

* Superscript

* Subscript

* Insert equation

I can make edits if people want more!

(The following procedure is, to the very best of my limited ability and knowledge, safe to use. However, you should always look over any code you get from the Internet to make sure it's not suspicious before executing it! I will explain my code as I give it to you, to help allay any fears.)

This is an involved process, so I hope you have a little time. It also requires some basic technical skills. It worked for me; please let me know if it works for you!

Instructions for adding shortcuts to the rich content editor, including equation editor shortcut: (Chrome only)

1. Make a folder on own local hard drive. (Not a cloud folder.) You can call it anything you want. I called it CustomChromeShortcuts.

2. Inside that folder, use a plain text editor or a .JSON editor to make a new file called manifest.json

(Make sure it doesn't have some kind of hidden extension; some editors will quietly save files like this as manifest.json.txt)

Note: I used an editor called Brackets, but other editors will work. It's important that your editor not add a lot of special characters to this. I think Windows Notepad or Mac Textedit will work fine, too.

3. Put exactly these contents into the manifest.json file.

{
    "name": "Canvas Custom Keyboard Shortcuts",
    "version": "1.0",
    "description": "Add custom shortcuts to Canvas",
    "manifest_version": 2,
    "permissions": ["https://*.instructure.com/*"],
    "content_scripts": [
        {
            "matches": ["https://*.instructure.com/*"],
            "match_about_blank": true,
            "js": ["js/contentScript.js"]
        }
    ],
    "web_accessible_resources": ["js/shortcutScript.js"],
    "commands":{
        "toggle-subscript": {
            "suggested_key": {
                "default": "Ctrl+Comma",
                "mac": "Command+Comma"
            },
            "description": "Canvas Rich Text Editor: toggle subscript"
        },
        "toggle-superscript": {
            "suggested_key": {
                "default": "Ctrl+Period",
                "mac": "Command+Period"
            },
            "description": "Canvas Rich Text Editor: toggle superscript"
        },
        "insert-equation":{
            "suggested_key": {
                "default": "Alt+Q",
                "mac": "Alt+Q"
            },
            "description": "Canvas Rich Text Editor: insert equation"
        }
    },
    "background":{
        "scripts": ["js/background.js"],
        "persistent": false
    }
}

IMPORTANT: If your school does not use an .instructure.com domain to run Canvas, you will need to change the two lines that mention instructure.com to match your school's domain.

This manifest file will tell Chrome the following:

a) Which default keys to use for the shortcuts; you can change them later. We define three commands: "toggle-subscript", "toggle-superscript", and "insert-equation".

b) Which files will hold our content and background scripts.

c) Which sites our script should run on (only instructure sites)

4: Inside the folder you just put manifest.json in, create another folder called js

This folder will hold Javascript.

5: Inside the js folder, make a plain text file called background.js

Put exactly these contents in the file:

chrome.commands.onCommand.addListener(function (command) {
    const commandSuffix = command.split('-')[1];
    chrome.tabs.query({active:true, currentWindow:true}, function(tabs){ 
        chrome.tabs.sendMessage(tabs[0].id, {commandSuffix});    
    });
});

(Many thanks to Karen Ying's excellent tutorial "Hack Keyboard Shortcuts into Sites with a Custom Chrome Extension" for the above code!)

background.js listens for the shortcuts we specify. When we push the shortcut key combo, it sends a message to the current Chrome tab matching the part of the command name after the hyphen. For example, if we push the toggle-superscript shortcut, which is Ctrl-Period by default, it will send a message with the word "superscript". background.js has no access to the content of webpages directly; it just passes these messages on. 

6: Also inside the .js folder, make a plain text file called contentScript.js

Put exactly these contents in the file:

var s = document.createElement('script');
s.src=chrome.extension.getURL('js/shortcutScript.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function (){
    s.parentNode.removeChild(s);
};

function detectNewMathScreen(mutations){
    var mathContainer = document.getElementById('mathquill-container');
    if (mathContainer){
        var mathDialog = document.getElementsByClassName("math-dialog")[0];        
        var mathDialogObserver = new MutationObserver(detectVisibleMathScreen);
        mathDialogObserver.observe(mathDialog, { attributeFilter: ["aria-hidden"]});        
        this.disconnect();
        clickTextArea();
    }
};

function detectVisibleMathScreen(mutations){
    for (let mutation of mutations){
        var isHidden = mutation.target.getAttribute("aria-hidden");
        if (isHidden === "false"){
            clickTextArea();
        }
    }
};

function clickTextArea(){
        var mathDialog = document.getElementsByClassName("math-dialog")[0];        
        var textArea = mathDialog.getElementsByTagName("textarea")[0];
        setTimeout(function() {textArea.click(); textArea.scrollIntoView({block: "center"});},300);
}

var mathCreateObserver = new MutationObserver(detectNewMathScreen);
mathCreateObserver.observe(document.body, { childList: true});

chrome.runtime.onMessage.addListener(function (message) {
    document.dispatchEvent(new Event(message['commandSuffix']));
})

* The first block of code here gives instructions to inject another script into any instructure page we visit, called shortcutScript.js. We'll make that next.

* The second block of code gets triggered the FIRST time you open the equation editor. It sets up an observer that trips whenever you enter the equation editor in the future, then calls the function to select the text area so that you don't have to use the mouse to click on it.

* The third block of code gets triggered for SUBSEQUENT times you open the equation editor, since now it has already been created and the page is just hiding and revealing it. It also calls the function to select the text area.

* The fourth block of code selects the text area. It is a little ugly - I can't just trigger a click on the text area because the formula bar steals the focus away immediately! I have to put in a short delay. But it works!

The next few lines set up all of the above observers to make sure they trigger when you open the math editor. 

The last few lines raise an 'event' (another kind of message) in the website that our shortcutScript.js can read.

Whew!

7: Last script! In the js directory, create a file called shortcutScript.js

This script will be injected into Instructure pages to let us access local functions, like parts of the text editor. Here is the code:

function addListener (){
    document.addEventListener('superscript', function (e){
        if (typeof(tinymce) !== 'undefined'){        
            tinymce.activeEditor.execCommand('Superscript');
        }
    });
    document.addEventListener('subscript', function (e){
            if (typeof(tinymce) !== 'undefined'){        
                tinymce.activeEditor.execCommand('Subscript');
            }
    });
    document.addEventListener('equation', function (e){
            if (typeof(tinymce) !== 'undefined'){        
                tinymce.activeEditor.execCommand('instructureEquation');
            }
    });
}
addListener();

 

Basically, this code "listens" for the events we passed over from the contentScript.js. Since it is part of the webpage itself, it can access the editor (which our contentScript cannot easily do). It calls the editor's execCommand function and triggers various cases.

I encourage anyone who is interested to make their own version of this, adding shortcuts of their own!

8: Now we're ready to load this in Chrome. Open Chrome, then go to the Extensions menu (puzzle piece). Click Manage Extensions. (If you don't have a puzzle piece menu, you may need to use the "three dots" button to reach the Manage Extensions option.) 

9: If it is not already on, turn on the Developer Mode switch in the upper right.

10. Click Load Unpacked, then select the folder where you put your manifest.json

Just to check, the directory structure should be:

yourfoldername/manifest.json
yourfoldername/js/contentScript.js
yourfoldername/js/background.js
yourfoldername/js/shortcutScript.js

11. This should let you load the extension. The next time you load an Instructure page, try using ALT-Q to open the equation editor.

If you'd rather use different keys, you can go to chrome://extensions/shortcuts and change them to whatever you like - as long as your choices don't conflict with Chrome's built-in shortcuts!

Note: if you start the extension while a Canvas page is open, you will need to refresh the page before it will work.

I hope this helps your carpal tunnel! 

tom_gibbons
Community Contributor

Holy cats. I think I need a moment alone. 

@nagami this is amazeballs. 

I kind of feel like I'm reading a German newspaper: I can follow it, I understand it, but I wouldn't be able to write it without a few months of focus. I'll see if I can get it to work for me before I have to remediate the next quiz for next week. 

I would accept this as the solution if it weren't for the fact that I still hope for more built-in shortcut support in the RCE interface. 

You're welcome! This has been helpful for me as well.

One shortcoming still: When you exit the equation editor, Canvas's default behavior is to (for some reason I do not understand) remove focus from the textbox. This is goofy, and I have yet to fix it.

Currently, you can do ALMOST everything without moving the mouse.

EDIT: Pressing tab after retuning does seem to usually put the focus on the text box, so maybe this does allow mouse-free use after all.

tom_gibbons
Community Contributor

@nagami : It works! The thing that I needed was to access the editor in a less clicky way and it's great. (For mac users, this exact script will allow you to pop open the equation editor by hitting opt+q. I always get turned around on what the mac equivalent of alt is.)

The subscript shortcut also works. The superscript doesn't seem to work for mac. I don't need it, so I'm not going to work on tracking it down right now.

So this is great. BUT, when I get a chance, I think I'm going to see how I can deploy this script in Tampermonkey (which I use but I'm not a programmer, so I just use other people's stuff), so that I can run it wherever I'm logged into Chrome, regardless of machine. 

Again, you're a boss!

You're welcome!

I initially considered making this a TamperMonkey script, but I decided that the small risk of people getting exposed to exploits by that route made it better to write a tightly-controlled one-off extension, especially if I can eventually get it on the Chrome store as a free download.

This is absolutely impressive, but unfortunately it doesn't work for me. I've updated the domain to match our school's name, and it seems to recognize the script since it shows up on chrome://extensions/shortcuts... but when I actually click the shortcut on the canvas site, nothing happens. 😞