├── .gitattributes
├── app
├── scripts.babel
│ ├── .tern-port
│ ├── chromereload.js
│ ├── background.js
│ ├── contentscript.js
│ └── options.js
├── images
│ ├── duckduckgo-16.png
│ ├── duckduckgo-38.png
│ ├── duckduckgo-48.png
│ ├── duckduckgo-64.png
│ └── duckduckgo-128.png
├── _locales
│ └── en
│ │ └── messages.json
├── styles
│ └── main.css
├── manifest.json
└── options.html
├── .babelrc
├── .bowerrc
├── .yo-rc.json
├── screenshots
├── Chrome
│ ├── options.png
│ ├── buttons-bang.png
│ ├── context-menu.png
│ └── buttons-shortname.png
└── Opera
│ ├── options.png
│ ├── context-menu.png
│ └── buttons-short-name.png
├── .tern-project
├── bower.json
├── test
├── spec
│ └── test.js
└── index.html
├── .editorconfig
├── .gitignore
├── LICENSE
├── package.json
├── readme.ORG
└── gulpfile.babel.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/app/scripts.babel/.tern-port:
--------------------------------------------------------------------------------
1 | 41513
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-mocha": {
3 | "ui": "tdd",
4 | "rjs": false
5 | }
6 | }
--------------------------------------------------------------------------------
/app/images/duckduckgo-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/app/images/duckduckgo-16.png
--------------------------------------------------------------------------------
/app/images/duckduckgo-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/app/images/duckduckgo-38.png
--------------------------------------------------------------------------------
/app/images/duckduckgo-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/app/images/duckduckgo-48.png
--------------------------------------------------------------------------------
/app/images/duckduckgo-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/app/images/duckduckgo-64.png
--------------------------------------------------------------------------------
/app/images/duckduckgo-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/app/images/duckduckgo-128.png
--------------------------------------------------------------------------------
/screenshots/Chrome/options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Chrome/options.png
--------------------------------------------------------------------------------
/screenshots/Opera/options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Opera/options.png
--------------------------------------------------------------------------------
/screenshots/Chrome/buttons-bang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Chrome/buttons-bang.png
--------------------------------------------------------------------------------
/screenshots/Chrome/context-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Chrome/context-menu.png
--------------------------------------------------------------------------------
/screenshots/Opera/context-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Opera/context-menu.png
--------------------------------------------------------------------------------
/screenshots/Chrome/buttons-shortname.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Chrome/buttons-shortname.png
--------------------------------------------------------------------------------
/screenshots/Opera/buttons-short-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engwerda/duckduckgo-bangs/HEAD/screenshots/Opera/buttons-short-name.png
--------------------------------------------------------------------------------
/.tern-project:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": {
3 | "es_modules": {}
4 | },
5 | "libs": [
6 | "ecma5",
7 | "ecma6"
8 | ],
9 | "ecmaVersion": 6
10 | }
11 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "duckduckgo-bangs",
3 | "private": true,
4 | "version": "0.0.0",
5 | "dependencies": {},
6 | "devDependencies": {
7 | "chai": "^3.5.0",
8 | "mocha": "^3.2.0"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/spec/test.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | describe('Give it some context', function () {
5 | describe('maybe a bit more context here', function () {
6 | it('should run here few assertions', function () {
7 |
8 | });
9 | });
10 | });
11 | })();
12 |
--------------------------------------------------------------------------------
/app/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "DuckDuckGo Bangs",
4 | "description": "The name of the application"
5 | },
6 | "appDescription": {
7 | "message": "Addin FuckDuckGo !bang buttons to search results and adds DuckDuckGo !bang links to the context menu.",
8 | "description": "The description of the application"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/styles/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 20px;
3 | }
4 | .status {
5 | display: none;
6 | background-color: #dff0d8;
7 | border-color: #d0e9c6;
8 | color: #3c763d;
9 | padding: .30rem 1.25rem;
10 | margin-bottom: 1rem;
11 | border: 1px solid transparent;
12 | border-radius: .25rem;
13 | font-size: 0.9rem;
14 | }
15 |
16 | .italic{
17 | font-style: italic;
18 | }
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [*.json]
15 | indent_size = 2
16 |
17 | # We recommend you to keep these unchanged
18 | end_of_line = lf
19 | charset = utf-8
20 | trim_trailing_whitespace = true
21 | insert_final_newline = true
22 |
23 | [*.md]
24 | trim_trailing_whitespace = false
25 |
--------------------------------------------------------------------------------
/app/scripts.babel/chromereload.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Reload client for Chrome Apps & Extensions.
4 | // The reload client has a compatibility with livereload.
5 | // WARNING: only supports reload command.
6 |
7 | const LIVERELOAD_HOST = 'localhost:';
8 | const LIVERELOAD_PORT = 35729;
9 | const connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');
10 |
11 | connection.onerror = error => {
12 | console.log('reload connection got error:', error);
13 | };
14 |
15 | connection.onmessage = e => {
16 | if (e.data) {
17 | const data = JSON.parse(e.data);
18 | if (data && data.command === 'reload') {
19 | chrome.runtime.reload();
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mocha Spec Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | temp
3 | .tmp
4 | dist
5 | .sass-cache
6 | app/bower_components
7 | test/bower_components
8 | package
9 | app/scripts
10 |
11 |
12 | # Created by https://www.gitignore.io/api/emacs
13 |
14 | ### Emacs ###
15 | # -*- mode: gitignore; -*-
16 | *~
17 | \#*\#
18 | /.emacs.desktop
19 | /.emacs.desktop.lock
20 | *.elc
21 | auto-save-list
22 | tramp
23 | .\#*
24 |
25 | # Org-mode
26 | .org-id-locations
27 | *_archive
28 |
29 | # flymake-mode
30 | *_flymake.*
31 |
32 | # eshell files
33 | /eshell/history
34 | /eshell/lastdir
35 |
36 | # elpa packages
37 | /elpa/
38 |
39 | # reftex files
40 | *.rel
41 |
42 | # AUCTeX auto folder
43 | /auto/
44 |
45 | # cask packages
46 | .cask/
47 | dist/
48 |
49 | # Flycheck
50 | flycheck_*.el
51 |
52 | # server auth directory
53 | /server/
54 |
55 | # projectiles files
56 | .projectile
57 |
58 | # directory configuration
59 | .dir-locals.el
60 |
61 | # End of https://www.gitignore.io/api/emacs
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Simon Engwerda
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 |
--------------------------------------------------------------------------------
/app/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "DuckDuckGo !bangs",
3 | "description": "Add DuckDuckGo bang buttons to search results and search links in the context menu.",
4 | "version": "0.6.1",
5 | "author": "Simon Engwerda",
6 | "manifest_version": 2,
7 | "applications": {
8 | "gecko": {
9 | "id": "duckduckgobangs@mozilla.org"
10 | }
11 | },
12 | "icons": {
13 | "16": "images/duckduckgo-16.png",
14 | "48": "images/duckduckgo-48.png",
15 | "128": "images/duckduckgo-128.png"
16 | },
17 | "default_locale": "en",
18 | "background": {
19 | "scripts": [
20 | "scripts/chromereload.js",
21 | "scripts/background.js"
22 | ]
23 | },
24 | "permissions": [
25 | "tabs",
26 | "contextMenus",
27 | "storage"
28 | ],
29 | "options_ui": {
30 | "page": "options.html",
31 | "chrome_style": false
32 | },
33 | "content_scripts": [{
34 | "matches": [
35 | "*://*.duckduckgo.com/*"
36 | ],
37 | "js": [
38 | "scripts/contentscript.js"
39 | ],
40 | "run_at": "document_end",
41 | "all_frames": false
42 | }],
43 | "omnibox": {
44 | "keyword": "dd"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "duckduckgo-bangs",
3 | "private": true,
4 | "engines": {
5 | "node": ">=0.8.0"
6 | },
7 | "devDependencies": {
8 | "babel-core": "^6.7.2",
9 | "babel-preset-es2015": "^6.6.0",
10 | "del": "^2.2.0",
11 | "gulp": "^3.9.1",
12 | "gulp-babel": "^6.1.2",
13 | "gulp-cache": "^0.4.3",
14 | "gulp-chrome-manifest": "0.0.13",
15 | "gulp-clean-css": "^2.0.3",
16 | "gulp-eslint": "^2.0.0",
17 | "gulp-if": "^2.0.0",
18 | "gulp-imagemin": "^2.4.0",
19 | "gulp-livereload": "^3.8.1",
20 | "gulp-load-plugins": "^1.2.0",
21 | "gulp-htmlmin": "^1.3.0",
22 | "gulp-size": "^2.1.0",
23 | "gulp-sourcemaps": "^1.6.0",
24 | "gulp-uglify": "^1.5.3",
25 | "gulp-useref": "^3.0.8",
26 | "gulp-zip": "^3.2.0",
27 | "main-bower-files": "^2.11.1",
28 | "run-sequence": "^1.1.5",
29 | "wiredep": "^4.0.0"
30 | },
31 | "eslintConfig": {
32 | "env": {
33 | "node": true,
34 | "browser": true,
35 | "es6": true
36 | },
37 | "globals": {
38 | "chrome": true
39 | },
40 | "rules": {
41 | "no-console": 0,
42 | "eol-last": 0,
43 | "quotes": [
44 | 2,
45 | "single"
46 | ]
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/readme.ORG:
--------------------------------------------------------------------------------
1 | * DuckDuckGo !Bangs
2 | A web extension for making it easier to search using [[https://duckduckgo.com/bang][DuckDuckGo !bangs]].
3 | DuckDuckGo is a search engine that does not track you. DuckDuckGo !bangs allow you to search on thousands of sites, directly.
4 | This extension add two functions.
5 | - Add !bang buttons to the DuckDuckGo search results page.
6 | - add context menu to search using [[https://duckduckgo.com/bang][DuckDuckGo !bangs]] for selected text.
7 | Supports 19 !bangs at the moment of over 9000 available.
8 |
9 | ** Supported bangs
10 | - Google !g
11 | - Google Images: !gi
12 | - Google Maps: !gm
13 | - Google Translate: !tr
14 | - Youtube: !yt
15 | - Dictionary.com: !di
16 | - Google News: !gn
17 | - Hacker News: !hn
18 | - Facebook: !fb
19 | - Twitter: !tw
20 | - Wikipedia: !wi
21 | - IMDb: !imd
22 | - Bing: !b
23 | - Github: !git
24 | - StackOverflow: !so
25 | - Reddit: !r
26 | - Amazon: !a
27 | - Piratebay: !piratebay
28 | - Avaxhome: !avax
29 | - RaRBG: !rarbg
30 |
31 | ** Credits
32 | [[https://github.com/ChimeraCoder][Aditya Mukerjee]] I have been using his [[https://github.com/ChimeraCoder/duckduckbang][Firefox addon]] for some time and it gave me the idea for the buttons.
33 | I also used his code as a starting point for adding the buttons to the search result.
34 | [[https://duckduckgo.com/][DuckDuckGo.com]] for icons, !bangs and their awesome search engine.
35 |
36 | ** Possible improvements
37 | - Add custom buttons and links by providing a !bang and title.
38 | - Custom sort order of buttons.
39 | - Custom sort links in context menu.
40 | - Add buttons to other search engines like Google.
41 |
42 | ** Development
43 | Clone the repo and run npm install
44 | #+BEGIN_SRC shell
45 | git clone https://github.com/engwerda/duckduckgo-bangs.git
46 | #+END_SRC
47 | #+BEGIN_SRC shell
48 | npm install
49 | #+END_SRC
50 |
51 |
52 | *** Gulp Tasks
53 | **** Babel
54 | gulp babel should be run before test and run a extension on browser.
55 | #+BEGIN_SRC shell
56 | gulp babel
57 | #+END_SRC
58 |
59 | **** Watch
60 | Watch task helps you reduce your efforts during development extensions.
61 | #+BEGIN_SRC shell
62 | gulp watch
63 | #+END_SRC
64 |
65 | **** Build and Package
66 | It will build the app as a result you can have a distribution version of the app in dist. Run this command to build the browser extension app.
67 | #+BEGIN_SRC shell
68 | gulp build
69 | #+END_SRC
70 | This command will compress your app built by gulp build command.
71 | #+BEGIN_SRC shell
72 | gulp package
73 | #+END_SRC
74 | Or create an xpi file.
75 | #+BEGIN_SRC shell
76 | gulp package-xpi
77 | #+END_SRC
78 |
79 |
80 | For more info: [[https://github.com/yeoman/generator-chrome-extension][Yeoman Chrome Extension generator]]
81 |
82 |
83 | ** License
84 | MIT
85 |
--------------------------------------------------------------------------------
/app/scripts.babel/background.js:
--------------------------------------------------------------------------------
1 | // Load the options page on install.
2 | chrome.runtime.onInstalled.addListener(function() {
3 | chrome.runtime.openOptionsPage();
4 | });
5 |
6 | // Function to add children to a parent Context Menu.
7 | function refreshContextMenu() {
8 | chrome.contextMenus.removeAll();
9 | createMainMenu();
10 | addChildrenToContextMenu();
11 | }
12 |
13 | function addChildToContextMenu(title, bang, parentMenu = 'parentMenu') {
14 | chrome.contextMenus.create({
15 | id: title.replace(' ', '').toLowerCase(),
16 | title: title,
17 | contexts: ['selection'],
18 | 'parentId': parentMenu,
19 | onclick: function(info, tab) {
20 | const queryText = info.selectionText;
21 | chrome.tabs.create({
22 | 'url': 'https://duckduckgo.com/?q=' + queryText + ' ' + bang,
23 | 'index': tab.index + 1,
24 | 'active': true
25 | });
26 | }
27 | });
28 | }
29 |
30 |
31 | //This adds Context Menu when user select some text.
32 | function createMainMenu() {
33 | chrome.contextMenus.create({
34 | id: 'parentMenu',
35 | title: 'Search "%s" on:',
36 | contexts: ['selection']
37 | });
38 | }
39 | createMainMenu();
40 |
41 |
42 | // Add children when set in options.
43 | function addChildrenToContextMenu() {
44 | chrome.storage.local.get(null, function(items) {
45 |
46 | if (items.duckDuckGoLink === true) {
47 | addChildToContextMenu('DuckDuckGo', '');
48 | }
49 | if (items.googleLink === true) {
50 | addChildToContextMenu('Google', '!g');
51 | }
52 | if (items.youtubeLink === true) {
53 | addChildToContextMenu('Youtube', '!yt');
54 | }
55 | if (items.googleImagesLink === true) {
56 | addChildToContextMenu('Google Images', '!gi');
57 | }
58 | if (items.googleMapsLink === true) {
59 | addChildToContextMenu('Google Maps', '!gm');
60 | }
61 | if (items.googleTranslateLink === true) {
62 | addChildToContextMenu('Google Translate', '!tr');
63 | }
64 | if (items.dictionaryLink === true) {
65 | addChildToContextMenu('Dictionary.com', '!di');
66 | }
67 | if (items.googleNewsLink === true) {
68 | addChildToContextMenu('Google News', '!gn');
69 | }
70 | if (items.hackerNewsLink === true) {
71 | addChildToContextMenu('Hacker News', '!hn');
72 | }
73 | if (items.facebookLink === true) {
74 | addChildToContextMenu('Facebook', '!fb');
75 | }
76 | if (items.twitterLink === true) {
77 | addChildToContextMenu('Twitter', '!tw');
78 | }
79 | if (items.wikipediaLink === true) {
80 | addChildToContextMenu('Wikipedia', '!wi');
81 | }
82 | if (items.imdLink === true) {
83 | addChildToContextMenu('IMDb', '!imd');
84 | }
85 | if (items.bingLink === true) {
86 | addChildToContextMenu('Bing', '!b');
87 | }
88 | if (items.githubLink === true) {
89 | addChildToContextMenu('GitHub', '!git');
90 | }
91 | if (items.stackOverflowLink === true) {
92 | addChildToContextMenu('StackOverflow', '!so');
93 | }
94 | if (items.redditLink === true) {
95 | addChildToContextMenu('Reddit', '!r');
96 | }
97 | if (items.amazonLink === true) {
98 | addChildToContextMenu('Amazon', '!a');
99 | }
100 | if (items.piratebayLink === true) {
101 | addChildToContextMenu('Piratebay', '!piratebay');
102 | }
103 | if (items.avaxhomeLink === true) {
104 | addChildToContextMenu('AvaxHome', '!avax');
105 | }
106 | if (items.rarbgLink === true) {
107 | addChildToContextMenu('RaRBG', '!rarbg');
108 | }
109 | });
110 | }
111 | addChildrenToContextMenu();
112 |
113 | chrome.storage.onChanged.addListener(function (){
114 | refreshContextMenu();
115 | });
116 |
117 | chrome.omnibox.onInputEntered.addListener(function(text) {
118 | chrome.tabs.query({
119 | 'currentWindow': true,
120 | 'active': true
121 | }, function(tabs) {
122 | chrome.tabs.update(tabs[0].id, {
123 | url: 'https://duckduckgo.com/?q=' + encodeURIComponent(text)
124 | });
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | // generated on 2017-04-16 using generator-chrome-extension 0.6.1
2 | import gulp from 'gulp';
3 | import gulpLoadPlugins from 'gulp-load-plugins';
4 | import del from 'del';
5 | import runSequence from 'run-sequence';
6 | import {
7 | stream as wiredep
8 | } from 'wiredep';
9 |
10 | const $ = gulpLoadPlugins();
11 |
12 | gulp.task('extras', () => {
13 | return gulp.src([
14 | 'app/*.*',
15 | 'app/_locales/**',
16 | '!app/scripts.babel',
17 | '!app/*.json',
18 | '!app/*.html',
19 | ], {
20 | base: 'app',
21 | dot: true
22 | }).pipe(gulp.dest('dist'));
23 | });
24 |
25 | function lint(files, options) {
26 | return () => {
27 | return gulp.src(files)
28 | .pipe($.eslint(options))
29 | .pipe($.eslint.format());
30 | };
31 | }
32 |
33 | gulp.task('lint', lint('app/scripts.babel/**/*.js', {
34 | env: {
35 | es6: true
36 | }
37 | }));
38 |
39 | gulp.task('images', () => {
40 | return gulp.src('app/images/**/*')
41 | .pipe($.if($.if.isFile, $.cache($.imagemin({
42 | progressive: true,
43 | interlaced: true,
44 | // don't remove IDs from SVGs, they are often used
45 | // as hooks for embedding and styling
46 | svgoPlugins: [{
47 | cleanupIDs: false
48 | }]
49 | }))
50 | .on('error', function(err) {
51 | console.log(err);
52 | this.end();
53 | })))
54 | .pipe(gulp.dest('dist/images'));
55 | });
56 |
57 | gulp.task('html', () => {
58 | return gulp.src('app/*.html')
59 | .pipe($.useref({
60 | searchPath: ['.tmp', 'app', '.']
61 | }))
62 | .pipe($.sourcemaps.init())
63 | .pipe($.if('*.js', $.uglify()))
64 | .pipe($.if('*.css', $.cleanCss({
65 | compatibility: '*'
66 | })))
67 | .pipe($.sourcemaps.write())
68 | .pipe($.if('*.html', $.htmlmin({
69 | removeComments: true,
70 | collapseWhitespace: true
71 | })))
72 | .pipe(gulp.dest('dist'));
73 | });
74 |
75 | gulp.task('chromeManifest', () => {
76 | return gulp.src('app/manifest.json')
77 | .pipe($.chromeManifest({
78 | buildnumber: false,
79 | background: {
80 | target: 'scripts/background.js',
81 | exclude: [
82 | 'scripts/chromereload.js'
83 | ]
84 | }
85 | }))
86 | .pipe($.if('*.css', $.cleanCss({
87 | compatibility: '*'
88 | })))
89 | .pipe($.if('*.js', $.sourcemaps.init()))
90 | .pipe($.if('*.js', $.uglify()))
91 | .pipe($.if('*.js', $.sourcemaps.write('.')))
92 | .pipe(gulp.dest('dist'));
93 | });
94 |
95 | gulp.task('babel', () => {
96 | return gulp.src('app/scripts.babel/**/*.js')
97 | .pipe($.babel({
98 | presets: ['es2015']
99 | }))
100 | .pipe(gulp.dest('app/scripts'));
101 | });
102 |
103 | gulp.task('clean', del.bind(null, ['.tmp', 'dist']));
104 |
105 | gulp.task('watch', ['lint', 'babel'], () => {
106 | $.livereload.listen();
107 |
108 | gulp.watch([
109 | 'app/*.html',
110 | 'app/scripts/**/*.js',
111 | 'app/images/**/*',
112 | 'app/styles/**/*',
113 | 'app/_locales/**/*.json'
114 | ]).on('change', $.livereload.reload);
115 |
116 | gulp.watch('app/scripts.babel/**/*.js', ['lint', 'babel']);
117 | gulp.watch('bower.json', ['wiredep']);
118 | });
119 |
120 | gulp.task('size', () => {
121 | return gulp.src('dist/**/*').pipe($.size({
122 | title: 'build',
123 | gzip: true
124 | }));
125 | });
126 |
127 | gulp.task('wiredep', () => {
128 | gulp.src('app/*.html')
129 | .pipe(wiredep({
130 | ignorePath: /^(\.\.\/)*\.\./
131 | }))
132 | .pipe(gulp.dest('app'));
133 | });
134 |
135 | gulp.task('package', function() {
136 | var manifest = require('./dist/manifest.json');
137 | return gulp.src('dist/**')
138 | .pipe($.zip('duckduckgobangs-' + manifest.version + '.zip'))
139 | .pipe(gulp.dest('package'));
140 | });
141 |
142 | gulp.task('package-xpi', function() {
143 | var manifest = require('./dist/manifest.json');
144 | return gulp.src('dist/**')
145 | .pipe($.zip('duckduckgobangs-' + manifest.version + '.xpi'))
146 | .pipe(gulp.dest('package'));
147 | });
148 |
149 | gulp.task('build', (cb) => {
150 | runSequence(
151 | 'lint', 'babel', 'chromeManifest', ['html', 'images', 'extras'],
152 | 'size', cb);
153 | });
154 |
155 | gulp.task('default', ['clean'], cb => {
156 | runSequence('build', cb);
157 | });
158 |
--------------------------------------------------------------------------------
/app/scripts.babel/contentscript.js:
--------------------------------------------------------------------------------
1 | const searchElement = document.getElementById('search_form_input');
2 | const linksElement = document.getElementById('links');
3 | const styling = '.bang-btn { margin: 10px; }';
4 |
5 | const urlBuilder = (query, bang) => {
6 | return 'https://duckduckgo.com/?q=' + bang + '%20' + query;
7 | };
8 |
9 | // Build the HTML for the button link
10 | const linkBuilder = (query, bang, shortName, fullName) => {
11 | const url = urlBuilder(query, bang);
12 | const a = document.createElement('a');
13 | a.className = 'button bang-btn';
14 | a.href = url;
15 |
16 | // Set button text from option setting
17 | chrome.storage.local.get('buttonText', function(items) {
18 | if (items.buttonText === 'fullName') {
19 | a.textContent = fullName;
20 | } else if (items.buttonText === 'bang') {
21 | a.textContent = bang;
22 | } else {
23 | a.textContent = shortName;
24 | }
25 | });
26 | a.title = 'Search for "' + query + '" on ' + fullName;
27 | // Open link in new tab if set in options.
28 | chrome.storage.local.get('newTab', function(items) {
29 | if (items.newTab === true) {
30 | a.target = '_blank';
31 | }
32 | });
33 | return a;
34 | };
35 |
36 |
37 | const inject = (query) => {
38 | const css = document.createElement('style');
39 | css.type = 'text/css';
40 | css.textContent = styling;
41 | document.body.appendChild(css);
42 |
43 | const containerElement = linksElement.parentElement;
44 | chrome.storage.local.get(null, function(items) {
45 | if (items.googleButton === true) {
46 | containerElement.insertBefore(linkBuilder(query, '!g', 'G', 'Google'), linksElement);
47 | }
48 | if (items.youtubeButton === true) {
49 | containerElement.insertBefore(linkBuilder(query, '!yt', 'YT', 'Youtube'), linksElement);
50 | }
51 | if (items.googleImagesButton === true) {
52 | containerElement.insertBefore(linkBuilder(query, '!gi', 'Img', 'Google Images'), linksElement);
53 | }
54 | if (items.googleMapsButton === true) {
55 | containerElement.insertBefore(linkBuilder(query, '!gm', 'Maps', 'Google Maps'), linksElement);
56 | }
57 | if (items.googleTranslateButton === true) {
58 | containerElement.insertBefore(linkBuilder(query, '!tr', 'Trans', 'Google Translate'), linksElement);
59 | }
60 | if (items.dictionaryButton === true) {
61 | containerElement.insertBefore(linkBuilder(query, '!di', 'Dict', 'Dictionary.com'), linksElement);
62 | }
63 | if (items.googleNewsButton === true) {
64 | containerElement.insertBefore(linkBuilder(query, '!gn', 'News', 'Google News'), linksElement);
65 | }
66 | if (items.wikipediaButton === true) {
67 | containerElement.insertBefore(linkBuilder(query, '!w', 'Wiki', 'Wikipedia'), linksElement);
68 | }
69 | if (items.hackerNewsButton === true) {
70 | containerElement.insertBefore(linkBuilder(query, '!hn', 'Hack', 'Hacker News'), linksElement);
71 | }
72 | if (items.facebookButton === true) {
73 | containerElement.insertBefore(linkBuilder(query, '!fb', 'FB', 'Facebook'), linksElement);
74 | }
75 | if (items.twitterButton === true) {
76 | containerElement.insertBefore(linkBuilder(query, '!tw', 'TW', 'Twitter'), linksElement);
77 | }
78 | if (items.imdButton === true) {
79 | containerElement.insertBefore(linkBuilder(query, '!imd', 'IMD', 'IMDb'), linksElement);
80 | }
81 | if (items.bingButton === true) {
82 | containerElement.insertBefore(linkBuilder(query, '!b', 'B', 'Bing'), linksElement);
83 | }
84 | if (items.githubButton === true) {
85 | containerElement.insertBefore(linkBuilder(query, '!gh', 'GitHub', 'Github'), linksElement);
86 | }
87 | if (items.stackOverflowButton === true) {
88 | containerElement.insertBefore(linkBuilder(query, '!so', 'SO', 'StackOverflow'), linksElement);
89 | }
90 | if (items.redditButton === true) {
91 | containerElement.insertBefore(linkBuilder(query, '!r', 'R', 'Reddit'), linksElement);
92 | }
93 | if (items.amazonButton === true) {
94 | containerElement.insertBefore(linkBuilder(query, '!a', 'A', 'Amazon'), linksElement);
95 | }
96 | if (items.piratebayButton === true) {
97 | containerElement.insertBefore(linkBuilder(query, '!piratebay', 'PB', 'Piratebay'), linksElement);
98 | }
99 | if (items.avaxhomeButton === true) {
100 | containerElement.insertBefore(linkBuilder(query, '!avax', 'Avax', 'AvaxHome'), linksElement);
101 | }
102 | if (items.rarbgButton === true) {
103 | containerElement.insertBefore(linkBuilder(query, '!rarbg', 'RaRBG', 'RaRBG'), linksElement);
104 | }
105 | });
106 |
107 | };
108 |
109 | if (searchElement !== null && linksElement !== null) {
110 | inject(searchElement.value);
111 | }
112 |
--------------------------------------------------------------------------------
/app/scripts.babel/options.js:
--------------------------------------------------------------------------------
1 | function save_options() {
2 |
3 | let newTab = document.getElementById('newTab').checked;
4 | let buttonText = document.getElementById('buttonText').value;
5 |
6 | let googleButton = document.getElementById('googleButton').checked;
7 | let youtubeButton = document.getElementById('youtubeButton').checked;
8 | let googleImagesButton = document.getElementById('googleImagesButton').checked;
9 | let googleMapsButton = document.getElementById('googleMapsButton').checked;
10 | let googleTranslateButton = document.getElementById('googleTranslateButton').checked;
11 | let dictionaryButton = document.getElementById('dictionaryButton').checked;
12 | let googleNewsButton = document.getElementById('googleNewsButton').checked;
13 | let hackerNewsButton = document.getElementById('hackerNewsButton').checked;
14 | let facebookButton = document.getElementById('facebookButton').checked;
15 | let twitterButton = document.getElementById('twitterButton').checked;
16 | let wikipediaButton = document.getElementById('wikipediaButton').checked;
17 | let imdButton = document.getElementById('imdButton').checked;
18 | let bingButton = document.getElementById('bingButton').checked;
19 | let githubButton = document.getElementById('githubButton').checked;
20 | let stackOverflowButton = document.getElementById('stackOverflowButton').checked;
21 | let redditButton = document.getElementById('redditButton').checked;
22 | let amazonButton = document.getElementById('amazonButton').checked;
23 | let piratebayButton = document.getElementById('piratebayButton').checked;
24 | let avaxhomeButton = document.getElementById('avaxhomeButton').checked;
25 | let rarbgButton = document.getElementById('rarbgButton').checked;
26 |
27 | let duckDuckGoLink = document.getElementById('duckDuckGoLink').checked;
28 | let googleLink = document.getElementById('googleLink').checked;
29 | let youtubeLink = document.getElementById('youtubeLink').checked;
30 | let googleImagesLink = document.getElementById('googleImagesLink').checked;
31 | let googleMapsLink = document.getElementById('googleMapsLink').checked;
32 | let googleTranslateLink = document.getElementById('googleTranslateLink').checked;
33 | let dictionaryLink = document.getElementById('dictionaryLink').checked;
34 | let googleNewsLink = document.getElementById('googleNewsLink').checked;
35 | let hackerNewsLink = document.getElementById('hackerNewsLink').checked;
36 | let facebookLink = document.getElementById('facebookLink').checked;
37 | let twitterLink = document.getElementById('twitterLink').checked;
38 | let wikipediaLink = document.getElementById('wikipediaLink').checked;
39 | let imdLink = document.getElementById('imdLink').checked;
40 | let bingLink = document.getElementById('bingLink').checked;
41 | let githubLink = document.getElementById('githubLink').checked;
42 | let stackOverflowLink = document.getElementById('stackOverflowLink').checked;
43 | let redditLink = document.getElementById('redditLink').checked;
44 | let amazonLink = document.getElementById('amazonLink').checked;
45 | let piratebayLink = document.getElementById('piratebayLink').checked;
46 | let avaxhomeLink = document.getElementById('avaxhomeLink').checked;
47 | let rarbgLink = document.getElementById('rarbgLink').checked;
48 |
49 | chrome.storage.local.set({
50 | newTab: newTab,
51 | buttonText: buttonText,
52 |
53 | googleButton: googleButton,
54 | youtubeButton: youtubeButton,
55 | googleImagesButton: googleImagesButton,
56 | googleMapsButton: googleMapsButton,
57 | googleTranslateButton: googleTranslateButton,
58 | dictionaryButton: dictionaryButton,
59 | googleNewsButton: googleNewsButton,
60 | hackerNewsButton: hackerNewsButton,
61 | facebookButton: facebookButton,
62 | twitterButton: twitterButton,
63 | wikipediaButton: wikipediaButton,
64 | imdButton: imdButton,
65 | bingButton: bingButton,
66 | githubButton: githubButton,
67 | stackOverflowButton: stackOverflowButton,
68 | redditButton: redditButton,
69 | amazonButton: amazonButton,
70 | piratebayButton: piratebayButton,
71 | avaxhomeButton: avaxhomeButton,
72 | rarbgButton: rarbgButton,
73 |
74 | duckDuckGoLink: duckDuckGoLink,
75 | googleLink: googleLink,
76 | youtubeLink: youtubeLink,
77 | googleImagesLink: googleImagesLink,
78 | googleMapsLink: googleMapsLink,
79 | googleTranslateLink: googleTranslateLink,
80 | dictionaryLink: dictionaryLink,
81 | googleNewsLink: googleNewsLink,
82 | hackerNewsLink: hackerNewsLink,
83 | facebookLink: facebookLink,
84 | twitterLink: twitterLink,
85 | wikipediaLink: wikipediaLink,
86 | imdLink: imdLink,
87 | bingLink: bingLink,
88 | githubLink: githubLink,
89 | stackOverflowLink: stackOverflowLink,
90 | redditLink: redditLink,
91 | amazonLink: amazonLink,
92 | piratebayLink: piratebayLink,
93 | avaxhomeLink: avaxhomeLink,
94 | rarbgLink: rarbgLink
95 |
96 | }, function() {
97 | // Update status to let user know options were saved.
98 |
99 | let status = document.getElementsByClassName('status');
100 | for (let i = 0; i < status.length; i++) {
101 | status[i].style.display = 'block';
102 | status[i].textContent = 'Options saved.';
103 | }
104 | setTimeout(function() {
105 | for (let i = 0; i < status.length; i++) {
106 | status[i].textContent = '';
107 | status[i].style.display = 'none';
108 | }
109 | }, 1000);
110 | });
111 | }
112 |
113 |
114 | // Restores select box and checkbox state using the preferences
115 | // stored in chrome.storage.
116 | function restore_options() {
117 | // Use default value color = 'red' and likesColor = true.
118 | chrome.storage.local.get({
119 | newTab: false,
120 | buttonText: 'shortName',
121 |
122 | googleButton: true,
123 | youtubeButton: true,
124 | googleImagesButton: true,
125 | googleMapsButton: true,
126 | googleTranslateButton: true,
127 | dictionaryButton: true,
128 | wikipediaButton: true,
129 | googleNewsButton: false,
130 | hackerNewsButton: false,
131 | facebookButton: false,
132 | twitterButton: false,
133 | imdButton: false,
134 | bingButton: false,
135 | githubButton: false,
136 | stackOverflowButton: false,
137 | redditButton: false,
138 | amazonButton: false,
139 | piratebayButton: false,
140 | avaxhomeButton: false,
141 | rarbgButton: false,
142 |
143 | duckDuckGoLink: false,
144 | googleLink: true,
145 | youtubeLink: true,
146 | googleImagesLink: true,
147 | googleMapsLink: true,
148 | googleTranslateLink: true,
149 | dictionaryLink: true,
150 | googleNewsLink: true,
151 | wikipediaLink: true,
152 | hackerNewsLink: false,
153 | facebookLink: false,
154 | twitterLink: false,
155 | imdLink: false,
156 | bingLink: false,
157 | githubLink: false,
158 | stackOverflowLink: false,
159 | redditLink: false,
160 | amazonLink: false,
161 | piratebayLink: false,
162 | avaxhomeLink: false,
163 | rarbgLink: false
164 |
165 |
166 | }, function(items) {
167 | document.getElementById('newTab').checked = items.newTab;
168 | document.getElementById('buttonText').value = items.buttonText;
169 |
170 | document.getElementById('googleButton').checked = items.googleButton;
171 | document.getElementById('youtubeButton').checked = items.youtubeButton;
172 | document.getElementById('googleImagesButton').checked = items.googleImagesButton;
173 | document.getElementById('googleMapsButton').checked = items.googleMapsButton;
174 | document.getElementById('googleTranslateButton').checked = items.googleTranslateButton;
175 | document.getElementById('dictionaryButton').checked = items.dictionaryButton;
176 | document.getElementById('googleNewsButton').checked = items.googleNewsButton;
177 | document.getElementById('hackerNewsButton').checked = items.hackerNewsButton;
178 | document.getElementById('facebookButton').checked = items.facebookButton;
179 | document.getElementById('twitterButton').checked = items.twitterButton;
180 | document.getElementById('wikipediaButton').checked = items.wikipediaButton;
181 | document.getElementById('imdButton').checked = items.imdButton;
182 | document.getElementById('bingButton').checked = items.bingButton;
183 | document.getElementById('githubButton').checked = items.githubButton;
184 | document.getElementById('stackOverflowButton').checked = items.stackOverflowButton;
185 | document.getElementById('redditButton').checked = items.redditButton;
186 | document.getElementById('amazonButton').checked = items.amazonButton;
187 | document.getElementById('piratebayButton').checked = items.piratebayButton;
188 | document.getElementById('avaxhomeButton').checked = items.avaxhomeButton;
189 | document.getElementById('rarbgButton').checked = items.rarbgButton;
190 |
191 | document.getElementById('duckDuckGoLink').checked = items.duckDuckGoLink;
192 | document.getElementById('googleLink').checked = items.googleLink;
193 | document.getElementById('youtubeLink').checked = items.youtubeLink;
194 | document.getElementById('googleImagesLink').checked = items.googleImagesLink;
195 | document.getElementById('googleMapsLink').checked = items.googleMapsLink;
196 | document.getElementById('googleTranslateLink').checked = items.googleTranslateLink;
197 | document.getElementById('dictionaryLink').checked = items.dictionaryLink;
198 | document.getElementById('googleNewsLink').checked = items.googleNewsLink;
199 | document.getElementById('hackerNewsLink').checked = items.hackerNewsLink;
200 | document.getElementById('facebookLink').checked = items.facebookLink;
201 | document.getElementById('twitterLink').checked = items.twitterLink;
202 | document.getElementById('wikipediaLink').checked = items.wikipediaLink;
203 | document.getElementById('imdLink').checked = items.imdLink;
204 | document.getElementById('bingLink').checked = items.bingLink;
205 | document.getElementById('githubLink').checked = items.githubLink;
206 | document.getElementById('stackOverflowLink').checked = items.stackOverflowLink;
207 | document.getElementById('redditLink').checked = items.redditLink;
208 | document.getElementById('amazonLink').checked = items.amazonLink;
209 | document.getElementById('piratebayLink').checked = items.piratebayLink;
210 | document.getElementById('avaxhomeLink').checked = items.avaxhomeLink;
211 | document.getElementById('rarbgLink').checked = items.rarbgLink;
212 |
213 | });
214 | }
215 | document.addEventListener('DOMContentLoaded', restore_options);
216 | // document.getElementsByClassName('save').addEventListener('click',
217 | // save_options);
218 |
219 | let save = document.getElementsByClassName('save');
220 | for (let i = 0; i < save.length; i++) {
221 | save[i].addEventListener('click', save_options);
222 | }
223 |
--------------------------------------------------------------------------------
/app/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | My Test Extension Options
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
Options
22 |
23 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------