├── .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 | 
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 |
2 |
3 |
8 |
13 |
14 |
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 |
2 |
11 |
12 |
13 |
40 |
85 |
--------------------------------------------------------------------------------
/src/components/Bookmark.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
19 |
24 |
25 |
26 |
27 |
28 |
162 |
163 |
164 |
244 |
--------------------------------------------------------------------------------
/src/components/BookmarkSideBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
Bookmarklets
12 |
17 |
18 |
19 |
20 |
40 |
41 |
47 |
--------------------------------------------------------------------------------
/src/components/Button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
25 |
--------------------------------------------------------------------------------
/src/components/Checkbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
26 |
27 |
82 |
--------------------------------------------------------------------------------
/src/components/Editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
114 |
115 |
116 |
139 |
--------------------------------------------------------------------------------
/src/components/Options.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
21 |
--------------------------------------------------------------------------------
/src/components/RemoveBookmarkletDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
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 |
--------------------------------------------------------------------------------