Skip to content

Instantly share code, notes, and snippets.

@a-gu
Last active October 1, 2020 06:28
Show Gist options
  • Save a-gu/9cc1b4caf6440cb2b918c13c72e0d0ce to your computer and use it in GitHub Desktop.
Save a-gu/9cc1b4caf6440cb2b918c13c72e0d0ce to your computer and use it in GitHub Desktop.
Userscript for a better experience using Zoom web conferencing in a web browser
// ==UserScript==
// @name Better Browser Zoom
// @version 0.6
// @description Userscript for a better experience using Zoom web conferencing in a web browser
// @author Andrew Gu
// @updateURL https://gist.githubusercontent.com/a-gu/9cc1b4caf6440cb2b918c13c72e0d0ce/raw/better_browser_zoom.user.js
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// @match https://*.zoom.us/*
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
.bbz-link {
color: #0e71eb;
font-size: 3em;
font-weight: bold;
transition: transform 0.5s ease;
}
.bbz-link:not(:hover):not(:focus) {
animation: 2s ease infinite attention;
}
.bbz-link:hover,
.bbz-link:focus {
transform: scale(1.1) rotate(0);
}
.bbz-autofill-join-name {
margin: 0.5em 0;
}
@keyframes attention {
40% { transform: scale(1) rotate(0); }
50%, 60% { transform: scale(1.1) rotate(1.5deg); }
75% { transform: scale(0.95) rotate(-1.5deg); }
}
`)
let browserJoinLink = function () {
if (!/^Launch Meeting\b/.test(document.querySelector('title').innerText)) { return }
let observer = new MutationObserver(function () {
let uiContainer = document.querySelector('#zoom-ui-frame')
if (!uiContainer) { return }
// determine action URL and label
let actionURL = atob(unsafeWindow.launchBase64).match(/\u001a.(.+?)"/)[1]
let actionText = ''
if (/\/start$/.test(actionURL)) {
actionText = 'Start From Your Browser'
} else if (/^[^?&]+\/join\/[^\/]+(\?|$)/.test(actionURL)) {
actionText = 'Join From Your Browser'
} else {
console.warn('Unknown launchURL', actionURL)
observer.disconnect()
return
}
// generate UI
let webJoinAnchor = document.createElement('a')
webJoinAnchor.href = actionURL
webJoinAnchor.innerText = actionText
webJoinAnchor.classList.add('bbz-link')
uiContainer.prepend(webJoinAnchor)
observer.disconnect()
})
observer.observe(document.body, {
attributes: true,
attributeFilter: [ 'id' ],
childList: true,
subtree: true
})
}
browserJoinLink()
let autofillJoinName = function () {
let nameInput = document.querySelector('input#inputname')
if (!nameInput) { return false }
let autofillName
let rememberedName = function (type, key, value) {
let store = JSON.parse(GM_getValue(`bbz-autofill-join-name-${type}`, '{}'))
let now = (new Date()).getTime()
// set new values
if (value) { store[key] = { value: value, expiry: now + 365*24*3600e3 } }
// remove expired stores
for (let filterKey in store) {
if (store[key].expiry < now) {
delete store[key]
}
}
// persist modified store
GM_setValue(`bbz-autofill-join-name-${type}`, JSON.stringify(store))
// return requested value
if (key in store) { return store[key].value }
}
// load remembered name
let accountIDs = Array.from(document.scripts).map(script => script.innerText.match(/var userId *= *'([^']+)' *;/)).filter(x => !!x)
let rememberIDs = {
'meeting': document.querySelector('input#meeting_number').value,
'account': accountIDs.length ? accountIDs[0][1] : null,
'global': 'global'
}
autofillName = autofillName || rememberedName('meeting', rememberIDs.meeting)
autofillName = autofillName || rememberedName('account', rememberIDs.account)
autofillName = autofillName || rememberedName('global', 'global')
// autofill loaded name
if (autofillName) { nameInput.value = autofillName }
// generate UI
let rememberOption = document.createElement('div')
rememberOption.classList.add('bbz-autofill-join-name')
rememberOption.appendChild(document.createTextNode('Remember name for '))
let rememberSelect = document.createElement('select')
rememberOption.appendChild(rememberSelect)
let rememberLabels = { 'meeting': 'this meeting', 'account': 'this account', 'global': 'everything' }
Object.keys(rememberLabels).forEach(type => {
let option = document.createElement('option')
option.setAttribute('value', type)
option.innerText = rememberLabels[type]
rememberSelect.appendChild(option)
})
nameInput.parentNode.appendChild(rememberOption)
// save name on join
let saveName = () => {
if (rememberSelect.value in rememberLabels) {
rememberedName(rememberSelect.value, rememberIDs[rememberSelect.value], nameInput.value)
}
}
document.querySelector('button#joinBtn').addEventListener('click', saveName)
nameInput.closest('form').addEventListener('submit', saveName)
}
autofillJoinName()
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment