├── .gitignore ├── README.md ├── Tasklog.zip ├── assets ├── img │ ├── logo128.png │ ├── logo16.png │ ├── logo240.png │ ├── logo32.png │ └── logo48.png └── locales │ └── en │ └── messages.json ├── package-lock.json ├── package.json ├── src ├── auth │ ├── auth.html │ ├── auth.js │ └── auth.scss ├── background │ ├── bg.js │ └── manager.js ├── contentScript │ ├── cs.js │ ├── newTaskWindow.js │ └── newTaskWindow.scss ├── manifest.json ├── popup │ ├── popup.html │ ├── popup.js │ └── popup.scss ├── scss │ └── base.scss ├── utils │ ├── api.js │ ├── element.js │ ├── sendMessage.js │ └── svg.js └── vendors │ └── browser-polyfill.min.js └── test └── sample.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | dist 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tasklog 2 | 3 | Log task on Makerlog 4 | 5 | ### How to build 6 | Please make sure you have Node installed. 7 | 1) Run `npm i` in source directory. 8 | 2) For development run `npm run start`. This will build files in ./dist directory. 9 | 3) For production run `npm run build`. This will generate release.zip with build files ready to publish. 10 | 11 | ### Available Commands 12 | 13 | | Commands | Description | 14 | | --- | --- | 15 | | `npm run start` | build extension, watch file changes | 16 | | `npm run build` | generate release version | 17 | | `npm run docs` | generate source code docs | 18 | | `npm run clean` | remove temporary files | 19 | | `npm run test` | run unit tests | 20 | | `npm run sync` | update config files | 21 | 22 | For CLI instructions see [User Guide →](https://oss.mobilefirst.me/extension-cli/) 23 | 24 | -------------------------------------------------------------------------------- /Tasklog.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/Tasklog.zip -------------------------------------------------------------------------------- /assets/img/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/assets/img/logo128.png -------------------------------------------------------------------------------- /assets/img/logo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/assets/img/logo16.png -------------------------------------------------------------------------------- /assets/img/logo240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/assets/img/logo240.png -------------------------------------------------------------------------------- /assets/img/logo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/assets/img/logo32.png -------------------------------------------------------------------------------- /assets/img/logo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zicsus/tasklog/74949573b61ea241d14b86563bf1419e5aac6c2f/assets/img/logo48.png -------------------------------------------------------------------------------- /assets/locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Tasklog" 4 | }, 5 | "appShortName": { 6 | "message": "Tasklog" 7 | }, 8 | "appDescription": { 9 | "message": "Log task on Makerlog" 10 | } 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tasklog", 3 | "description": "Log task on Makerlog", 4 | "version": "0.1.2", 5 | "homepage": "https://github.com/zicsus/tasklog", 6 | "author": "Himanshu Mishra ", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/zicsus/tasklog" 10 | }, 11 | "scripts": { 12 | "start": "xt-build -e dev -w", 13 | "build": "xt-build -e prod", 14 | "clean": "xt-clean", 15 | "docs": "xt-docs", 16 | "test": "xt-test", 17 | "sync": "xt-sync --all" 18 | }, 19 | "babel": { 20 | "presets": [ 21 | "@babel/preset-env" 22 | ] 23 | }, 24 | "eslintIgnore": [ 25 | "test/**/*" 26 | ], 27 | "devDependencies": { 28 | "extension-cli": "^0.11.8", 29 | "webextension-polyfill": "^0.7.0" 30 | }, 31 | "xtdocs": { 32 | "source": { 33 | "include": [ 34 | "README.md", 35 | "src" 36 | ] 37 | }, 38 | "templates": { 39 | "systemName": "Tasklog", 40 | "systemSummary": "Log task on Makerlog", 41 | "systemColor": "#4CAF50" 42 | } 43 | }, 44 | "xtbuild": { 45 | "js_bundles": [ 46 | { 47 | "name": "js/background", 48 | "src": "./src/background/bg.js" 49 | }, 50 | { 51 | "name": "js/contentScript", 52 | "src": "./src/contentScript/cs.js" 53 | }, 54 | { 55 | "name": "js/popup", 56 | "src": "./src/popup/popup.js" 57 | }, 58 | { 59 | "name": "js/auth", 60 | "src": "./src/auth/auth.js" 61 | } 62 | ], 63 | "scss_bundles": [ 64 | { 65 | "name": "popup", 66 | "src": "./src/popup/popup.scss" 67 | }, 68 | { 69 | "name": "base", 70 | "src": "./src/scss/base.scss" 71 | }, 72 | { 73 | "name": "auth", 74 | "src": "./src/auth/auth.scss" 75 | }, 76 | { 77 | "name": "newTaskWindow", 78 | "src": "./src/contentScript/newTaskWindow.scss" 79 | } 80 | ], 81 | "copyAsIs": [ 82 | "./src/vendors/**/*" 83 | ] 84 | }, 85 | "dependencies": { 86 | "axios": "^0.21.1" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/auth/auth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Auth | Tasklog 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 | 23 | 27 | 28 | 29 | Don't have an account? Create one 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/auth/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import sendMessage from '../utils/sendMessage'; 3 | 4 | const usernameInput = document.getElementById('username'); 5 | const passwordInput = document.getElementById('password'); 6 | 7 | const loginBtn = document.getElementById('login'); 8 | 9 | function isLoading(status) 10 | { 11 | if (status) 12 | { 13 | loginBtn.loading = true; 14 | loginBtn.querySelector('.loader').style.display = "block"; 15 | loginBtn.querySelector('span').style.display = "none"; 16 | } 17 | else 18 | { 19 | loginBtn.loading = false; 20 | loginBtn.querySelector('.loader').style.display = "none"; 21 | loginBtn.querySelector('span').style.display = "block"; 22 | } 23 | } 24 | 25 | loginBtn.addEventListener('click', login); 26 | passwordInput.addEventListener("keypress", (e) => 27 | { 28 | if (e.key === "Enter") login(); 29 | }); 30 | 31 | function login() 32 | { 33 | if (!loginBtn.loading) 34 | { 35 | const username = usernameInput.value; 36 | const password = passwordInput.value; 37 | if(username && password) 38 | { 39 | isLoading(true); 40 | sendMessage("Auth.login", {username, password}); 41 | } 42 | } 43 | } 44 | 45 | chrome.runtime.onMessage.addListener((message, sender, _) => 46 | { 47 | console.log(message); 48 | switch (message.action) 49 | { 50 | case "Auth.loginStatus": 51 | { 52 | isLoading(false); 53 | if (message.status) window.close(); 54 | else alert(message.err); 55 | } break; 56 | } 57 | }); -------------------------------------------------------------------------------- /src/auth/auth.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | width: 400px; 3 | margin: 10rem auto; 4 | position: relative; 5 | box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.4); 6 | padding: 4rem 1.5rem 1.5rem 1.5rem; 7 | border-radius: 0.3rem; 8 | 9 | img { 10 | width: 110px; 11 | height: 110px; 12 | position: absolute; 13 | top: -60px; 14 | left: calc(50% - 55px); 15 | padding: 8px; 16 | border-radius: 50%; 17 | } 18 | 19 | .message { 20 | display: block; 21 | margin-top: 1rem; 22 | font: normal 14px Nunito; 23 | color: #fff; 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/background/bg.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import Manager from './manager'; 3 | import Api from '../utils/api'; 4 | import sendMessage from '../utils/sendMessage'; 5 | 6 | async function handleContentScript(sender, message) 7 | { 8 | const tabId = sender.tab.id; 9 | switch (message.action) 10 | { 11 | case "Auth.login": 12 | { 13 | try 14 | { 15 | const status = await Api.login(message.username, message.password); 16 | sendMessage("Auth.loginStatus", {status}, tabId); 17 | } 18 | catch (err) 19 | { 20 | sendMessage("Auth.loginStatus", {status: false, err: err.message}, tabId); 21 | } 22 | } break; 23 | } 24 | } 25 | 26 | async function handleMessage(message) 27 | { 28 | switch (message.action) 29 | { 30 | case "Auth.check": 31 | { 32 | try 33 | { 34 | const status = await Manager.checkAuth(); 35 | sendMessage("Auth.set", {status}); 36 | } 37 | catch (err) 38 | { 39 | sendMessage("Auth.set", {status: false}); 40 | } 41 | } break; 42 | 43 | case "Api.getProfile": 44 | { 45 | try 46 | { 47 | await Api.getProfile(data => 48 | { 49 | sendMessage("Api.setProfile", {err: null, data}); 50 | }); 51 | } 52 | catch (err) 53 | { 54 | sendMessage("Api.setProfile", {err, data: null}); 55 | } 56 | } break; 57 | 58 | case "Manager.logout": 59 | { 60 | Manager.logout(); 61 | } break; 62 | 63 | case "Tab.open": 64 | { 65 | chrome.tabs.create({ url: message.url }); 66 | } break; 67 | } 68 | } 69 | 70 | async function toggleTasklog() 71 | { 72 | try 73 | { 74 | const status = await Manager.checkAuth(); 75 | if (status) 76 | { 77 | chrome.tabs.query({active: true, currentWindow: true}, async (tabs) => 78 | { 79 | const tabId = tabs[0].id; 80 | sendMessage("Tasklog.toggle", {}, tabId); 81 | }); 82 | } 83 | } 84 | catch (err){} 85 | } 86 | 87 | browser.runtime.onMessage.addListener((message, sender, sendMessage) => 88 | { 89 | if (sender.tab) handleContentScript(sender, message); 90 | else handleMessage(message); 91 | }); 92 | 93 | chrome.contextMenus.create({ 94 | title: "Log task with Tasklog", 95 | onclick: toggleTasklog, 96 | contexts: ["all"] 97 | }); 98 | 99 | browser.commands.onCommand.addListener((command) => 100 | { 101 | if (command === "toggle-window") toggleTasklog(); 102 | }); 103 | 104 | -------------------------------------------------------------------------------- /src/background/manager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | async function checkAuth() 4 | { 5 | try 6 | { 7 | const {token} = await browser.storage.local.get(["token"]); 8 | if (!token) browser.tabs.create({url: browser.runtime.getURL('auth.html')}); 9 | return token ? true : false; 10 | } 11 | catch (err) 12 | { 13 | console.log(err); 14 | throw err; 15 | } 16 | } 17 | 18 | function logout() 19 | { 20 | browser.storage.local.remove("token"); 21 | browser.storage.local.remove("user"); 22 | } 23 | 24 | export default { 25 | checkAuth, 26 | logout 27 | }; -------------------------------------------------------------------------------- /src/contentScript/cs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import Api from '../utils/api'; 3 | import newTaskWindow from './newTaskWindow'; 4 | 5 | const taskWindow = newTaskWindow(); 6 | const attachmentBtn = taskWindow.querySelector('#choose_attachment'); 7 | const attachmentInput = taskWindow.querySelector('#attachment_input'); 8 | const attachmentImg = taskWindow.querySelector('#attachment'); 9 | const attachmentRemove = taskWindow.querySelector('#attachment_remove'); 10 | const contentInput = taskWindow.querySelector('#content'); 11 | const addTaskBtn = taskWindow.querySelector('#add_task'); 12 | const state = { 13 | enabled: false, 14 | loading: false 15 | }; 16 | 17 | attachmentBtn.addEventListener('click', (e) => 18 | { 19 | attachmentInput.click(); 20 | }); 21 | 22 | attachmentInput.addEventListener('change', (e) => 23 | { 24 | if (attachmentInput.files && attachmentInput.files[0]) 25 | { 26 | let reader = new FileReader(); 27 | reader.onload = (e) => 28 | { 29 | taskWindow.querySelector(".image").style.display = "flex"; 30 | attachmentImg.src = e.target.result; 31 | }; 32 | reader.readAsDataURL(attachmentInput.files[0]); 33 | } 34 | }); 35 | 36 | attachmentRemove.addEventListener('click', (e) => 37 | { 38 | resetImage(); 39 | }); 40 | 41 | addTaskBtn.addEventListener('click', () => 42 | { 43 | if (!state.loading) newTask(contentInput.value); 44 | }); 45 | 46 | contentInput.addEventListener('keyup', (e) => 47 | { 48 | if ((e.key === "Enter" || e.code === "Enter") && !state.loading) newTask(contentInput.value); 49 | }); 50 | 51 | taskWindow.querySelector(".overlay").addEventListener("click", () => toggle()) 52 | 53 | document.addEventListener("keydown", (e) => 54 | { 55 | if ((e.key === "Escape" || e.key === "Esc") && state.enabled) toggle(); 56 | }); 57 | 58 | function isLoading(status) 59 | { 60 | if (status) 61 | { 62 | state.loading = true; 63 | addTaskBtn.querySelector('.loader').style.display = "block"; 64 | addTaskBtn.querySelector('svg').style.display = "none"; 65 | } 66 | else 67 | { 68 | state.loading = false; 69 | addTaskBtn.querySelector('.loader').style.display = "none"; 70 | addTaskBtn.querySelector('svg').style.display = "block"; 71 | } 72 | } 73 | 74 | function resetImage() 75 | { 76 | taskWindow.querySelector(".image").style.display = "none"; 77 | attachmentInput.value = ""; 78 | } 79 | 80 | async function newTask(content) 81 | { 82 | let done = true, 83 | inProgress = false; 84 | 85 | if (content.startsWith("/todo ")) 86 | { 87 | done = false; 88 | inProgress = false; 89 | content = content.replace("/todo ", ''); 90 | } 91 | else if (content.startsWith("/doing ")) 92 | { 93 | done = false; 94 | inProgress = true; 95 | content = content.replace("/doing ", ''); 96 | } 97 | else content = content.replace("/done ", ''); 98 | 99 | if (!content) return; 100 | isLoading(true); 101 | 102 | let attach = undefined; 103 | if (attachmentInput.files && attachmentInput.files[0]) attach = attachmentInput.files[0]; 104 | 105 | try 106 | { 107 | const data = await Api.newTask(content, done, inProgress, attach); 108 | isLoading(false); 109 | if (data.id) 110 | { 111 | contentInput.value = ""; 112 | resetImage(); 113 | } 114 | else alert("Something went wrong!"); 115 | } 116 | catch (err) 117 | { 118 | isLoading(false); 119 | alert("Something went wrong!"); 120 | } 121 | } 122 | 123 | function toggle() 124 | { 125 | if (state.enabled) 126 | { 127 | state.enabled = false; 128 | taskWindow.querySelector(".overlay").classList.remove("active"); 129 | taskWindow.querySelector(".popup").classList.remove("active"); 130 | setTimeout(() => 131 | { 132 | taskWindow.style.display = "none"; 133 | }, 200); 134 | } 135 | else 136 | { 137 | state.enabled = true; 138 | taskWindow.style.display = "block"; 139 | contentInput.focus(); 140 | setTimeout(() => 141 | { 142 | taskWindow.querySelector(".overlay").classList.add("active"); 143 | taskWindow.querySelector(".popup").classList.add("active"); 144 | }, 200); 145 | } 146 | } 147 | 148 | browser.runtime.onMessage.addListener((message, sender, sendMessage) => 149 | { 150 | switch (message.action) 151 | { 152 | case "Tasklog.toggle": 153 | { 154 | toggle(); 155 | } break; 156 | } 157 | }); -------------------------------------------------------------------------------- /src/contentScript/newTaskWindow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import $ from '../utils/element'; 3 | import Svg from '../utils/svg'; 4 | 5 | function createWindow() 6 | { 7 | const tasklogWindow = document.createElement('tasklog-window'); 8 | const shadow = tasklogWindow.attachShadow({ mode: 'open' }); 9 | 10 | const baseStyles = $.create(` `); 11 | const windowStyles = $.create(` `); 12 | const taskWindow = $.create(`
`); 13 | taskWindow.style.display = 'none'; 14 | const overlay = $.create(`
`); 15 | 16 | const popup = $.create(` 17 | 35 | `); 36 | 37 | taskWindow.appendChild(overlay); 38 | taskWindow.appendChild(popup); 39 | shadow.appendChild(baseStyles); 40 | shadow.appendChild(windowStyles); 41 | shadow.appendChild(taskWindow); 42 | document.body.appendChild(tasklogWindow); 43 | 44 | return taskWindow; 45 | } 46 | 47 | export default createWindow; -------------------------------------------------------------------------------- /src/contentScript/newTaskWindow.scss: -------------------------------------------------------------------------------- 1 | .window { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 10000000; 8 | display: none; 9 | 10 | .overlay { 11 | width: 100%; 12 | height: 100%; 13 | background: rgba(0, 0, 0, 0.4); 14 | opacity: 0; 15 | transition: opacity 0.2s ease-in-out; 16 | 17 | &.active { 18 | opacity: 1; 19 | } 20 | } 21 | 22 | .popup { 23 | position: absolute; 24 | left: 50%; 25 | top: -5rem; 26 | transform: translate(-50%, 0%); 27 | transition: top 0.2s ease-in-out; 28 | width: 560px; 29 | 30 | .task { 31 | width: 100%; 32 | display: flex; 33 | align-items: center; 34 | height: 50px; 35 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.4); 36 | border-radius: 0.4rem; 37 | overflow: hidden; 38 | margin-top: 1.5rem; 39 | background: transparent; 40 | 41 | input { 42 | flex: 1; 43 | height: 100%; 44 | font-size: 18px; 45 | padding: 0.5rem; 46 | border: 0px; 47 | font: 600 18px Nunito; 48 | background: #2f3136; 49 | color: white; 50 | } 51 | 52 | .action { 53 | width: 50px; 54 | height: 50px; 55 | color: #16171b; 56 | display: flex; 57 | align-items: center; 58 | justify-content: center; 59 | transition: background 0.2s ease-in-out, color 0.2s ease-in-out; 60 | background: #2f3136; 61 | color: rgba(white, 0.6); 62 | 63 | input, .loader { 64 | display: none; 65 | } 66 | 67 | svg { 68 | width: 24px; 69 | height: 24px; 70 | } 71 | 72 | &:hover { 73 | background: #43b581; 74 | color: #fff; 75 | cursor: pointer; 76 | } 77 | } 78 | } 79 | 80 | .image { 81 | width: 100%; 82 | max-height: 360px; 83 | margin-top: 0.5rem; 84 | display: none; 85 | align-items: center; 86 | justify-content: center; 87 | position: relative; 88 | 89 | img { 90 | max-width: 100%; 91 | max-height: 360px; 92 | border-radius: 0.4rem; 93 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); 94 | } 95 | 96 | span { 97 | position: absolute; 98 | bottom: 1rem; 99 | left: 50%; 100 | transform: translate(-50%, 0); 101 | display: flex; 102 | align-items: center; 103 | justify-content: center; 104 | width: 100px; 105 | height: 36px; 106 | background: rgba(#000, 0.4); 107 | border-radius: 0.2rem; 108 | font: 16px Nunito; 109 | transition: background 0.2s ease-in-out; 110 | cursor: pointer; 111 | backdrop-filter: blur(5px); 112 | color: white; 113 | 114 | &:hover { 115 | background: rgba(#000, 0.6); 116 | } 117 | } 118 | } 119 | 120 | &.active { 121 | top: 2rem; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Tasklog for Makerlog", 4 | "description": "Log tasks on Makerlog from anywhere.", 5 | "version": "0.1.0", 6 | "author": "Himanshu Mishra ", 7 | "homepage_url": "https://github.com/zicsus/tasklog", 8 | "default_locale": "en", 9 | "minimum_chrome_version": "31", 10 | "web_accessible_resources": [ 11 | "css/*" 12 | ], 13 | "icons": { 14 | "16": "icons/logo16.png", 15 | "32": "icons/logo32.png", 16 | "48": "icons/logo48.png", 17 | "128": "icons/logo128.png" 18 | }, 19 | "background": { 20 | "scripts": [ 21 | "vendors/browser-polyfill.min.js", 22 | "js/background.js" 23 | ] 24 | }, 25 | "content_scripts": [ 26 | { 27 | "matches": ["https://*/*", "http://*/*", "file:///*"], 28 | "js": ["vendors/browser-polyfill.min.js", "js/contentScript.js"] 29 | } 30 | ], 31 | "permissions": [ 32 | "", 33 | "tabs", 34 | "storage", 35 | "contextMenus" 36 | ], 37 | "browser_action": { 38 | "default_icon": { 39 | "16": "icons/logo16.png", 40 | "32": "icons/logo32.png", 41 | "48": "icons/logo48.png" 42 | }, 43 | "default_popup": "popup.html", 44 | "default_title": "Tasklog" 45 | }, 46 | "commands": { 47 | "toggle-window": { 48 | "suggested_key": { 49 | "default": "Alt+P", 50 | "mac": "Alt+P" 51 | }, 52 | "description": "Toggle new task window." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tasklog 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 |

25 | 26 |
27 |
    28 |
  • 29 | 30 | 31 | 32 |

    0

    33 |
  • 34 | 35 |
  • 36 | 37 |

    0

    38 |
  • 39 | 40 |
  • 41 | 42 |

    0

    43 |
  • 44 |
45 |
46 | 47 |
48 | 49 |
50 |
51 |
52 | 53 | 54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 | 64 | Remove 65 |
66 |
67 | 68 | 82 |
83 |
84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/popup/popup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import Api from '../utils/api'; 3 | import sendMessage from '../utils/sendMessage'; 4 | 5 | const state = { 6 | user: null 7 | }; 8 | 9 | const attachmentBtn = document.getElementById('choose_attachment'); 10 | const attachmentInput = document.getElementById('attachment_input'); 11 | const attachmentImg = document.getElementById('attachment'); 12 | const attachmentRemove = document.getElementById('attachment_remove'); 13 | const contentInput = document.getElementById('content'); 14 | const addTaskBtn = document.getElementById('add_task'); 15 | const logoutBtn = document.getElementById('logout'); 16 | 17 | document.getElementById("name").addEventListener("click", () => sendMessage("Tab.open", { url: `https://getmakerlog.com/@${state.user.username}` })); 18 | document.querySelector("#discuss").addEventListener("click", () => sendMessage("Tab.open", { url: "https://getmakerlog.com/discussions" })); 19 | document.querySelector("#stories").addEventListener("click", () => sendMessage("Tab.open", { url: "https://getmakerlog.com/stories" })); 20 | document.querySelector("#explore").addEventListener("click", () => sendMessage("Tab.open", { url: "https://getmakerlog.com/" })); 21 | 22 | logoutBtn.addEventListener("click", () => 23 | { 24 | sendMessage("Manager.logout", {}); 25 | window.close(); 26 | }); 27 | 28 | attachmentBtn.addEventListener('click', (e) => 29 | { 30 | attachmentInput.click(); 31 | }); 32 | 33 | attachmentInput.addEventListener('change', (e) => 34 | { 35 | if (attachmentInput.files && attachmentInput.files[0]) 36 | { 37 | let reader = new FileReader(); 38 | reader.onload = (e) => 39 | { 40 | document.querySelector(".image").style.display = "flex"; 41 | attachmentImg.src = e.target.result; 42 | }; 43 | reader.readAsDataURL(attachmentInput.files[0]); 44 | } 45 | }); 46 | 47 | addTaskBtn.addEventListener('click', () => 48 | { 49 | if (!addTaskBtn.loading) newTask(contentInput.value); 50 | }); 51 | 52 | contentInput.addEventListener('keyup', (e) => 53 | { 54 | if (e.key === "Enter" || e.code === "Enter") 55 | { 56 | if (!addTaskBtn.loading) newTask(contentInput.value); 57 | } 58 | }); 59 | 60 | attachmentRemove.addEventListener('click', (e) => 61 | { 62 | resetImage(); 63 | }); 64 | 65 | function resetImage() 66 | { 67 | document.querySelector(".image").style.display = "none"; 68 | attachmentInput.value = ""; 69 | } 70 | 71 | function setProfile(err, data) 72 | { 73 | if (data) 74 | { 75 | state.user = data; 76 | document.getElementById("header").src = data.header; 77 | document.getElementById("avatar").src = data.avatar; 78 | document.querySelector("#name h1").innerHTML = data.name; 79 | document.getElementById("streak").innerHTML = data.streak; 80 | document.getElementById("claps").innerHTML = data.praise_received; 81 | document.getElementById("rest_days").innerHTML = data.rest_day_balance; 82 | } 83 | } 84 | 85 | function isLoading(status) 86 | { 87 | if (status) 88 | { 89 | addTaskBtn.loading = true; 90 | addTaskBtn.querySelector('.loader').style.display = "block"; 91 | addTaskBtn.querySelector('svg').style.display = "none"; 92 | } 93 | else 94 | { 95 | addTaskBtn.loading = false; 96 | addTaskBtn.querySelector('.loader').style.display = "none"; 97 | addTaskBtn.querySelector('svg').style.display = "block"; 98 | } 99 | } 100 | 101 | async function newTask(content) 102 | { 103 | let done = true, 104 | inProgress = false; 105 | 106 | if (content.startsWith("/todo ")) 107 | { 108 | done = false; 109 | inProgress = false; 110 | content = content.replace("/todo ", ''); 111 | } 112 | else if (content.startsWith("/doing ")) 113 | { 114 | done = false; 115 | inProgress = true; 116 | content = content.replace("/doing ", ''); 117 | } 118 | else content = content.replace("/done ", ''); 119 | 120 | if (!content) return; 121 | isLoading(true); 122 | 123 | let attach = undefined; 124 | if (attachmentInput.files && attachmentInput.files[0]) attach = attachmentInput.files[0]; 125 | 126 | try 127 | { 128 | const data = await Api.newTask(content, done, inProgress, attach); 129 | isLoading(false); 130 | if (data.id) 131 | { 132 | contentInput.value = ""; 133 | resetImage(); 134 | } 135 | else alert("Something went wrong!"); 136 | } 137 | catch (err) 138 | { 139 | alert("Something went wrong!"); 140 | } 141 | } 142 | 143 | chrome.runtime.onMessage.addListener((message, sender, _) => 144 | { 145 | switch (message.action) 146 | { 147 | case "Auth.set": 148 | { 149 | if (message.status) sendMessage("Api.getProfile"); 150 | else window.close(); 151 | } break; 152 | 153 | case "Api.setProfile": 154 | { 155 | setProfile(message.err, message.data); 156 | } break; 157 | } 158 | }); 159 | 160 | sendMessage("Auth.check"); -------------------------------------------------------------------------------- /src/popup/popup.scss: -------------------------------------------------------------------------------- 1 | html { 2 | overflow-y: overlay; 3 | } 4 | 5 | .main { 6 | width: 560px; 7 | overflow: hidden; 8 | position: relative; 9 | 10 | .header { 11 | position: absolute; 12 | width: 700px; 13 | height: 700px; 14 | overflow: hidden; 15 | z-index: -2; 16 | 17 | img { 18 | position: absolute; 19 | left: 50%; 20 | top: 50%; 21 | width: 100%; 22 | height: 100%; 23 | width: auto; 24 | transform: translate(-50%,-50%); 25 | filter: blur(5px); 26 | } 27 | 28 | .overlay { 29 | position: absolute; 30 | z-index: 5; 31 | background: rgba(0, 0, 0, 0.2); 32 | width: 100%; 33 | height: 100%; 34 | top: 0; 35 | left: 0; 36 | } 37 | } 38 | 39 | .gradient { 40 | position: absolute; 41 | z-index: -1; 42 | background: linear-gradient(transparent, rgba(22, 23, 27, 0.8), rgba(22, 23, 27, 0.99)); 43 | width: 100%; 44 | height: 100%; 45 | bottom: 0; 46 | } 47 | 48 | .content { 49 | padding: 1rem; 50 | width: 100%; 51 | overflow: hidden; 52 | 53 | .profile { 54 | width: 100%; 55 | display: flex; 56 | align-items: center; 57 | 58 | img { 59 | width: 70px; 60 | height: 70px; 61 | border-radius: 50%; 62 | padding: 2px; 63 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); 64 | background: white; 65 | } 66 | 67 | .info { 68 | display: flex; 69 | flex: 1; 70 | flex-direction: column; 71 | justify-content: center; 72 | margin-left: 1rem; 73 | 74 | .name { 75 | display: flex; 76 | align-items: center; 77 | cursor: pointer; 78 | 79 | h1 { 80 | font: bold 22px Nunito; 81 | color: #fff; 82 | } 83 | 84 | svg { 85 | width: 18px; 86 | height: 18px; 87 | color: #fff; 88 | margin-left: 0.5rem; 89 | } 90 | } 91 | 92 | .stats { 93 | list-style: none; 94 | margin-top: 0.2rem; 95 | display: flex; 96 | 97 | li { 98 | display: flex; 99 | align-items: center; 100 | color: #fff; 101 | margin-right: 1rem; 102 | 103 | svg { 104 | width: 20px; 105 | height: 20px; 106 | } 107 | 108 | h4 { 109 | font: 700 16px Nunito; 110 | margin-left: 0.5rem; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | .task { 118 | width: 100%; 119 | display: flex; 120 | align-items: center; 121 | height: 50px; 122 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.4); 123 | border-radius: 0.4rem; 124 | overflow: hidden; 125 | margin-top: 1.5rem; 126 | background: transparent; 127 | 128 | input { 129 | flex: 1; 130 | height: 100%; 131 | font-size: 18px; 132 | padding: 0.5rem; 133 | border: 0px; 134 | font: 600 18px Nunito; 135 | color: #16171b; 136 | background: #fff; 137 | } 138 | 139 | .action { 140 | width: 50px; 141 | height: 50px; 142 | color: #16171b; 143 | display: flex; 144 | align-items: center; 145 | justify-content: center; 146 | transition: background 0.2s ease-in-out, color 0.2s ease-in-out; 147 | background: #fff; 148 | 149 | input, .loader { 150 | display: none; 151 | } 152 | 153 | .loader div { 154 | background: #43b581; 155 | } 156 | 157 | svg { 158 | width: 20px; 159 | height: 20px; 160 | } 161 | 162 | &:hover { 163 | background: #43b581; 164 | color: #fff; 165 | cursor: pointer; 166 | 167 | .loader div { 168 | background: #fff; 169 | } 170 | } 171 | } 172 | } 173 | 174 | .image { 175 | width: 100%; 176 | max-height: 360px; 177 | margin-top: 0.5rem; 178 | display: none; 179 | align-items: center; 180 | justify-content: center; 181 | position: relative; 182 | 183 | img { 184 | max-width: 100%; 185 | max-height: 360px; 186 | border-radius: 0.4rem; 187 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); 188 | } 189 | 190 | span { 191 | position: absolute; 192 | bottom: 1rem; 193 | left: 50%; 194 | transform: translate(-50%, 0); 195 | display: flex; 196 | align-items: center; 197 | justify-content: center; 198 | width: 100px; 199 | height: 36px; 200 | background: rgba(#000, 0.4); 201 | border-radius: 0.2rem; 202 | font: 16px Nunito; 203 | transition: background 0.2s ease-in-out; 204 | cursor: pointer; 205 | backdrop-filter: blur(5px); 206 | color: white; 207 | 208 | &:hover { 209 | background: rgba(#000, 0.6); 210 | } 211 | } 212 | } 213 | 214 | .logout { 215 | width: 80px; 216 | height: 30px; 217 | background: #d32f2f; 218 | 219 | span { 220 | font-size: 14px; 221 | } 222 | } 223 | 224 | .footer { 225 | display: flex; 226 | margin-top: 1rem; 227 | 228 | ul { 229 | display: flex; 230 | align-items: center; 231 | list-style: none; 232 | padding: 0px; 233 | 234 | li { 235 | color: rgba(255, 255, 255, 0.6); 236 | font: 600 14px Nunito; 237 | margin-right: 0.5rem; 238 | transition: color 0.2s ease-in-out; 239 | display: flex; 240 | align-items: center; 241 | 242 | svg { 243 | width: 16px; 244 | height: 16px; 245 | margin-right: 0.5rem; 246 | } 247 | 248 | &:hover { 249 | color: #fff; 250 | cursor: pointer; 251 | } 252 | 253 | &:after { 254 | content: '|'; 255 | margin-left: 0.4rem; 256 | color: rgba(255, 255, 255, 0.6); 257 | } 258 | 259 | &:last-child { 260 | margin-right: 0rem; 261 | } 262 | 263 | &:last-child:after { 264 | content: ''; 265 | margin-right: 0rem; 266 | } 267 | } 268 | } 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/scss/base.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap'); 2 | 3 | * { 4 | padding: 0px; 5 | margin: 0px; 6 | box-sizing: border-box; 7 | font-family: Nunito; 8 | outline: none; 9 | border: 0; 10 | } 11 | 12 | body { 13 | background: #16171b; 14 | } 15 | 16 | // .green-color { 17 | // color: #01b075; 18 | // } 19 | 20 | // .green-background { 21 | // background: #01b075; 22 | // } 23 | 24 | // .dark-color { 25 | // color: #16171b; 26 | // } 27 | 28 | // .dark-background { 29 | // background: #16171b; 30 | // } 31 | 32 | // .gray-color { 33 | // color: #202125; 34 | // } 35 | 36 | // .gray-background { 37 | // background: #202125; 38 | // } 39 | 40 | a { 41 | color: #01b075; 42 | text-decoration: none; 43 | } 44 | 45 | .input { 46 | width: 100%; 47 | margin-top: 1rem; 48 | 49 | label { 50 | display: block; 51 | color: #545557; 52 | font: bold 14px Nunito; 53 | } 54 | 55 | input { 56 | width: 100%; 57 | height: 40px; 58 | font: normal 16px Nunito; 59 | color: #16171b; 60 | border: 2px solid transparent; 61 | margin-top: 0.3rem; 62 | border-radius: 0.2rem; 63 | padding: 1rem; 64 | 65 | &:focus { 66 | border: 2px solid #01b075; 67 | } 68 | } 69 | } 70 | 71 | button { 72 | display: flex; 73 | align-items: center; 74 | justify-content: center; 75 | width: 100%; 76 | background: #01b075; 77 | border: 0px; 78 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.6); 79 | border-radius: 0.2rem; 80 | height: 40px; 81 | margin: 2rem auto 0rem auto; 82 | cursor: pointer; 83 | transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out; 84 | color: #fff; 85 | 86 | span { 87 | font: 600 16px Nunito; 88 | color: #fff; 89 | margin: 0; 90 | } 91 | 92 | .loader { 93 | display: none; 94 | } 95 | 96 | &:hover { 97 | transform: scale(1.02); 98 | box-shadow: 0px 0px 15px rgba(27, 92, 44, 0.5); 99 | } 100 | } 101 | 102 | .space { 103 | flex: 1; 104 | } 105 | 106 | /* Loader */ 107 | .loader { 108 | position: relative; 109 | width: 40px; 110 | height: 40px; 111 | 112 | div { 113 | position: absolute; 114 | top: 16.5px; 115 | width: 8px; 116 | height: 8px; 117 | border-radius: 50%; 118 | background: #fff; 119 | animation-timing-function: cubic-bezier(0, 1, 1, 0); 120 | } 121 | 122 | div:nth-child(1) { 123 | left: 4px; 124 | animation: loader1 0.6s infinite; 125 | } 126 | 127 | div:nth-child(2) { 128 | left: 4px; 129 | animation: loader2 0.6s infinite; 130 | } 131 | 132 | div:nth-child(3) { 133 | left: 16px; 134 | animation: loader2 0.6s infinite; 135 | } 136 | 137 | div:nth-child(4) { 138 | left: 26px; 139 | animation: loader3 0.6s infinite; 140 | } 141 | 142 | @keyframes loader1 { 143 | 0% { 144 | transform: scale(0); 145 | } 146 | 100% { 147 | transform: scale(1); 148 | } 149 | } 150 | 151 | @keyframes loader3 { 152 | 0% { 153 | transform: scale(1); 154 | } 155 | 100% { 156 | transform: scale(0); 157 | } 158 | } 159 | 160 | @keyframes loader2 { 161 | 0% { 162 | transform: translate(0, 0); 163 | } 164 | 165 | 100% { 166 | transform: translate(12px, 0); 167 | } 168 | } 169 | } 170 | 171 | ::-webkit-scrollbar { 172 | width: 6px; 173 | position: absolute; 174 | } 175 | 176 | ::-webkit-scrollbar-track { 177 | background: transparent; 178 | border-radius: 2rem; 179 | } 180 | 181 | ::-webkit-scrollbar-thumb { 182 | background: rgba(white, 0.2); 183 | border-radius: 2rem; 184 | transition: background 0.2s ease-in-out; 185 | } 186 | 187 | ::-webkit-scrollbar-thumb:hover { 188 | background: rgba(white, 0.4); 189 | } 190 | -------------------------------------------------------------------------------- /src/utils/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import axios from 'axios'; 3 | 4 | const baseUrl = "https://api.getmakerlog.com"; 5 | let token = "", user = null; 6 | 7 | (async () => 8 | { 9 | const result = await browser.storage.local.get(['token', 'user']); 10 | token = result.token; 11 | user = result.user; 12 | })(); 13 | 14 | async function login(username, password) 15 | { 16 | try 17 | { 18 | const {data} = await axios.post(`${baseUrl}/api-token-auth/`, {username, password}); 19 | if (data.token) 20 | { 21 | await browser.storage.local.set({token: data.token}); 22 | token = data.token; 23 | return true; 24 | } 25 | else return false; 26 | } 27 | catch (err) 28 | { 29 | console.log(err); 30 | throw err; 31 | } 32 | } 33 | 34 | async function getProfile(callback) 35 | { 36 | if (user) callback(user); 37 | try 38 | { 39 | const headers = { 40 | "Authorization": `Token ${token}` 41 | }; 42 | const {data} = await axios.get(`${baseUrl}/me/`, {headers}); 43 | if (data) 44 | { 45 | const stats = await getStats(data.username); 46 | const result = { 47 | username: data.username, 48 | name: `${data.first_name} ${data.last_name}`, 49 | avatar: data.avatar, 50 | header: data.header, 51 | remaining_tasks: stats.remaining_tasks, 52 | done_today: stats.done_today, 53 | streak: stats.streak, 54 | praise_received: stats.praise_received, 55 | rest_day_balance: stats.rest_day_balance 56 | }; 57 | 58 | await browser.storage.local.set({user: result}); 59 | user = result; 60 | callback(result); 61 | } 62 | else callback(null); 63 | } 64 | catch (err) 65 | { 66 | console.log(err); 67 | throw err; 68 | } 69 | } 70 | 71 | async function getStats(username) 72 | { 73 | try 74 | { 75 | const headers = { 76 | "Authorization": `Token ${token}` 77 | }; 78 | const {data} = await axios.get(`${baseUrl}/users/${username}/stats`, {headers}); 79 | if (data) return data; 80 | else return null; 81 | } 82 | catch (err) 83 | { 84 | console.log(err); 85 | throw err; 86 | } 87 | } 88 | 89 | async function newTask(content, done, inProgress, attachment) 90 | { 91 | try 92 | { 93 | const headers = { 94 | "Authorization": `Token ${token}`, 95 | "Content-Type": "multipart/form-data" 96 | }; 97 | 98 | const formData = new FormData(); 99 | formData.append('content', content); 100 | formData.append('in_progress', inProgress); 101 | formData.append('done', done); 102 | if (attachment) formData.append('attachment', attachment); 103 | 104 | const {data} = await axios.post(`${baseUrl}/tasks/`, formData, {headers}); 105 | if (data) return data; 106 | else return null; 107 | } 108 | catch (err) 109 | { 110 | console.log(err); 111 | throw err; 112 | } 113 | } 114 | 115 | export default { 116 | login, 117 | getProfile, 118 | getStats, 119 | newTask 120 | }; -------------------------------------------------------------------------------- /src/utils/element.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function create(text) 4 | { 5 | const wrapper = document.createElement('div'); 6 | wrapper.innerHTML = text; 7 | return wrapper.firstElementChild; 8 | } 9 | 10 | export default { 11 | create 12 | }; -------------------------------------------------------------------------------- /src/utils/sendMessage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const sendMessage = (action, message, tabId) => 4 | { 5 | if (!message) message = {}; 6 | message.action = action; 7 | 8 | if (tabId) browser.tabs.sendMessage(tabId, message); 9 | else browser.runtime.sendMessage(message); 10 | } 11 | 12 | export default sendMessage; -------------------------------------------------------------------------------- /src/utils/svg.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const arrowRight = ``; 4 | 5 | const image = `` 6 | 7 | export default { 8 | arrowRight, 9 | image 10 | }; -------------------------------------------------------------------------------- /src/vendors/browser-polyfill.min.js: -------------------------------------------------------------------------------- 1 | (function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";if("undefined"==typeof browser||Object.getPrototypeOf(browser)!==Object.prototype){if("object"!=typeof chrome||!chrome||!chrome.runtime||!chrome.runtime.id)throw new Error("This script should only be loaded in a browser extension.");a.exports=(a=>{const b={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(b).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class c extends WeakMap{constructor(a,b=void 0){super(b),this.createItem=a}get(a){return this.has(a)||this.set(a,this.createItem(a)),super.get(a)}}const d=a=>a&&"object"==typeof a&&"function"==typeof a.then,e=(b,c)=>(...d)=>{a.runtime.lastError?b.reject(a.runtime.lastError):c.singleCallbackArg||1>=d.length&&!1!==c.singleCallbackArg?b.resolve(d[0]):b.resolve(d)},f=a=>1==a?"argument":"arguments",g=(a,b)=>function(c,...d){if(d.lengthb.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((f,g)=>{if(b.fallbackToNoCallback)try{c[a](...d,e({resolve:f,reject:g},b))}catch(e){console.warn(`${a} API method doesn't seem to support the callback parameter, `+"falling back to call it without a callback: ",e),c[a](...d),b.fallbackToNoCallback=!1,b.noCallback=!0,f()}else b.noCallback?(c[a](...d),f()):c[a](...d,e({resolve:f,reject:g},b))})},h=(a,b,c)=>new Proxy(b,{apply(b,d,e){return c.call(d,a,...e)}});let i=Function.call.bind(Object.prototype.hasOwnProperty);const j=(a,b={},c={})=>{let d=Object.create(null),e=Object.create(a);return new Proxy(e,{has(b,c){return c in a||c in d},get(e,f){if(f in d)return d[f];if(!(f in a))return;let k=a[f];if("function"==typeof k){if("function"==typeof b[f])k=h(a,a[f],b[f]);else if(i(c,f)){let b=g(f,c[f]);k=h(a,a[f],b)}else k=k.bind(a);}else if("object"==typeof k&&null!==k&&(i(b,f)||i(c,f)))k=j(k,b[f],c[f]);else if(i(c,"*"))k=j(k,b[f],c["*"]);else return Object.defineProperty(d,f,{configurable:!0,enumerable:!0,get(){return a[f]},set(b){a[f]=b}}),k;return d[f]=k,k},set(b,c,e){return c in d?d[c]=e:a[c]=e,!0},defineProperty(a,b,c){return Reflect.defineProperty(d,b,c)},deleteProperty(a,b){return Reflect.deleteProperty(d,b)}})},k=a=>({addListener(b,c,...d){b.addListener(a.get(c),...d)},hasListener(b,c){return b.hasListener(a.get(c))},removeListener(b,c){b.removeListener(a.get(c))}});let l=!1;const m=new c(a=>"function"==typeof a?function(b,c,e){let f,g,h=!1,i=new Promise(a=>{f=function(b){l||(console.warn("Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)",new Error().stack),l=!0),h=!0,a(b)}});try{g=a(b,c,f)}catch(a){g=Promise.reject(a)}const j=!0!==g&&d(g);if(!0!==g&&!j&&!h)return!1;const k=a=>{a.then(a=>{e(a)},a=>{let b;b=a&&(a instanceof Error||"string"==typeof a.message)?a.message:"An unexpected error occurred",e({__mozWebExtensionPolyfillReject__:!0,message:b})}).catch(a=>{console.error("Failed to send onMessage rejected reply",a)})};return j?k(g):k(i),!0}:a),n=({reject:b,resolve:c},d)=>{a.runtime.lastError?a.runtime.lastError.message==="The message port closed before a response was received."?c():b(a.runtime.lastError):d&&d.__mozWebExtensionPolyfillReject__?b(new Error(d.message)):c(d)},o=(a,b,c,...d)=>{if(d.lengthb.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((a,b)=>{const e=n.bind(null,{resolve:a,reject:b});d.push(e),c.sendMessage(...d)})},p={runtime:{onMessage:k(m),onMessageExternal:k(m),sendMessage:o.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:o.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},q={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return b.privacy={network:{"*":q},services:{"*":q},websites:{"*":q}},j(a,p,b)})(chrome)}else a.exports=browser}); 2 | //# sourceMappingURL=browser-polyfill.min.js.map 3 | 4 | // webextension-polyfill v.0.7.0 (https://github.com/mozilla/webextension-polyfill) 5 | 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 9 | -------------------------------------------------------------------------------- /test/sample.js: -------------------------------------------------------------------------------- 1 | describe('Test extension', () => { 2 | 3 | it('This is a dummy test', () => { 4 | expect(true).to.be.true; 5 | }); 6 | }); --------------------------------------------------------------------------------