├── dist
├── scripts
│ └── .gitkeep
├── styles
│ └── .gitkeep
├── images
│ ├── icon.png
│ ├── icon-128.png
│ ├── icon-16.png
│ └── icon-48.png
└── manifest.json
├── .gitignore
├── src
├── scripts
│ ├── background.js
│ ├── util.js
│ └── main.js
├── data
│ └── styles.json
└── styles
│ └── main.scss
├── .editorconfig
├── .github
└── workflows
│ └── ci.yml
├── webpack.mix.js
├── LICENSE
├── package.json
└── readme.md
/dist/scripts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/styles/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dist/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenorocha/codecopy/HEAD/dist/images/icon.png
--------------------------------------------------------------------------------
/dist/images/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenorocha/codecopy/HEAD/dist/images/icon-128.png
--------------------------------------------------------------------------------
/dist/images/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenorocha/codecopy/HEAD/dist/images/icon-16.png
--------------------------------------------------------------------------------
/dist/images/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenorocha/codecopy/HEAD/dist/images/icon-48.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.xpi
2 | *.zip
3 | node_modules
4 | .idea
5 | dist/scripts/*
6 | dist/styles/*
7 | !/**/.gitkeep
--------------------------------------------------------------------------------
/src/scripts/background.js:
--------------------------------------------------------------------------------
1 | import 'webext-dynamic-content-scripts';
2 | import addDomainPermissionToggle from 'webext-domain-permission-toggle';
3 |
4 | addDomainPermissionToggle();
5 |
--------------------------------------------------------------------------------
/src/data/styles.json:
--------------------------------------------------------------------------------
1 | {
2 | "large": [
3 | "github.com",
4 | "gist.github.com",
5 | "medium.com",
6 | "www.npmjs.com",
7 | "developer.mozilla.org",
8 | "tableless.com.br",
9 | "laracasts.com",
10 | "docs.rs",
11 | "www.digitalocean.com",
12 | "developer.github.com"
13 | ],
14 | "xlarge": [
15 | "nodejs.org"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 2
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
18 | [package.json]
19 | indent_size = 2
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [ 10.x, 12.x, 14.x ]
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Use Node.js ${{ matrix.node-version }}
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 | - run: npm install
24 | - run: npm start
25 |
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix');
2 |
3 | /*
4 | |--------------------------------------------------------------------------
5 | | Mix Asset Management
6 | |--------------------------------------------------------------------------
7 | |
8 | | Mix provides a clean, fluent API for defining some Webpack build steps
9 | | for your Laravel application. By default, we are compiling the Sass
10 | | file for your application, as well as bundling up your JS files.
11 | |
12 | */
13 |
14 | mix.js('src/scripts/main.js', 'dist/scripts')
15 | .js('src/scripts/background.js', 'dist/scripts')
16 | .sass('src/styles/main.scss', 'dist/styles');
17 |
18 | // Disable mix-manifest.json
19 | Mix.manifest.refresh = _ => void 0
20 |
--------------------------------------------------------------------------------
/src/scripts/util.js:
--------------------------------------------------------------------------------
1 | import styles from '../data/styles';
2 |
3 | export const htmlButton = `
4 | `;
9 |
10 | export function getSiteStyle() {
11 | let currentStyle
12 |
13 | Object.keys(styles).forEach((style) => {
14 | if (styles[style].indexOf(document.location.hostname) !== -1) {
15 | currentStyle = style;
16 | }
17 | });
18 |
19 | return currentStyle || 'small';
20 | }
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Zeno Rocha
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/styles/main.scss:
--------------------------------------------------------------------------------
1 | .codecopy {
2 | @import "@primer/css/tooltips/index.scss";
3 | @import "@primer/css/buttons/index.scss";
4 |
5 | position: relative;
6 | overflow: visible;
7 |
8 | .codecopy-btn {
9 | @extend .btn;
10 | @extend .btn-sm;
11 |
12 | box-shadow: none;
13 | min-height: initial;
14 |
15 | transition: opacity 0.3s ease-in-out;
16 | opacity: 0;
17 |
18 | position: absolute;
19 | z-index: 1;
20 |
21 | .codecopy-btn-icon {
22 | border-radius: 0;
23 | margin-top: -3px;
24 | position: relative;
25 | top: 3px;
26 | padding: 0;
27 | vertical-align: initial;
28 | min-height: initial;
29 | }
30 |
31 | &:hover,
32 | &:focus {
33 | box-shadow: none;
34 | }
35 | }
36 |
37 | &:hover .codecopy-btn {
38 | opacity: 1;
39 | }
40 | }
41 |
42 | .codecopy.codecopy-small .codecopy-btn {
43 | padding: 2px 6px;
44 | right: 0;
45 | top: 0;
46 | }
47 |
48 | .codecopy.codecopy-large .codecopy-btn {
49 | padding: 3px 6px;
50 | right: 5px;
51 | top: 5px;
52 | }
53 |
54 | .codecopy.codecopy-xlarge .codecopy-btn {
55 | padding: 3px 6px;
56 | right: 30px;
57 | top: 10px;
58 | }
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codecopy",
3 | "version": "1.3.0",
4 | "description": "Because copy to clipboard buttons should exist on every code snippet",
5 | "main": "src/scripts/main.js",
6 | "repository": "zenorocha/codecopy",
7 | "license": "MIT",
8 | "dependencies": {
9 | "@primer/css": "^15.2.0",
10 | "clipboard": "^2.0.6",
11 | "webext-domain-permission-toggle": "^1.0.1",
12 | "webext-dynamic-content-scripts": "^7.0.0"
13 | },
14 | "devDependencies": {
15 | "cross-env": "^7.0.2",
16 | "laravel-mix": "^5.0.7",
17 | "resolve-url-loader": "^3.1.0",
18 | "sass": "^1.27.0",
19 | "sass-loader": "^8.0.2",
20 | "vue-template-compiler": "^2.6.12"
21 | },
22 | "scripts": {
23 | "start": "npm run build",
24 | "build": "npm run prod",
25 | "dev": "npm run development",
26 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
27 | "watch": "npm run development -- --watch",
28 | "prod": "npm run production",
29 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
30 | "package": "npm run package:blink && npm run package:gecko",
31 | "package:blink": "cd dist && zip -r ../codecopy.zip * && cd ..",
32 | "package:gecko": "cd dist && zip -r ../codecopy.xpi * && cd .."
33 | }
34 | }
--------------------------------------------------------------------------------
/src/scripts/main.js:
--------------------------------------------------------------------------------
1 | import ClipboardJS from 'clipboard';
2 | import {htmlButton, getSiteStyle} from './util';
3 |
4 | // Get button style based on the current page
5 |
6 | const siteStyle = getSiteStyle();
7 |
8 | // Scan for code snippets and append buttons
9 |
10 | const snippets = document.querySelectorAll('pre');
11 |
12 | snippets.forEach((snippet) => {
13 | const parent = snippet.parentNode;
14 | const wrapper = document.createElement('div');
15 |
16 | parent.replaceChild(wrapper, snippet);
17 | wrapper.appendChild(snippet);
18 |
19 | wrapper.classList.add('codecopy', `codecopy-${siteStyle}`);
20 | wrapper.firstChild.insertAdjacentHTML('beforebegin', htmlButton);
21 | });
22 |
23 | // Add copy to clipboard functionality and user feedback
24 |
25 | const clipboard = new ClipboardJS('.codecopy-btn', {
26 | target: (trigger) => {
27 | return trigger.parentNode;
28 | }
29 | });
30 |
31 | clipboard.on('success', (e) => {
32 | e.trigger.setAttribute('aria-label', 'Copied!');
33 | e.clearSelection();
34 | });
35 |
36 | // Replace tooltip message when mouse leaves button
37 | // and prevent page refresh after click button
38 |
39 | const btns = document.querySelectorAll('.codecopy-btn');
40 |
41 | btns.forEach((btn) => {
42 | btn.addEventListener('mouseleave', (e) => {
43 | e.target.setAttribute('aria-label', 'Copy to clipboard');
44 | e.target.blur();
45 | });
46 |
47 | btn.addEventListener('click', (e) => {
48 | e.preventDefault()
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "CodeCopy",
4 | "description": "Because copy to clipboard buttons should exist on every code snippet",
5 | "homepage_url": "https://github.com/zenorocha/codecopy",
6 | "version": "1.3.0",
7 | "author": "Zeno Rocha",
8 | "applications": {
9 | "gecko": {
10 | "id": "codecopy@clipboardjs.com",
11 | "strict_min_version": "45.0"
12 | }
13 | },
14 | "icons": {
15 | "16": "images/icon-16.png",
16 | "48": "images/icon-48.png",
17 | "128": "images/icon-128.png"
18 | },
19 | "browser_action": {
20 | "default_icon": {
21 | "16": "images/icon-16.png",
22 | "48": "images/icon-48.png",
23 | "128": "images/icon-128.png"
24 | }
25 | },
26 | "permissions": [
27 | "contextMenus",
28 | "activeTab"
29 | ],
30 | "optional_permissions": [
31 | "http://*/*",
32 | "https://*/*"
33 | ],
34 | "background": {
35 | "scripts": [
36 | "scripts/background.js"
37 | ]
38 | },
39 | "content_scripts": [
40 | {
41 | "css": [
42 | "styles/main.css"
43 | ],
44 | "js": [
45 | "scripts/main.js"
46 | ],
47 | "matches": [
48 | "https://developer.mozilla.org/*",
49 | "https://medium.com/*",
50 | "https://www.npmjs.com/*",
51 | "https://github.com/*/*",
52 | "https://gist.github.com/*",
53 | "https://developer.github.com/*",
54 | "https://stackexchange.com/*",
55 | "https://*.stackexchange.com/*",
56 | "https://serverfault.com/*",
57 | "https://superuser.com/*",
58 | "https://askubuntu.com/*",
59 | "https://stackoverflow.com/*",
60 | "https://www.digitalocean.com/community/*",
61 | "https://nodejs.org/api/*",
62 | "https://css-tricks.com/*"
63 | ]
64 | }
65 | ]
66 | }
67 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # CodeCopy
2 |
3 | > Because copy to clipboard buttons should exist on every code snippet.
4 |
5 | 
6 |
7 | ## Install
8 |
9 | This browser extension available for:
10 |
11 | |
|
|
12 | |:---:|:---:|
13 | | [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) | [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/) |
14 |
15 | ## Supported sites
16 |
17 | Works with:
18 |
19 | * [GitHub](https://github.com/)
20 | * [MDN](https://developer.mozilla.org/)
21 | * [Gist](https://gist.github.com/)
22 | * [StackOverflow](http://stackoverflow.com/)
23 | * [StackExchange](https://stackexchange.com/sites)
24 | * [npm](https://www.npmjs.com/)
25 | * [Medium](https://medium.com/)
26 |
27 | [And more](https://github.com/zenorocha/codecopy/blob/7c638611f7ad01d923361f7fedfe3933b35e114c/dist/manifest.json#L27).
28 |
29 | If you want to add a new site, feel free to send a pull request :)
30 |
31 | ### Custom domains
32 |
33 | You can also enable CodeCopy on custom domains or Github Enterprise.
34 | Right click on CodeCopy's icon in the the toolbar and select **Enable CodeCopy on this domain**.
35 |
36 | ## Preview
37 |
38 | 
39 |
40 | ## Setup
41 |
42 | Install dependencies:
43 |
44 | ```
45 | npm install
46 | ```
47 |
48 | Compile scripts and styles:
49 |
50 | ```
51 | npm start
52 | ```
53 |
54 | ## Testing
55 |
56 | ###### Chrome
57 |
58 | 1. Navigate to `chrome://extensions`
59 |
60 | 2. Click on `Load unpacked extension...`
61 |
62 | 3. Select the `dist` folder
63 |
64 | ###### Firefox
65 |
66 | 1. Navigate to `about:debugging`
67 |
68 | 2. Click on `Load temporary Add-on`
69 |
70 | 3. Select the `manifest.json` inside the `dist` folder
71 |
72 | ###### Opera
73 |
74 | 1. Navigate to `extensions`
75 |
76 | 2. Click on `Developer Mode`
77 |
78 | 3. Click on `Load unpacked extension...`
79 |
80 | 4. Select the `dist` folder
81 |
82 | ## License
83 |
84 | [MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha
85 |
--------------------------------------------------------------------------------