├── .gitignore ├── LICENSE.md ├── README.md ├── babel.config.js ├── bookmarklet-manager.ai ├── docs └── bookmarklets-manager.gif ├── ext ├── icons │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-19.png │ └── icon-48.png ├── manifest.json ├── options │ ├── icon-128.png │ └── index.html └── popup │ ├── popup.html │ └── popup.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── app.css ├── components │ ├── AddBookmarkletDialog.vue │ ├── Bookmark.vue │ ├── BookmarkSideBar.vue │ ├── Button.vue │ ├── Checkbox.vue │ ├── Editor.vue │ ├── Options.vue │ └── RemoveBookmarkletDialog.vue ├── main.js └── util │ ├── BookmarkStore.js │ ├── DynamicComponent.js │ ├── EditorHelper.js │ ├── JavascriptUrlParser.js │ └── Storage.js └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | \.DS_Store 31 | 32 | app/\.cache/ 33 | 34 | ext/options/app/ 35 | 36 | .DS_Store 37 | node_modules 38 | /dist 39 | 40 | # local env files 41 | .env.local 42 | .env.*.local 43 | 44 | # Log files 45 | npm-debug.log* 46 | yarn-debug.log* 47 | yarn-error.log* 48 | 49 | # Editor directories and files 50 | .idea 51 | .vscode 52 | *.suo 53 | *.ntvs* 54 | *.njsproj 55 | *.sln 56 | *.sw* 57 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2018 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :bookmark: Chrome Bookmarklet Manager 2 | 3 | A chrome extension to edit [bookmarklets](https://en.wikipedia.org/wiki/Bookmarklet) with the [monaco editor](https://microsoft.github.io/monaco-editor/). 4 | 5 | ![bookmarklet manager demo](docs/bookmarklets-manager.gif) 6 | 7 | 8 | > A bookmarklet is a bookmark stored in a web browser that contains JavaScript commands that add new features to the browser. 9 | 10 | 11 | ## Install 12 | > pending review on chrome store 13 | 14 | - Go to `chrome://extensions/` 15 | - Enable `Developer mode` slider 16 | - Download the latest `.ext` from (releases](https://github.com/ahmed-musallam/chrome-bookmarklet-manager/releases) 17 | - Drag `.ext` file onto chrome to install 18 | 19 | ## Development 20 | 21 | - Clone this repo 22 | - Go to `chrome://extensions/` 23 | - Enable `Developer mode` slider 24 | - Click `Load unpacked` 25 | - Aelect the `ext` folder from the cloned repo 26 | - Run `npm run dev`: will watch source changes and build the Js bundle and put it in the `ext` folder. 27 | 28 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/app"] 3 | }; 4 | -------------------------------------------------------------------------------- /bookmarklet-manager.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/bookmarklet-manager.ai -------------------------------------------------------------------------------- /docs/bookmarklets-manager.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/docs/bookmarklets-manager.gif -------------------------------------------------------------------------------- /ext/icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/ext/icons/icon-128.png -------------------------------------------------------------------------------- /ext/icons/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/ext/icons/icon-16.png -------------------------------------------------------------------------------- /ext/icons/icon-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/ext/icons/icon-19.png -------------------------------------------------------------------------------- /ext/icons/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/ext/icons/icon-48.png -------------------------------------------------------------------------------- /ext/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bookmarklet Manager", 3 | "version": "0.0.1", 4 | "manifest_version": 2, 5 | "description": "A manager for bookmarklets", 6 | "homepage_url": "https://github.com/ahmed-musallam/chrome-bookmarklet-manager", 7 | "icons": { 8 | "16": "icons/icon-16.png", 9 | "48": "icons/icon-48.png", 10 | "128": "icons/icon-128.png" 11 | }, 12 | "options_page": "options/index.html", 13 | "browser_action": { 14 | "default_icon": "icons/icon-19.png", 15 | "default_title": "browser action demo", 16 | "default_popup": "popup/popup.html" 17 | }, 18 | "permissions": [ 19 | "bookmarks", 20 | "tabs", 21 | "storage" 22 | ] 23 | } -------------------------------------------------------------------------------- /ext/options/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/ext/options/icon-128.png -------------------------------------------------------------------------------- /ext/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bookmarklet Manager 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ext/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ext/popup/popup.js: -------------------------------------------------------------------------------- 1 | // open options page then close popup. 2 | chrome.runtime.openOptionsPage(function(){ 3 | window.close(); 4 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-bookmarklet-manager", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "dev": "vue-cli-service build --watch --mode=development --report", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "monaco-editor": "^0.14.3", 13 | "p-event": "^4.1.0", 14 | "split.js": "^1.5.11", 15 | "vue": "^2.6.10", 16 | "vue-toasted": "^1.1.27" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^3.8.0", 20 | "@vue/cli-plugin-eslint": "^3.8.0", 21 | "@vue/cli-service": "^3.8.3", 22 | "eslint-config-prettier": "^4.3.0", 23 | "eslint-plugin-prettier": "^3.1.0", 24 | "monaco-editor-webpack-plugin": "^1.7.0", 25 | "vue-template-compiler": "^2.6.10" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true 31 | }, 32 | "globals": { 33 | "chrome": true 34 | }, 35 | "extends": [ 36 | "plugin:vue/essential", 37 | "plugin:prettier/recommended", 38 | "eslint:recommended" 39 | ], 40 | "rules": { 41 | "no-console": 0 42 | }, 43 | "parserOptions": { 44 | "parser": "babel-eslint" 45 | } 46 | }, 47 | "postcss": { 48 | "plugins": { 49 | "autoprefixer": {} 50 | } 51 | }, 52 | "browserslist": [ 53 | "last 2 Chrome versions" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | chrome-bookmarklet-manager 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 70 | 71 | 122 | -------------------------------------------------------------------------------- /src/assets/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmed-musallam/chrome-bookmarklet-manager/872d24149f306f7697ad14ae27c58eb62c968195/src/assets/app.css -------------------------------------------------------------------------------- /src/components/AddBookmarkletDialog.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 40 | 85 | -------------------------------------------------------------------------------- /src/components/Bookmark.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 162 | 163 | 164 | 244 | -------------------------------------------------------------------------------- /src/components/BookmarkSideBar.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | 41 | 47 | -------------------------------------------------------------------------------- /src/components/Button.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 25 | -------------------------------------------------------------------------------- /src/components/Checkbox.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | 82 | -------------------------------------------------------------------------------- /src/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 114 | 115 | 116 | 139 | -------------------------------------------------------------------------------- /src/components/Options.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /src/components/RemoveBookmarkletDialog.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 43 | 88 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import BookmarkStore from "./util/BookmarkStore"; 4 | import Toasted from "vue-toasted"; 5 | 6 | Vue.use(Toasted); 7 | Vue.config.productionTip = false; 8 | 9 | new Vue({ 10 | data: { 11 | sharedState: BookmarkStore.state 12 | }, 13 | render: h => h(App) 14 | }).$mount("#app"); 15 | -------------------------------------------------------------------------------- /src/util/BookmarkStore.js: -------------------------------------------------------------------------------- 1 | const BookmarkStore = { 2 | debug: true, 3 | state: { 4 | bookmarks: [], 5 | currentBookmark: "", 6 | config: { 7 | bookmarkletsOnly: true 8 | } 9 | } 10 | }; 11 | 12 | function getBookmarks() { 13 | chrome.bookmarks.getTree(bookmarks => { 14 | BookmarkStore.state.bookmarks = bookmarks[0].children; 15 | }); 16 | } 17 | chrome.bookmarks.onCreated.addListener(getBookmarks); 18 | chrome.bookmarks.onRemoved.addListener(getBookmarks); 19 | chrome.bookmarks.onMoved.addListener(getBookmarks); 20 | 21 | getBookmarks(); 22 | export default BookmarkStore; 23 | -------------------------------------------------------------------------------- /src/util/DynamicComponent.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | export default class DynamicComponent { 3 | static createAppendComponent(component, config) { 4 | // new instance 5 | var ComponentClass = Vue.extend(component); 6 | var instance = new ComponentClass(config); 7 | // mont, add to dom and show 8 | instance.$mount(); 9 | document.body.appendChild(instance.$el); 10 | return instance; 11 | } 12 | static destroyAndRemoveCompoennt(instance) { 13 | if (!instance) return; 14 | instance.$destroy(); 15 | instance.$el.parentNode.removeChild(instance.$el); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/util/EditorHelper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A few helpers for editor... the Editor component was getting too large :) 3 | */ 4 | import * as monaco from "monaco-editor"; 5 | 6 | export default class EditorHelper { 7 | /** 8 | * Set worker urls. 9 | * Those are particularly set this way since that's where webpack outputs them in the extension 10 | */ 11 | /* 12 | static initWorkerUrls () { 13 | const PREFIX = "./app/"; 14 | self.MonacoEnvironment = { 15 | getWorkerUrl: function(moduleId, label) { 16 | if (label === "json") { 17 | return PREFIX + "json.worker.js"; 18 | } 19 | if (label === "css") { 20 | return PREFIX + "css.worker.js"; 21 | } 22 | if (label === "html") { 23 | return PREFIX + "html.worker.js"; 24 | } 25 | if (label === "typescript" || label === "javascript") { 26 | return PREFIX + "typescript.worker.js"; 27 | } 28 | return PREFIX + "editor.worker.js"; 29 | } 30 | }; 31 | } 32 | */ 33 | /** 34 | * Default editor configs 35 | */ 36 | static getConfig() { 37 | return { 38 | language: "javascript", 39 | theme: "vs-dark", 40 | minimap: { 41 | enabled: false 42 | } 43 | }; 44 | } 45 | static create(el) { 46 | return monaco.editor.create(el, EditorHelper.getConfig()); 47 | } 48 | /** 49 | * Add an overlay button to the editor 50 | * @param {*} editor 51 | * @param {*} options 52 | * { 53 | * text: 'the button text', 54 | * onClick: () => (), // the click handler 55 | * id: 'id' // the id 56 | * } 57 | */ 58 | static addBtn(editor, options) { 59 | return editor.addOverlayWidget({ 60 | getDomNode: () => { 61 | var btn = document.createElement("button"); 62 | btn.innerHTML = options.text; 63 | btn.onclick = options.onClick; 64 | return btn; 65 | }, 66 | getId() { 67 | return options.id; 68 | }, 69 | getPosition() { 70 | return { 71 | preference: 0 72 | }; 73 | } 74 | }); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/util/JavascriptUrlParser.js: -------------------------------------------------------------------------------- 1 | const jsPrefix = "javascript:"; 2 | export default class JavascriptUrlParser { 3 | static isValid(url) { 4 | // is string 5 | if (typeof url == "string" || url instanceof String) { 6 | return url.trim().startsWith(jsPrefix); 7 | } else return false; 8 | } 9 | static decode(url) { 10 | if (JavascriptUrlParser.isValid(url)) { 11 | const encodedJS = url.trim().replace(jsPrefix, ""); 12 | return decodeURIComponent(encodedJS); 13 | } else return url; 14 | } 15 | static encode(script) { 16 | const encodedJs = encodeURIComponent(script); 17 | return jsPrefix + encodedJs; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/util/Storage.js: -------------------------------------------------------------------------------- 1 | export default class Storage { 2 | static set(items, callback) { 3 | chrome.storage.sync.set(items, callback); 4 | } 5 | static get(keys, callback) { 6 | chrome.storage.sync.get(keys, callback); 7 | } 8 | static remove(keys, callback) { 9 | chrome.storage.sync.remove(keys, callback); 10 | } 11 | static getAll(callback) { 12 | chrome.storage.sync.get(null, callback); 13 | } 14 | static clean() { 15 | Storage.getAll(items => { 16 | Object.keys(items).forEach(key => { 17 | chrome.bookmarks.get([key], bookmarks => { 18 | if (!bookmarks || !bookmarks.length) { 19 | Storage.remove([key]); 20 | } 21 | }); 22 | }); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); 2 | 3 | module.exports = { 4 | baseUrl: "./app/", 5 | outputDir: "ext/options/app", 6 | configureWebpack: { 7 | // Chrome extensions do not support `eval` thus we chose an appropriate devtool 8 | // see: https://webpack.js.org/configuration/devtool/ 9 | devtool: process.env.NODE_ENV == "development" ? "cheap-source-map" : false, 10 | output: { 11 | globalObject: "self", 12 | filename: "[name].js" 13 | }, 14 | plugins: [ 15 | new MonacoWebpackPlugin({ 16 | // output: "../ext/options/app", 17 | languages: ["javascript", "typescript"] 18 | }) 19 | ], 20 | entry: { 21 | // "editor.worker": 'monaco-editor/esm/vs/editor/editor.worker.js', 22 | // "json.worker": 'monaco-editor/esm/vs/language/json/json.worker', 23 | // "css.worker": 'monaco-editor/esm/vs/language/css/css.worker', 24 | // "html.worker": 'monaco-editor/esm/vs/language/html/html.worker', 25 | // "ts.worker": 'monaco-editor/esm/vs/language/typescript/ts.worker', 26 | } 27 | } 28 | }; 29 | --------------------------------------------------------------------------------