├── .gitignore ├── icons └── icon-128.png ├── zipForStore.sh ├── screenshots ├── screenshot.png ├── screenshot-new.png ├── large_promo_tile.png ├── promo_tile_orig.png ├── small_promo_tile.png ├── marquee_promo_tile.png ├── chrome_web_store_button.png └── zoom_chrome_ext_trailer.mov ├── PRIVACY_POLICY.md ├── LICENSE ├── README.md ├── src ├── content.css ├── background.js └── content.js ├── chrome-extension-store.md └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | store.zip -------------------------------------------------------------------------------- /icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/icons/icon-128.png -------------------------------------------------------------------------------- /zipForStore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm store.zip 3 | zip -R store.zip 'src/*' 'icons/*' 'manifest.json' 'LICENSE' 'README.md' -------------------------------------------------------------------------------- /screenshots/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/screenshot.png -------------------------------------------------------------------------------- /screenshots/screenshot-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/screenshot-new.png -------------------------------------------------------------------------------- /screenshots/large_promo_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/large_promo_tile.png -------------------------------------------------------------------------------- /screenshots/promo_tile_orig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/promo_tile_orig.png -------------------------------------------------------------------------------- /screenshots/small_promo_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/small_promo_tile.png -------------------------------------------------------------------------------- /screenshots/marquee_promo_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/marquee_promo_tile.png -------------------------------------------------------------------------------- /screenshots/chrome_web_store_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/chrome_web_store_button.png -------------------------------------------------------------------------------- /screenshots/zoom_chrome_ext_trailer.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/HEAD/screenshots/zoom_chrome_ext_trailer.mov -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | # meetings-page-auto-closer-for-zoom 2 | 3 | # Privacy Policy 4 | 5 | ## - __We don't have any servers.__ 6 | ## - __No data leaves your computer.__ 7 | ## - __We don't send or transmit any data to anywhere.__ 8 | ## - __All your data belongs to you, we don't want it.__ 9 |
10 | 11 | # So how does it work? 12 | ## All the data is securely processed inside the browser in an _[Isolated World](https://developer.chrome.com/extensions/content_scripts#isolated_world)_. With no data being transmitted outside the browser. 13 | 14 |
15 | 16 | ### So, in other words 17 | - Complete Privacy 18 | - No ads 19 | - No analytics 20 | - No data collection 21 | 22 | #### Therefore, we are HIPAA and GDPR Compliant. 23 | 24 | #### It's that simple :) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Github User: ChromeAdmin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # meetings-page-auto-closer-for-zoom 2 | 3 | Automatically closes the page used to launch Zoom meetings and restores focus to the tab you were on beforehand. Supports pages launched by Zoom and ZoomGov. Easily change the countdown time by clicking one of the offered alternate times in blue. 4 | 5 | ## Safest extension. No Permissions Needed. 6 | 7 | Use Zoom for meetings? Then this extension will keep your Chrome running fast and smooth by automatically closing the page used to launch Zoom meetings after you've joined and restoring focus to the tab you were on beforehand. 8 | 9 | The most user friendly Redirect Page Closer you'll find in the store that uses the least permissions while giving you the ability to cancel the auto close timer. We natively blend in with the Zoom UI for a seamless experience. Auto closes the Zoom tab after joining the meeting. 10 | 11 | Complete privacy. Contains no analytics. Zero dependencies. 12 |
[See our Privacy Policy](PRIVACY_POLICY.md) 13 |
This extension is HIPAA and GDPR compliant. 14 | 15 | [![Chrome Web Store](screenshots/chrome_web_store_button.png?raw=true "Chrome Web Store")](https://chrome.google.com/webstore/detail/meetings-page-auto-closer/pbgidoglkjhfgjhalbbiiahdlokjcplb) 16 | 17 | [![Screenshot](screenshots/screenshot-new.png?raw=true "Screenshot")](https://chrome.google.com/webstore/detail/meetings-page-auto-closer/pbgidoglkjhfgjhalbbiiahdlokjcplb) 18 | 19 | 20 | ## Test Urls 21 | - https://zoom.us/test 22 | - https://zoom.us/postattendee 23 | - https://zoom.us/wc/leave 24 | 25 | ## Notices 26 | This extension is not affliated, authorized by, endorsed by, or produced by Zoom Video Communications. 27 |
ZOOM is a trademark of Zoom Video Communications, Inc. 28 |
All other trademarks are the property of their respective holders. 29 | -------------------------------------------------------------------------------- /src/content.css: -------------------------------------------------------------------------------- 1 | .meetings-page-auto-closer-for-zoom-wrapper { 2 | position: absolute; 3 | margin: auto; 4 | left: 0; 5 | right: 0; 6 | /* top: 100px; */ 7 | /* top: 8px; */ 8 | top: 0; 9 | /* Because we added padding, we need to skoot back to the top */ 10 | z-index: 999; 11 | width: fit-content; 12 | } 13 | 14 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-main-pop-over { 15 | padding: 6px; 16 | padding-left: 10px; 17 | padding-right: 10px; 18 | text-align: center; 19 | color: #3a3c3e; 20 | font-size: 18px; 21 | font-weight: 400; 22 | text-shadow: 0 0 2px white, 0 0 4px white, 0 0 6px snow, 0 0 10px snow; 23 | background: hsla(0, 0%, 100%, 90%); 24 | box-shadow: 0px 1px 10px 1px rgba(0, 0, 0, 0.67); 25 | border-radius: 4px; 26 | } 27 | 28 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-main-pop-over .meetings-page-auto-closer-for-zoom-countdown-text { 29 | margin-bottom: 6px; 30 | } 31 | 32 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-main-pop-over .meetings-page-auto-closer-for-zoom-stop-link { 33 | cursor: pointer; 34 | font-size: 16px; 35 | padding: 4px; 36 | } 37 | 38 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-main-pop-over .meetings-page-auto-closer-for-zoom-close-now-btn { 39 | cursor: pointer; 40 | font-weight: bold; 41 | padding: 4px; 42 | font-size: 16px; 43 | margin-left: 36px; 44 | } 45 | 46 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-settings-menu { 47 | margin-top: 6px; 48 | padding: 6px; 49 | padding-left: 10px; 50 | padding-right: 10px; 51 | text-align: center; 52 | color: #1e1e1f; 53 | font-size: 12px; 54 | font-weight: 400; 55 | background: rgb(255 240 178 / 80%); 56 | box-shadow: 0px 1px 5px 1px rgba(0, 0, 0, 0.33); 57 | border-radius: 2px; 58 | } 59 | 60 | .meetings-page-auto-closer-for-zoom-wrapper .meetings-page-auto-closer-for-zoom-settings-menu .meetings-page-auto-closer-for-zoom-settings-option { 61 | cursor: pointer; 62 | font-size: 12px; 63 | padding: 1px; 64 | } 65 | -------------------------------------------------------------------------------- /chrome-extension-store.md: -------------------------------------------------------------------------------- 1 | # chrome-extension-store 2 | ## Product details 3 | ### Description 4 | Tired of those lingering redirect tabs? Close them automatically! Works with popular platforms like Zoom! This extension will keep your Chrome running fast and smooth by automatically closing the page used to launch Zoom meetings after you've joined and restores focus to the tab you were on beforehand. 5 | 6 | The most user friendly Redirect Closer you'll find in the store that uses the least permissions while giving you the ability to cancel the auto close timer. We natively blend in with the Zoom UI for a seamless experience. This extension Auto closes the Zoom tab after joining the meeting. 7 | 8 | Complete privacy. Contains no analytics. Zero dependencies. 9 | This extension is HIPAA and GDPR compliant. 10 | 11 | Experiencing issues? Please report them here: https://forms.gle/Y64QCYkRfFd2qVXm7 12 | 13 | Fully open source, safe, and secure: 14 | https://github.com/ChromeAdmin/meetings-page-auto-closer-for-zoom 15 | 16 | Privacy Policy: 17 | https://github.com/ChromeAdmin/meetings-page-auto-closer-for-zoom/blob/master/PRIVACY_POLICY.md 18 | 19 | Notices: 20 | This extension is not affiliated, authorized by, endorsed by, or produced by Zoom Video Communications. 21 | ZOOM is a trademark of Zoom Video Communications, Inc. 22 | All other trademarks are the property of their respective holders. 23 | 24 | Changelog: 25 | Last updated to allow easier closing and configurable close timer. 26 | 27 | ### Category 28 | Productivity 29 | 30 | ## Graphic Assets 31 | ### Promo Video 32 | https://www.youtube.com/watch?v=LVmwztcuIqQ 33 | 34 | ## Additional Fields 35 | ### Official URL 36 | chromeadmin.com 37 | ### Homepage Url 38 | https://github.com/ChromeAdmin/meetings-page-auto-closer-for-zoom 39 | ### Support URL 40 | https://forms.gle/Y64QCYkRfFd2qVXm7 41 | 42 | 43 | ## Privacy Practices 44 | ### Single purpose 45 | Automatically closes the page used to launch Zoom meetings and restores focus to the tab you were on beforehand. Supports pages launched by Zoom and ZoomGov. 46 | ### Permission justification 47 | This extension is limited to only run on specific zoom.us and zoomgov.com pages that are shown after joining a meeting, because those are the pages that we show to countdown timer on. 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | // Background script 2 | const windowToLastActiveTabIdsMap = {} 3 | 4 | function getLastActiveTabIdsListForWindowId(windowId) { 5 | if (!windowToLastActiveTabIdsMap[`wId-${windowId}`]) { 6 | windowToLastActiveTabIdsMap[`wId-${windowId}`] = []; 7 | } 8 | return windowToLastActiveTabIdsMap[`wId-${windowId}`]; 9 | } 10 | 11 | function addTabToWindowMap(windowId, tabId) { 12 | const lastActiveTabIdsList = getLastActiveTabIdsListForWindowId(windowId); 13 | //Remove the tabId from the last active if it exists 14 | const index = lastActiveTabIdsList.indexOf(tabId); 15 | if (index > -1) { 16 | lastActiveTabIdsList.splice(index, 1); 17 | } 18 | //Insert the tabId at the end 19 | lastActiveTabIdsList.push(tabId); 20 | 21 | //If we've gone over our self-imposed size then delete the oldest added tabId 22 | if (lastActiveTabIdsList.length > 5) { 23 | lastActiveTabIdsList.shift(); 24 | } 25 | } 26 | 27 | function focusTab(tabId) { 28 | const updateProperties = { active: true }; 29 | chrome.tabs.update(tabId, updateProperties, (tab) => { }); 30 | } 31 | 32 | 33 | chrome.tabs.onActivated.addListener((activeInfo) => { 34 | // console.log(`windowId: ${activeInfo.windowId} tabId:${activeInfo.tabId}`); 35 | addTabToWindowMap(activeInfo.windowId, activeInfo.tabId); 36 | }); 37 | 38 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { 39 | if (!message.pleaseCloseThisTab) { return; } 40 | 41 | const windowId = sender.tab.windowId; 42 | const tabId = sender.tab.id; 43 | const lastActiveTabIdsList = getLastActiveTabIdsListForWindowId(windowId); 44 | if (lastActiveTabIdsList.length >= 2) { //There need to be at least 2 tabs in our window's history 45 | const lastActiveTabId = lastActiveTabIdsList[lastActiveTabIdsList.length - 1]; 46 | const activeBeforeLastTabId = lastActiveTabIdsList[lastActiveTabIdsList.length - 2]; 47 | if (tabId === lastActiveTabId) { 48 | // This is a case where the zoom tab we are about to close 49 | // is the lastActiveTabId for this window. So we will change 50 | // the focus to the tab previously active in this window. 51 | focusTab(activeBeforeLastTabId); 52 | console.log('MPACFZ: Recovered the active tab before zoom.'); 53 | } 54 | } 55 | 56 | chrome.tabs.remove(tabId); 57 | }); 58 | 59 | console.log(`MPACFZ: meetings-page-auto-closer-for-zoom loaded.`) -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Meetings Page Auto Closer for Zoom", 3 | "description": "Automatically closes the page opened by Zoom to launch meetings and restores focus to the tab you were on before opened.", 4 | "version": "0.3.1", 5 | "manifest_version": 3, 6 | "homepage_url": "https://github.com/ChromeAdmin/meetings-page-auto-closer-for-zoom", 7 | "icons": { 8 | "16": "icons/icon-128.png", 9 | "48": "icons/icon-128.png", 10 | "128": "icons/icon-128.png" 11 | }, 12 | "permissions": [], 13 | "background": { 14 | "service_worker": "src/background.js" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "run_at": "document_start", 19 | "matches": [ 20 | "*://*.zoom.us/a/*", 21 | "*://*.zoom.us/b/*", 22 | "*://*.zoom.us/c/*", 23 | "*://*.zoom.us/d/*", 24 | "*://*.zoom.us/e/*", 25 | "*://*.zoom.us/f/*", 26 | "*://*.zoom.us/g/*", 27 | "*://*.zoom.us/h/*", 28 | "*://*.zoom.us/i/*", 29 | "*://*.zoom.us/j/*", 30 | "*://*.zoom.us/k/*", 31 | "*://*.zoom.us/l/*", 32 | "*://*.zoom.us/m/*", 33 | "*://*.zoom.us/n/*", 34 | "*://*.zoom.us/o/*", 35 | "*://*.zoom.us/p/*", 36 | "*://*.zoom.us/q/*", 37 | "*://*.zoom.us/r/*", 38 | "*://*.zoom.us/s/*", 39 | "*://*.zoom.us/t/*", 40 | "*://*.zoom.us/u/*", 41 | "*://*.zoom.us/v/*", 42 | "*://*.zoom.us/w/*", 43 | "*://*.zoom.us/x/*", 44 | "*://*.zoom.us/y/*", 45 | "*://*.zoom.us/z/*", 46 | "*://*.zoom.us/postattendee*", 47 | "*://*.zoom.us/wc/leave*", 48 | "*://*.zoomgov.com/a/*", 49 | "*://*.zoomgov.com/b/*", 50 | "*://*.zoomgov.com/c/*", 51 | "*://*.zoomgov.com/d/*", 52 | "*://*.zoomgov.com/e/*", 53 | "*://*.zoomgov.com/f/*", 54 | "*://*.zoomgov.com/g/*", 55 | "*://*.zoomgov.com/h/*", 56 | "*://*.zoomgov.com/i/*", 57 | "*://*.zoomgov.com/j/*", 58 | "*://*.zoomgov.com/k/*", 59 | "*://*.zoomgov.com/l/*", 60 | "*://*.zoomgov.com/m/*", 61 | "*://*.zoomgov.com/n/*", 62 | "*://*.zoomgov.com/o/*", 63 | "*://*.zoomgov.com/p/*", 64 | "*://*.zoomgov.com/q/*", 65 | "*://*.zoomgov.com/r/*", 66 | "*://*.zoomgov.com/s/*", 67 | "*://*.zoomgov.com/t/*", 68 | "*://*.zoomgov.com/u/*", 69 | "*://*.zoomgov.com/v/*", 70 | "*://*.zoomgov.com/w/*", 71 | "*://*.zoomgov.com/x/*", 72 | "*://*.zoomgov.com/y/*", 73 | "*://*.zoomgov.com/z/*", 74 | "*://*.zoomgov.com/postattendee*", 75 | "*://*.zoomgov.com/wc/leave*" 76 | ], 77 | "js": [ 78 | "src/content.js" 79 | ], 80 | "css": [ 81 | "src/content.css" 82 | ] 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /src/content.js: -------------------------------------------------------------------------------- 1 | const intervalRateMs = 1000; 2 | const maxCountdownStartTimeMs = 35 * 1000; 3 | const minCountdownStartTimeMs = 5 * 1000; 4 | 5 | const cssClassName_Wrapper = `meetings-page-auto-closer-for-zoom-wrapper`; 6 | const cssClassName_MainPopOver = `meetings-page-auto-closer-for-zoom-main-pop-over`; 7 | const cssClassName_CountdownText = `meetings-page-auto-closer-for-zoom-countdown-text`; 8 | const cssClassName_CloseNowBtn = `meetings-page-auto-closer-for-zoom-close-now-btn`; 9 | const cssClassName_StopLink = `meetings-page-auto-closer-for-zoom-stop-link`; 10 | 11 | const cssClassName_SettingsMenu = `meetings-page-auto-closer-for-zoom-settings-menu`; 12 | const cssClassName_SettingsOption = `meetings-page-auto-closer-for-zoom-settings-option`; 13 | 14 | const localStorageKey_CountdownStartTimeMs = `b9d55053-5a15-4b65-98ce-73711e1d83f9`; 15 | 16 | function log(text) { 17 | console.log(`MPACFZ: ${text}`); 18 | } 19 | 20 | log('loaded...'); 21 | 22 | let timeTillCloseMs = getCountdownStartTimeMs(); 23 | 24 | function getCountdownStartTimeMs() { 25 | const defaultStartTimeMs = 21 * 1000; 26 | let startTimeMs = defaultStartTimeMs; 27 | try { 28 | startTimeMs = Number(localStorage.getItem(localStorageKey_CountdownStartTimeMs)); 29 | } catch (e) { 30 | console.error(e); 31 | } 32 | if (!startTimeMs || startTimeMs <= minCountdownStartTimeMs || startTimeMs > maxCountdownStartTimeMs) { 33 | setCountdownStartTimeMs(defaultStartTimeMs); // Overwrite to self-correct 34 | startTimeMs = defaultStartTimeMs; 35 | } 36 | return startTimeMs; 37 | } 38 | 39 | function setCountdownStartTimeMs(startTimeMs) { 40 | localStorage.setItem(localStorageKey_CountdownStartTimeMs, startTimeMs); 41 | } 42 | 43 | function getWrapperEl() { 44 | return document.documentElement.querySelector(`.${cssClassName_Wrapper}`); 45 | } 46 | 47 | function countdownWithText(countdownTimeMs) { 48 | if (false) {//Used for freezing the countdown to debugging styling 49 | countdownTimeMs = getCountdownStartTimeMs(); 50 | clearInterval(intervalId); 51 | } 52 | 53 | let wrapperEl = getWrapperEl(); 54 | 55 | if (!wrapperEl) { // Lazy init the element 56 | wrapperEl = document.createElement('div'); 57 | wrapperEl.classList.add(cssClassName_Wrapper); 58 | wrapperEl.innerHTML = ` 59 |
60 |
61 | cancel 62 | close now 63 |
64 | `; 65 | document.body.appendChild(wrapperEl); 66 | 67 | wrapperEl.querySelector(`.${cssClassName_CloseNowBtn}`).onclick = () => { 68 | log('Closing tab now'); 69 | closeThisTabNow(); 70 | }; 71 | 72 | wrapperEl.querySelector(`.${cssClassName_StopLink}`).onclick = () => { 73 | log('Canceled the countdown'); 74 | clearInterval(intervalId); 75 | wrapperEl.remove(); 76 | }; 77 | 78 | injectAndUpdateSettingsMenu(); 79 | } 80 | 81 | const countdownEl = wrapperEl.querySelector(`.${cssClassName_CountdownText}`); 82 | countdownEl.innerText = `Closing page in ${Math.round(countdownTimeMs / 1000)} seconds`; 83 | } 84 | 85 | function injectAndUpdateSettingsMenu() { 86 | const incrementalSec = 5.0; 87 | const trueCountdownStartTimeSec = Math.round(getCountdownStartTimeMs() / incrementalSec / 1000.0) * incrementalSec; 88 | 89 | const optionsList = []; 90 | const decrementValSec = trueCountdownStartTimeSec - incrementalSec; 91 | const incrementValSec = trueCountdownStartTimeSec + incrementalSec; 92 | if (decrementValSec * 1000 >= minCountdownStartTimeMs) { 93 | optionsList.push(decrementValSec); 94 | } 95 | if (incrementValSec * 1000 < maxCountdownStartTimeMs) { 96 | optionsList.push(incrementValSec); 97 | } 98 | if (!optionsList) { 99 | log('no options'); 100 | return; 101 | } 102 | const wrapperEl = getWrapperEl(); 103 | wrapperEl.querySelector(`.${cssClassName_SettingsMenu}`)?.remove(); 104 | 105 | const settingsEl = document.createElement('div'); 106 | settingsEl.classList.add(cssClassName_SettingsMenu); 107 | settingsEl.innerHTML = ` 108 | ${trueCountdownStartTimeSec} seconds not your speed? Try 109 | ${optionsList[0]}s 110 | `; 111 | if (optionsList.length > 1) { 112 | settingsEl.innerHTML += ` 113 | or 114 | ${optionsList[1]}s 115 | `; 116 | } 117 | const optionsElList = settingsEl.querySelectorAll(`.${cssClassName_SettingsOption}`); 118 | for (let i = 0; i < optionsElList.length; i++) { 119 | const optionEl = optionsElList[i]; 120 | const op = optionsList[i]; 121 | optionEl.onclick = () => { 122 | log(`New option selected: ${op}`); 123 | const ms = (op + 1) * 1000; 124 | timeTillCloseMs = ms; 125 | setCountdownStartTimeMs(ms); 126 | injectAndUpdateSettingsMenu(); 127 | }; 128 | 129 | } 130 | wrapperEl.appendChild(settingsEl); 131 | } 132 | 133 | function getUrl() { 134 | return new URL(window.location.href); 135 | } 136 | 137 | function isWebClientLeave() { 138 | const url = getUrl(); 139 | if (url.pathname && url.pathname.startsWith('/wc/leave')) { 140 | return true; 141 | } else { 142 | return false; 143 | } 144 | } 145 | 146 | function isPostAttendee() { 147 | const url = getUrl(); 148 | if (url.pathname && url.pathname.startsWith('/postattendee')) { 149 | return true; 150 | } else { 151 | return false; 152 | } 153 | } 154 | 155 | function isMeetingStatusSuccess() { 156 | if (window.location.href.toLowerCase().includes('success')) { 157 | return true; 158 | } 159 | 160 | return false; 161 | } 162 | 163 | function isPageTextLikeMeetingLaunch() { 164 | const pageText = document?.body?.innerText?.toLowerCase() || ''; 165 | if (pageText.includes('click open zoom.')) { 166 | return true; 167 | } 168 | if (pageText.includes('click launch meeting below')) { 169 | return true; 170 | } 171 | if (pageText.includes('having issues with zoom')) { 172 | return true; 173 | } 174 | if (pageText.includes('meeting has been launched')) { 175 | return true; 176 | } 177 | if (pageText.includes('having issues with zoom')) { 178 | return true; 179 | } 180 | return false; 181 | } 182 | 183 | function countDownToClose() { 184 | timeTillCloseMs -= intervalRateMs; 185 | log(`TimeMs left: ${timeTillCloseMs} isPageText=${isPageTextLikeMeetingLaunch()} isSuccess=${isMeetingStatusSuccess()} isPostAttendee=${isPostAttendee()} isWebClientLeave=${isWebClientLeave()}`); 186 | 187 | if (isPageTextLikeMeetingLaunch() || isMeetingStatusSuccess() || isPostAttendee() || isWebClientLeave()) { 188 | log(`All checks good to auto close`); 189 | } else { 190 | timeTillCloseMs += intervalRateMs; // Put back the time 191 | return; 192 | } 193 | 194 | countdownWithText(timeTillCloseMs); 195 | 196 | if (timeTillCloseMs > 0) { return; } 197 | 198 | clearInterval(intervalId); 199 | 200 | closeThisTabNow(); 201 | } 202 | 203 | function closeThisTabNow() { 204 | chrome.runtime.sendMessage({ pleaseCloseThisTab: true }); 205 | } 206 | 207 | let intervalId = setInterval(countDownToClose, intervalRateMs); 208 | --------------------------------------------------------------------------------