├── .gitignore ├── README.md ├── lib └── browser-polyfill.js ├── magnify.png ├── manifest.json ├── popup.css ├── popup.html ├── popup.js ├── presets.json └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .web-extension-id 2 | web-ext-artifacts -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OD-Search 2 | > Find files on the Internet. 3 | 4 | OD-Search builds search queries, to find files on the Internet. Just select a preset (eg. videos, documents, audiofiles, ...) enter a searchterm and go. 5 | 6 | ![Screenshot](https://raw.githubusercontent.com/kevgk/OD-Search/master/screenshot.png) 7 | 8 | ## Installation 9 | Firefox: 10 | [OD-Search on Firefox Addons](https://addons.mozilla.org/de/firefox/addon/od-search/) 11 | 12 | ## About The Project 13 | Why OD-Search and not one of the many websites: 14 | * No middleman / 3rd Parties like CDNs 15 | * Not depending on someone elses server 16 | * No analytics / ads / logging 17 | * Convinience 18 | * Extendable (soon) 19 | 20 | ## Getting Started 21 | After you have installed the addon, you should see a search icon in your browser addons. Click on the search icon to open the search window. 22 | 23 | Now all you have to do is enter your search term, select a preset and click search. 24 | 25 | You can also copy the search query to your clipboard, if you don't want to search directly, just click on "copy query". 26 | 27 | The default search engine is Google, but you can customize it by clicking on the search engine menu. Currently we support Google, Startpage, DuckDuckGo and SearX. 28 | 29 | If you only want to get results for a certain timeframe, you can select it from the Timemenu. (We currently only support this feature on Google) 30 | 31 | You can also filter urls and words from the results by entering the urls/words in the corresponding input field. Multiple Urls/Words must be separated by a comma. 32 | 33 | ## How does it work 34 | All this addon does is combine search parameters. 35 | 36 | Here for we have developed a preset system that makes it easy to create new presets and extend old ones. 37 | 38 | For example, we have a preset for video formats and a preset for searches in Google Drive, now we combine them and have a Google Drive Video preset. 39 | 40 | We also have a preset that filters results from pages that intentionally deceive search results. Like a blacklist. 41 | 42 | Now we can include the blacklist in our presets and improve our search results. 43 | 44 | ## Presets 45 | Currently, presets can only be added/adjusted by the developer through updates. 46 | 47 | But this will change soon, we are working on a preset manager that allows you to easily add new presets. 48 | 49 | We also hope that the community will share their presets so that everyone can benefit. 50 | 51 | ## Contributing 52 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 53 | 54 | 1. Fork the Project 55 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 56 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 57 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 58 | 5. Open a Pull Request 59 | 60 | ## Contact 61 | If you have a problem, suggestions, wishes or feedback, please open a pull-request. 62 | 63 | ## Disclaimer 64 | We do not support illegal activities. We only help to make information more easily accessible on the Internet. 65 | 66 | ## Acknowledgements 67 | * [Lumpysoft](https://github.com/level42ca/lumpysoft) 68 | * [opendirectory-finder](https://github.com/ewasion/opendirectory-finder) -------------------------------------------------------------------------------- /lib/browser-polyfill.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define("webextension-polyfill", ["module"], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(module); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod); 11 | global.browser = mod.exports; 12 | } 13 | })(this, function (module) { 14 | /* webextension-polyfill - v0.4.0 - Wed Feb 06 2019 11:58:31 */ 15 | /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ 16 | /* vim: set sts=2 sw=2 et tw=80: */ 17 | /* This Source Code Form is subject to the terms of the Mozilla Public 18 | * License, v. 2.0. If a copy of the MPL was not distributed with this 19 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 20 | "use strict"; 21 | 22 | if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) { 23 | const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; 24 | const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)"; 25 | 26 | // Wrapping the bulk of this polyfill in a one-time-use function is a minor 27 | // optimization for Firefox. Since Spidermonkey does not fully parse the 28 | // contents of a function until the first time it's called, and since it will 29 | // never actually need to be called, this allows the polyfill to be included 30 | // in Firefox nearly for free. 31 | const wrapAPIs = extensionAPIs => { 32 | // NOTE: apiMetadata is associated to the content of the api-metadata.json file 33 | // at build time by replacing the following "include" with the content of the 34 | // JSON file. 35 | const apiMetadata = { 36 | "alarms": { 37 | "clear": { 38 | "minArgs": 0, 39 | "maxArgs": 1 40 | }, 41 | "clearAll": { 42 | "minArgs": 0, 43 | "maxArgs": 0 44 | }, 45 | "get": { 46 | "minArgs": 0, 47 | "maxArgs": 1 48 | }, 49 | "getAll": { 50 | "minArgs": 0, 51 | "maxArgs": 0 52 | } 53 | }, 54 | "bookmarks": { 55 | "create": { 56 | "minArgs": 1, 57 | "maxArgs": 1 58 | }, 59 | "get": { 60 | "minArgs": 1, 61 | "maxArgs": 1 62 | }, 63 | "getChildren": { 64 | "minArgs": 1, 65 | "maxArgs": 1 66 | }, 67 | "getRecent": { 68 | "minArgs": 1, 69 | "maxArgs": 1 70 | }, 71 | "getSubTree": { 72 | "minArgs": 1, 73 | "maxArgs": 1 74 | }, 75 | "getTree": { 76 | "minArgs": 0, 77 | "maxArgs": 0 78 | }, 79 | "move": { 80 | "minArgs": 2, 81 | "maxArgs": 2 82 | }, 83 | "remove": { 84 | "minArgs": 1, 85 | "maxArgs": 1 86 | }, 87 | "removeTree": { 88 | "minArgs": 1, 89 | "maxArgs": 1 90 | }, 91 | "search": { 92 | "minArgs": 1, 93 | "maxArgs": 1 94 | }, 95 | "update": { 96 | "minArgs": 2, 97 | "maxArgs": 2 98 | } 99 | }, 100 | "browserAction": { 101 | "disable": { 102 | "minArgs": 0, 103 | "maxArgs": 1, 104 | "fallbackToNoCallback": true 105 | }, 106 | "enable": { 107 | "minArgs": 0, 108 | "maxArgs": 1, 109 | "fallbackToNoCallback": true 110 | }, 111 | "getBadgeBackgroundColor": { 112 | "minArgs": 1, 113 | "maxArgs": 1 114 | }, 115 | "getBadgeText": { 116 | "minArgs": 1, 117 | "maxArgs": 1 118 | }, 119 | "getPopup": { 120 | "minArgs": 1, 121 | "maxArgs": 1 122 | }, 123 | "getTitle": { 124 | "minArgs": 1, 125 | "maxArgs": 1 126 | }, 127 | "openPopup": { 128 | "minArgs": 0, 129 | "maxArgs": 0 130 | }, 131 | "setBadgeBackgroundColor": { 132 | "minArgs": 1, 133 | "maxArgs": 1, 134 | "fallbackToNoCallback": true 135 | }, 136 | "setBadgeText": { 137 | "minArgs": 1, 138 | "maxArgs": 1, 139 | "fallbackToNoCallback": true 140 | }, 141 | "setIcon": { 142 | "minArgs": 1, 143 | "maxArgs": 1 144 | }, 145 | "setPopup": { 146 | "minArgs": 1, 147 | "maxArgs": 1, 148 | "fallbackToNoCallback": true 149 | }, 150 | "setTitle": { 151 | "minArgs": 1, 152 | "maxArgs": 1, 153 | "fallbackToNoCallback": true 154 | } 155 | }, 156 | "browsingData": { 157 | "remove": { 158 | "minArgs": 2, 159 | "maxArgs": 2 160 | }, 161 | "removeCache": { 162 | "minArgs": 1, 163 | "maxArgs": 1 164 | }, 165 | "removeCookies": { 166 | "minArgs": 1, 167 | "maxArgs": 1 168 | }, 169 | "removeDownloads": { 170 | "minArgs": 1, 171 | "maxArgs": 1 172 | }, 173 | "removeFormData": { 174 | "minArgs": 1, 175 | "maxArgs": 1 176 | }, 177 | "removeHistory": { 178 | "minArgs": 1, 179 | "maxArgs": 1 180 | }, 181 | "removeLocalStorage": { 182 | "minArgs": 1, 183 | "maxArgs": 1 184 | }, 185 | "removePasswords": { 186 | "minArgs": 1, 187 | "maxArgs": 1 188 | }, 189 | "removePluginData": { 190 | "minArgs": 1, 191 | "maxArgs": 1 192 | }, 193 | "settings": { 194 | "minArgs": 0, 195 | "maxArgs": 0 196 | } 197 | }, 198 | "commands": { 199 | "getAll": { 200 | "minArgs": 0, 201 | "maxArgs": 0 202 | } 203 | }, 204 | "contextMenus": { 205 | "remove": { 206 | "minArgs": 1, 207 | "maxArgs": 1 208 | }, 209 | "removeAll": { 210 | "minArgs": 0, 211 | "maxArgs": 0 212 | }, 213 | "update": { 214 | "minArgs": 2, 215 | "maxArgs": 2 216 | } 217 | }, 218 | "cookies": { 219 | "get": { 220 | "minArgs": 1, 221 | "maxArgs": 1 222 | }, 223 | "getAll": { 224 | "minArgs": 1, 225 | "maxArgs": 1 226 | }, 227 | "getAllCookieStores": { 228 | "minArgs": 0, 229 | "maxArgs": 0 230 | }, 231 | "remove": { 232 | "minArgs": 1, 233 | "maxArgs": 1 234 | }, 235 | "set": { 236 | "minArgs": 1, 237 | "maxArgs": 1 238 | } 239 | }, 240 | "devtools": { 241 | "inspectedWindow": { 242 | "eval": { 243 | "minArgs": 1, 244 | "maxArgs": 2, 245 | "singleCallbackArg": false 246 | } 247 | }, 248 | "panels": { 249 | "create": { 250 | "minArgs": 3, 251 | "maxArgs": 3, 252 | "singleCallbackArg": true 253 | } 254 | } 255 | }, 256 | "downloads": { 257 | "cancel": { 258 | "minArgs": 1, 259 | "maxArgs": 1 260 | }, 261 | "download": { 262 | "minArgs": 1, 263 | "maxArgs": 1 264 | }, 265 | "erase": { 266 | "minArgs": 1, 267 | "maxArgs": 1 268 | }, 269 | "getFileIcon": { 270 | "minArgs": 1, 271 | "maxArgs": 2 272 | }, 273 | "open": { 274 | "minArgs": 1, 275 | "maxArgs": 1, 276 | "fallbackToNoCallback": true 277 | }, 278 | "pause": { 279 | "minArgs": 1, 280 | "maxArgs": 1 281 | }, 282 | "removeFile": { 283 | "minArgs": 1, 284 | "maxArgs": 1 285 | }, 286 | "resume": { 287 | "minArgs": 1, 288 | "maxArgs": 1 289 | }, 290 | "search": { 291 | "minArgs": 1, 292 | "maxArgs": 1 293 | }, 294 | "show": { 295 | "minArgs": 1, 296 | "maxArgs": 1, 297 | "fallbackToNoCallback": true 298 | } 299 | }, 300 | "extension": { 301 | "isAllowedFileSchemeAccess": { 302 | "minArgs": 0, 303 | "maxArgs": 0 304 | }, 305 | "isAllowedIncognitoAccess": { 306 | "minArgs": 0, 307 | "maxArgs": 0 308 | } 309 | }, 310 | "history": { 311 | "addUrl": { 312 | "minArgs": 1, 313 | "maxArgs": 1 314 | }, 315 | "deleteAll": { 316 | "minArgs": 0, 317 | "maxArgs": 0 318 | }, 319 | "deleteRange": { 320 | "minArgs": 1, 321 | "maxArgs": 1 322 | }, 323 | "deleteUrl": { 324 | "minArgs": 1, 325 | "maxArgs": 1 326 | }, 327 | "getVisits": { 328 | "minArgs": 1, 329 | "maxArgs": 1 330 | }, 331 | "search": { 332 | "minArgs": 1, 333 | "maxArgs": 1 334 | } 335 | }, 336 | "i18n": { 337 | "detectLanguage": { 338 | "minArgs": 1, 339 | "maxArgs": 1 340 | }, 341 | "getAcceptLanguages": { 342 | "minArgs": 0, 343 | "maxArgs": 0 344 | } 345 | }, 346 | "identity": { 347 | "launchWebAuthFlow": { 348 | "minArgs": 1, 349 | "maxArgs": 1 350 | } 351 | }, 352 | "idle": { 353 | "queryState": { 354 | "minArgs": 1, 355 | "maxArgs": 1 356 | } 357 | }, 358 | "management": { 359 | "get": { 360 | "minArgs": 1, 361 | "maxArgs": 1 362 | }, 363 | "getAll": { 364 | "minArgs": 0, 365 | "maxArgs": 0 366 | }, 367 | "getSelf": { 368 | "minArgs": 0, 369 | "maxArgs": 0 370 | }, 371 | "setEnabled": { 372 | "minArgs": 2, 373 | "maxArgs": 2 374 | }, 375 | "uninstallSelf": { 376 | "minArgs": 0, 377 | "maxArgs": 1 378 | } 379 | }, 380 | "notifications": { 381 | "clear": { 382 | "minArgs": 1, 383 | "maxArgs": 1 384 | }, 385 | "create": { 386 | "minArgs": 1, 387 | "maxArgs": 2 388 | }, 389 | "getAll": { 390 | "minArgs": 0, 391 | "maxArgs": 0 392 | }, 393 | "getPermissionLevel": { 394 | "minArgs": 0, 395 | "maxArgs": 0 396 | }, 397 | "update": { 398 | "minArgs": 2, 399 | "maxArgs": 2 400 | } 401 | }, 402 | "pageAction": { 403 | "getPopup": { 404 | "minArgs": 1, 405 | "maxArgs": 1 406 | }, 407 | "getTitle": { 408 | "minArgs": 1, 409 | "maxArgs": 1 410 | }, 411 | "hide": { 412 | "minArgs": 1, 413 | "maxArgs": 1, 414 | "fallbackToNoCallback": true 415 | }, 416 | "setIcon": { 417 | "minArgs": 1, 418 | "maxArgs": 1 419 | }, 420 | "setPopup": { 421 | "minArgs": 1, 422 | "maxArgs": 1, 423 | "fallbackToNoCallback": true 424 | }, 425 | "setTitle": { 426 | "minArgs": 1, 427 | "maxArgs": 1, 428 | "fallbackToNoCallback": true 429 | }, 430 | "show": { 431 | "minArgs": 1, 432 | "maxArgs": 1, 433 | "fallbackToNoCallback": true 434 | } 435 | }, 436 | "permissions": { 437 | "contains": { 438 | "minArgs": 1, 439 | "maxArgs": 1 440 | }, 441 | "getAll": { 442 | "minArgs": 0, 443 | "maxArgs": 0 444 | }, 445 | "remove": { 446 | "minArgs": 1, 447 | "maxArgs": 1 448 | }, 449 | "request": { 450 | "minArgs": 1, 451 | "maxArgs": 1 452 | } 453 | }, 454 | "runtime": { 455 | "getBackgroundPage": { 456 | "minArgs": 0, 457 | "maxArgs": 0 458 | }, 459 | "getBrowserInfo": { 460 | "minArgs": 0, 461 | "maxArgs": 0 462 | }, 463 | "getPlatformInfo": { 464 | "minArgs": 0, 465 | "maxArgs": 0 466 | }, 467 | "openOptionsPage": { 468 | "minArgs": 0, 469 | "maxArgs": 0 470 | }, 471 | "requestUpdateCheck": { 472 | "minArgs": 0, 473 | "maxArgs": 0 474 | }, 475 | "sendMessage": { 476 | "minArgs": 1, 477 | "maxArgs": 3 478 | }, 479 | "sendNativeMessage": { 480 | "minArgs": 2, 481 | "maxArgs": 2 482 | }, 483 | "setUninstallURL": { 484 | "minArgs": 1, 485 | "maxArgs": 1 486 | } 487 | }, 488 | "sessions": { 489 | "getDevices": { 490 | "minArgs": 0, 491 | "maxArgs": 1 492 | }, 493 | "getRecentlyClosed": { 494 | "minArgs": 0, 495 | "maxArgs": 1 496 | }, 497 | "restore": { 498 | "minArgs": 0, 499 | "maxArgs": 1 500 | } 501 | }, 502 | "storage": { 503 | "local": { 504 | "clear": { 505 | "minArgs": 0, 506 | "maxArgs": 0 507 | }, 508 | "get": { 509 | "minArgs": 0, 510 | "maxArgs": 1 511 | }, 512 | "getBytesInUse": { 513 | "minArgs": 0, 514 | "maxArgs": 1 515 | }, 516 | "remove": { 517 | "minArgs": 1, 518 | "maxArgs": 1 519 | }, 520 | "set": { 521 | "minArgs": 1, 522 | "maxArgs": 1 523 | } 524 | }, 525 | "managed": { 526 | "get": { 527 | "minArgs": 0, 528 | "maxArgs": 1 529 | }, 530 | "getBytesInUse": { 531 | "minArgs": 0, 532 | "maxArgs": 1 533 | } 534 | }, 535 | "sync": { 536 | "clear": { 537 | "minArgs": 0, 538 | "maxArgs": 0 539 | }, 540 | "get": { 541 | "minArgs": 0, 542 | "maxArgs": 1 543 | }, 544 | "getBytesInUse": { 545 | "minArgs": 0, 546 | "maxArgs": 1 547 | }, 548 | "remove": { 549 | "minArgs": 1, 550 | "maxArgs": 1 551 | }, 552 | "set": { 553 | "minArgs": 1, 554 | "maxArgs": 1 555 | } 556 | } 557 | }, 558 | "tabs": { 559 | "captureVisibleTab": { 560 | "minArgs": 0, 561 | "maxArgs": 2 562 | }, 563 | "create": { 564 | "minArgs": 1, 565 | "maxArgs": 1 566 | }, 567 | "detectLanguage": { 568 | "minArgs": 0, 569 | "maxArgs": 1 570 | }, 571 | "discard": { 572 | "minArgs": 0, 573 | "maxArgs": 1 574 | }, 575 | "duplicate": { 576 | "minArgs": 1, 577 | "maxArgs": 1 578 | }, 579 | "executeScript": { 580 | "minArgs": 1, 581 | "maxArgs": 2 582 | }, 583 | "get": { 584 | "minArgs": 1, 585 | "maxArgs": 1 586 | }, 587 | "getCurrent": { 588 | "minArgs": 0, 589 | "maxArgs": 0 590 | }, 591 | "getZoom": { 592 | "minArgs": 0, 593 | "maxArgs": 1 594 | }, 595 | "getZoomSettings": { 596 | "minArgs": 0, 597 | "maxArgs": 1 598 | }, 599 | "highlight": { 600 | "minArgs": 1, 601 | "maxArgs": 1 602 | }, 603 | "insertCSS": { 604 | "minArgs": 1, 605 | "maxArgs": 2 606 | }, 607 | "move": { 608 | "minArgs": 2, 609 | "maxArgs": 2 610 | }, 611 | "query": { 612 | "minArgs": 1, 613 | "maxArgs": 1 614 | }, 615 | "reload": { 616 | "minArgs": 0, 617 | "maxArgs": 2 618 | }, 619 | "remove": { 620 | "minArgs": 1, 621 | "maxArgs": 1 622 | }, 623 | "removeCSS": { 624 | "minArgs": 1, 625 | "maxArgs": 2 626 | }, 627 | "sendMessage": { 628 | "minArgs": 2, 629 | "maxArgs": 3 630 | }, 631 | "setZoom": { 632 | "minArgs": 1, 633 | "maxArgs": 2 634 | }, 635 | "setZoomSettings": { 636 | "minArgs": 1, 637 | "maxArgs": 2 638 | }, 639 | "update": { 640 | "minArgs": 1, 641 | "maxArgs": 2 642 | } 643 | }, 644 | "topSites": { 645 | "get": { 646 | "minArgs": 0, 647 | "maxArgs": 0 648 | } 649 | }, 650 | "webNavigation": { 651 | "getAllFrames": { 652 | "minArgs": 1, 653 | "maxArgs": 1 654 | }, 655 | "getFrame": { 656 | "minArgs": 1, 657 | "maxArgs": 1 658 | } 659 | }, 660 | "webRequest": { 661 | "handlerBehaviorChanged": { 662 | "minArgs": 0, 663 | "maxArgs": 0 664 | } 665 | }, 666 | "windows": { 667 | "create": { 668 | "minArgs": 0, 669 | "maxArgs": 1 670 | }, 671 | "get": { 672 | "minArgs": 1, 673 | "maxArgs": 2 674 | }, 675 | "getAll": { 676 | "minArgs": 0, 677 | "maxArgs": 1 678 | }, 679 | "getCurrent": { 680 | "minArgs": 0, 681 | "maxArgs": 1 682 | }, 683 | "getLastFocused": { 684 | "minArgs": 0, 685 | "maxArgs": 1 686 | }, 687 | "remove": { 688 | "minArgs": 1, 689 | "maxArgs": 1 690 | }, 691 | "update": { 692 | "minArgs": 2, 693 | "maxArgs": 2 694 | } 695 | } 696 | }; 697 | 698 | if (Object.keys(apiMetadata).length === 0) { 699 | throw new Error("api-metadata.json has not been included in browser-polyfill"); 700 | } 701 | 702 | /** 703 | * A WeakMap subclass which creates and stores a value for any key which does 704 | * not exist when accessed, but behaves exactly as an ordinary WeakMap 705 | * otherwise. 706 | * 707 | * @param {function} createItem 708 | * A function which will be called in order to create the value for any 709 | * key which does not exist, the first time it is accessed. The 710 | * function receives, as its only argument, the key being created. 711 | */ 712 | class DefaultWeakMap extends WeakMap { 713 | constructor(createItem, items = undefined) { 714 | super(items); 715 | this.createItem = createItem; 716 | } 717 | 718 | get(key) { 719 | if (!this.has(key)) { 720 | this.set(key, this.createItem(key)); 721 | } 722 | 723 | return super.get(key); 724 | } 725 | } 726 | 727 | /** 728 | * Returns true if the given object is an object with a `then` method, and can 729 | * therefore be assumed to behave as a Promise. 730 | * 731 | * @param {*} value The value to test. 732 | * @returns {boolean} True if the value is thenable. 733 | */ 734 | const isThenable = value => { 735 | return value && typeof value === "object" && typeof value.then === "function"; 736 | }; 737 | 738 | /** 739 | * Creates and returns a function which, when called, will resolve or reject 740 | * the given promise based on how it is called: 741 | * 742 | * - If, when called, `chrome.runtime.lastError` contains a non-null object, 743 | * the promise is rejected with that value. 744 | * - If the function is called with exactly one argument, the promise is 745 | * resolved to that value. 746 | * - Otherwise, the promise is resolved to an array containing all of the 747 | * function's arguments. 748 | * 749 | * @param {object} promise 750 | * An object containing the resolution and rejection functions of a 751 | * promise. 752 | * @param {function} promise.resolve 753 | * The promise's resolution function. 754 | * @param {function} promise.rejection 755 | * The promise's rejection function. 756 | * @param {object} metadata 757 | * Metadata about the wrapped method which has created the callback. 758 | * @param {integer} metadata.maxResolvedArgs 759 | * The maximum number of arguments which may be passed to the 760 | * callback created by the wrapped async function. 761 | * 762 | * @returns {function} 763 | * The generated callback function. 764 | */ 765 | const makeCallback = (promise, metadata) => { 766 | return (...callbackArgs) => { 767 | if (extensionAPIs.runtime.lastError) { 768 | promise.reject(extensionAPIs.runtime.lastError); 769 | } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) { 770 | promise.resolve(callbackArgs[0]); 771 | } else { 772 | promise.resolve(callbackArgs); 773 | } 774 | }; 775 | }; 776 | 777 | const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments"; 778 | 779 | /** 780 | * Creates a wrapper function for a method with the given name and metadata. 781 | * 782 | * @param {string} name 783 | * The name of the method which is being wrapped. 784 | * @param {object} metadata 785 | * Metadata about the method being wrapped. 786 | * @param {integer} metadata.minArgs 787 | * The minimum number of arguments which must be passed to the 788 | * function. If called with fewer than this number of arguments, the 789 | * wrapper will raise an exception. 790 | * @param {integer} metadata.maxArgs 791 | * The maximum number of arguments which may be passed to the 792 | * function. If called with more than this number of arguments, the 793 | * wrapper will raise an exception. 794 | * @param {integer} metadata.maxResolvedArgs 795 | * The maximum number of arguments which may be passed to the 796 | * callback created by the wrapped async function. 797 | * 798 | * @returns {function(object, ...*)} 799 | * The generated wrapper function. 800 | */ 801 | const wrapAsyncFunction = (name, metadata) => { 802 | return function asyncFunctionWrapper(target, ...args) { 803 | if (args.length < metadata.minArgs) { 804 | throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); 805 | } 806 | 807 | if (args.length > metadata.maxArgs) { 808 | throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); 809 | } 810 | 811 | return new Promise((resolve, reject) => { 812 | if (metadata.fallbackToNoCallback) { 813 | // This API method has currently no callback on Chrome, but it return a promise on Firefox, 814 | // and so the polyfill will try to call it with a callback first, and it will fallback 815 | // to not passing the callback if the first call fails. 816 | try { 817 | target[name](...args, makeCallback({ resolve, reject }, metadata)); 818 | } catch (cbError) { 819 | console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError); 820 | 821 | target[name](...args); 822 | 823 | // Update the API method metadata, so that the next API calls will not try to 824 | // use the unsupported callback anymore. 825 | metadata.fallbackToNoCallback = false; 826 | metadata.noCallback = true; 827 | 828 | resolve(); 829 | } 830 | } else if (metadata.noCallback) { 831 | target[name](...args); 832 | resolve(); 833 | } else { 834 | target[name](...args, makeCallback({ resolve, reject }, metadata)); 835 | } 836 | }); 837 | }; 838 | }; 839 | 840 | /** 841 | * Wraps an existing method of the target object, so that calls to it are 842 | * intercepted by the given wrapper function. The wrapper function receives, 843 | * as its first argument, the original `target` object, followed by each of 844 | * the arguments passed to the original method. 845 | * 846 | * @param {object} target 847 | * The original target object that the wrapped method belongs to. 848 | * @param {function} method 849 | * The method being wrapped. This is used as the target of the Proxy 850 | * object which is created to wrap the method. 851 | * @param {function} wrapper 852 | * The wrapper function which is called in place of a direct invocation 853 | * of the wrapped method. 854 | * 855 | * @returns {Proxy} 856 | * A Proxy object for the given method, which invokes the given wrapper 857 | * method in its place. 858 | */ 859 | const wrapMethod = (target, method, wrapper) => { 860 | return new Proxy(method, { 861 | apply(targetMethod, thisObj, args) { 862 | return wrapper.call(thisObj, target, ...args); 863 | } 864 | }); 865 | }; 866 | 867 | let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); 868 | 869 | /** 870 | * Wraps an object in a Proxy which intercepts and wraps certain methods 871 | * based on the given `wrappers` and `metadata` objects. 872 | * 873 | * @param {object} target 874 | * The target object to wrap. 875 | * 876 | * @param {object} [wrappers = {}] 877 | * An object tree containing wrapper functions for special cases. Any 878 | * function present in this object tree is called in place of the 879 | * method in the same location in the `target` object tree. These 880 | * wrapper methods are invoked as described in {@see wrapMethod}. 881 | * 882 | * @param {object} [metadata = {}] 883 | * An object tree containing metadata used to automatically generate 884 | * Promise-based wrapper functions for asynchronous. Any function in 885 | * the `target` object tree which has a corresponding metadata object 886 | * in the same location in the `metadata` tree is replaced with an 887 | * automatically-generated wrapper function, as described in 888 | * {@see wrapAsyncFunction} 889 | * 890 | * @returns {Proxy} 891 | */ 892 | const wrapObject = (target, wrappers = {}, metadata = {}) => { 893 | let cache = Object.create(null); 894 | let handlers = { 895 | has(proxyTarget, prop) { 896 | return prop in target || prop in cache; 897 | }, 898 | 899 | get(proxyTarget, prop, receiver) { 900 | if (prop in cache) { 901 | return cache[prop]; 902 | } 903 | 904 | if (!(prop in target)) { 905 | return undefined; 906 | } 907 | 908 | let value = target[prop]; 909 | 910 | if (typeof value === "function") { 911 | // This is a method on the underlying object. Check if we need to do 912 | // any wrapping. 913 | 914 | if (typeof wrappers[prop] === "function") { 915 | // We have a special-case wrapper for this method. 916 | value = wrapMethod(target, target[prop], wrappers[prop]); 917 | } else if (hasOwnProperty(metadata, prop)) { 918 | // This is an async method that we have metadata for. Create a 919 | // Promise wrapper for it. 920 | let wrapper = wrapAsyncFunction(prop, metadata[prop]); 921 | value = wrapMethod(target, target[prop], wrapper); 922 | } else { 923 | // This is a method that we don't know or care about. Return the 924 | // original method, bound to the underlying object. 925 | value = value.bind(target); 926 | } 927 | } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) { 928 | // This is an object that we need to do some wrapping for the children 929 | // of. Create a sub-object wrapper for it with the appropriate child 930 | // metadata. 931 | value = wrapObject(value, wrappers[prop], metadata[prop]); 932 | } else { 933 | // We don't need to do any wrapping for this property, 934 | // so just forward all access to the underlying object. 935 | Object.defineProperty(cache, prop, { 936 | configurable: true, 937 | enumerable: true, 938 | get() { 939 | return target[prop]; 940 | }, 941 | set(value) { 942 | target[prop] = value; 943 | } 944 | }); 945 | 946 | return value; 947 | } 948 | 949 | cache[prop] = value; 950 | return value; 951 | }, 952 | 953 | set(proxyTarget, prop, value, receiver) { 954 | if (prop in cache) { 955 | cache[prop] = value; 956 | } else { 957 | target[prop] = value; 958 | } 959 | return true; 960 | }, 961 | 962 | defineProperty(proxyTarget, prop, desc) { 963 | return Reflect.defineProperty(cache, prop, desc); 964 | }, 965 | 966 | deleteProperty(proxyTarget, prop) { 967 | return Reflect.deleteProperty(cache, prop); 968 | } 969 | }; 970 | 971 | // Per contract of the Proxy API, the "get" proxy handler must return the 972 | // original value of the target if that value is declared read-only and 973 | // non-configurable. For this reason, we create an object with the 974 | // prototype set to `target` instead of using `target` directly. 975 | // Otherwise we cannot return a custom object for APIs that 976 | // are declared read-only and non-configurable, such as `chrome.devtools`. 977 | // 978 | // The proxy handlers themselves will still use the original `target` 979 | // instead of the `proxyTarget`, so that the methods and properties are 980 | // dereferenced via the original targets. 981 | let proxyTarget = Object.create(target); 982 | return new Proxy(proxyTarget, handlers); 983 | }; 984 | 985 | /** 986 | * Creates a set of wrapper functions for an event object, which handles 987 | * wrapping of listener functions that those messages are passed. 988 | * 989 | * A single wrapper is created for each listener function, and stored in a 990 | * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener` 991 | * retrieve the original wrapper, so that attempts to remove a 992 | * previously-added listener work as expected. 993 | * 994 | * @param {DefaultWeakMap} wrapperMap 995 | * A DefaultWeakMap object which will create the appropriate wrapper 996 | * for a given listener function when one does not exist, and retrieve 997 | * an existing one when it does. 998 | * 999 | * @returns {object} 1000 | */ 1001 | const wrapEvent = wrapperMap => ({ 1002 | addListener(target, listener, ...args) { 1003 | target.addListener(wrapperMap.get(listener), ...args); 1004 | }, 1005 | 1006 | hasListener(target, listener) { 1007 | return target.hasListener(wrapperMap.get(listener)); 1008 | }, 1009 | 1010 | removeListener(target, listener) { 1011 | target.removeListener(wrapperMap.get(listener)); 1012 | } 1013 | }); 1014 | 1015 | // Keep track if the deprecation warning has been logged at least once. 1016 | let loggedSendResponseDeprecationWarning = false; 1017 | 1018 | const onMessageWrappers = new DefaultWeakMap(listener => { 1019 | if (typeof listener !== "function") { 1020 | return listener; 1021 | } 1022 | 1023 | /** 1024 | * Wraps a message listener function so that it may send responses based on 1025 | * its return value, rather than by returning a sentinel value and calling a 1026 | * callback. If the listener function returns a Promise, the response is 1027 | * sent when the promise either resolves or rejects. 1028 | * 1029 | * @param {*} message 1030 | * The message sent by the other end of the channel. 1031 | * @param {object} sender 1032 | * Details about the sender of the message. 1033 | * @param {function(*)} sendResponse 1034 | * A callback which, when called with an arbitrary argument, sends 1035 | * that value as a response. 1036 | * @returns {boolean} 1037 | * True if the wrapped listener returned a Promise, which will later 1038 | * yield a response. False otherwise. 1039 | */ 1040 | return function onMessage(message, sender, sendResponse) { 1041 | let didCallSendResponse = false; 1042 | 1043 | let wrappedSendResponse; 1044 | let sendResponsePromise = new Promise(resolve => { 1045 | wrappedSendResponse = function (response) { 1046 | if (!loggedSendResponseDeprecationWarning) { 1047 | console.warn(SEND_RESPONSE_DEPRECATION_WARNING, new Error().stack); 1048 | loggedSendResponseDeprecationWarning = true; 1049 | } 1050 | didCallSendResponse = true; 1051 | resolve(response); 1052 | }; 1053 | }); 1054 | 1055 | let result; 1056 | try { 1057 | result = listener(message, sender, wrappedSendResponse); 1058 | } catch (err) { 1059 | result = Promise.reject(err); 1060 | } 1061 | 1062 | const isResultThenable = result !== true && isThenable(result); 1063 | 1064 | // If the listener didn't returned true or a Promise, or called 1065 | // wrappedSendResponse synchronously, we can exit earlier 1066 | // because there will be no response sent from this listener. 1067 | if (result !== true && !isResultThenable && !didCallSendResponse) { 1068 | return false; 1069 | } 1070 | 1071 | // A small helper to send the message if the promise resolves 1072 | // and an error if the promise rejects (a wrapped sendMessage has 1073 | // to translate the message into a resolved promise or a rejected 1074 | // promise). 1075 | const sendPromisedResult = promise => { 1076 | promise.then(msg => { 1077 | // send the message value. 1078 | sendResponse(msg); 1079 | }, error => { 1080 | // Send a JSON representation of the error if the rejected value 1081 | // is an instance of error, or the object itself otherwise. 1082 | let message; 1083 | if (error && (error instanceof Error || typeof error.message === "string")) { 1084 | message = error.message; 1085 | } else { 1086 | message = "An unexpected error occurred"; 1087 | } 1088 | 1089 | sendResponse({ 1090 | __mozWebExtensionPolyfillReject__: true, 1091 | message 1092 | }); 1093 | }).catch(err => { 1094 | // Print an error on the console if unable to send the response. 1095 | console.error("Failed to send onMessage rejected reply", err); 1096 | }); 1097 | }; 1098 | 1099 | // If the listener returned a Promise, send the resolved value as a 1100 | // result, otherwise wait the promise related to the wrappedSendResponse 1101 | // callback to resolve and send it as a response. 1102 | if (isResultThenable) { 1103 | sendPromisedResult(result); 1104 | } else { 1105 | sendPromisedResult(sendResponsePromise); 1106 | } 1107 | 1108 | // Let Chrome know that the listener is replying. 1109 | return true; 1110 | }; 1111 | }); 1112 | 1113 | const wrappedSendMessageCallback = ({ reject, resolve }, reply) => { 1114 | if (extensionAPIs.runtime.lastError) { 1115 | // Detect when none of the listeners replied to the sendMessage call and resolve 1116 | // the promise to undefined as in Firefox. 1117 | // See https://github.com/mozilla/webextension-polyfill/issues/130 1118 | if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) { 1119 | resolve(); 1120 | } else { 1121 | reject(extensionAPIs.runtime.lastError); 1122 | } 1123 | } else if (reply && reply.__mozWebExtensionPolyfillReject__) { 1124 | // Convert back the JSON representation of the error into 1125 | // an Error instance. 1126 | reject(new Error(reply.message)); 1127 | } else { 1128 | resolve(reply); 1129 | } 1130 | }; 1131 | 1132 | const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => { 1133 | if (args.length < metadata.minArgs) { 1134 | throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); 1135 | } 1136 | 1137 | if (args.length > metadata.maxArgs) { 1138 | throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); 1139 | } 1140 | 1141 | return new Promise((resolve, reject) => { 1142 | const wrappedCb = wrappedSendMessageCallback.bind(null, { resolve, reject }); 1143 | args.push(wrappedCb); 1144 | apiNamespaceObj.sendMessage(...args); 1145 | }); 1146 | }; 1147 | 1148 | const staticWrappers = { 1149 | runtime: { 1150 | onMessage: wrapEvent(onMessageWrappers), 1151 | onMessageExternal: wrapEvent(onMessageWrappers), 1152 | sendMessage: wrappedSendMessage.bind(null, "sendMessage", { minArgs: 1, maxArgs: 3 }) 1153 | }, 1154 | tabs: { 1155 | sendMessage: wrappedSendMessage.bind(null, "sendMessage", { minArgs: 2, maxArgs: 3 }) 1156 | } 1157 | }; 1158 | const settingMetadata = { 1159 | clear: { minArgs: 1, maxArgs: 1 }, 1160 | get: { minArgs: 1, maxArgs: 1 }, 1161 | set: { minArgs: 1, maxArgs: 1 } 1162 | }; 1163 | apiMetadata.privacy = { 1164 | network: { 1165 | networkPredictionEnabled: settingMetadata, 1166 | webRTCIPHandlingPolicy: settingMetadata 1167 | }, 1168 | services: { 1169 | passwordSavingEnabled: settingMetadata 1170 | }, 1171 | websites: { 1172 | hyperlinkAuditingEnabled: settingMetadata, 1173 | referrersEnabled: settingMetadata 1174 | } 1175 | }; 1176 | 1177 | return wrapObject(extensionAPIs, staticWrappers, apiMetadata); 1178 | }; 1179 | 1180 | // The build process adds a UMD wrapper around this file, which makes the 1181 | // `module` variable available. 1182 | module.exports = wrapAPIs(chrome); 1183 | } else { 1184 | module.exports = browser; 1185 | } 1186 | }); 1187 | //# sourceMappingURL=browser-polyfill.js.map 1188 | -------------------------------------------------------------------------------- /magnify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevgk/OD-Search/f52565bde5b17aab60e35d1f557bfb4ef7e2e077/magnify.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "OD-Search", 4 | "version": "1.2", 5 | "description": "Web Extension that builds search queries.", 6 | "browser_action": { 7 | "default_title": "OD-Search", 8 | "default_icon": "magnify.png", 9 | "default_popup": "popup.html" 10 | }, 11 | "permissions": [ 12 | "storage", 13 | "clipboardWrite" 14 | ], 15 | "applications": { 16 | "gecko": { 17 | "id": "{bda7d743-06f5-4628-bcf7-7a847293a0b3}" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /popup.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | 6 | html, body { 7 | width: 300px; 8 | font-family: sans-serif; 9 | display: flex; 10 | align-items: center; 11 | } 12 | 13 | label { 14 | font-size: 12px; 15 | padding: 8px 0; 16 | } 17 | 18 | .container { 19 | display: flex; 20 | flex-wrap: wrap; 21 | width: 100%; 22 | margin: 0 auto; 23 | padding: 5px; 24 | } 25 | 26 | .search__input, 27 | .search__button { 28 | padding: 5px 10px; 29 | } 30 | 31 | .search__input { 32 | background-color: #fff; 33 | outline: none; 34 | border: 1px solid #ccc; 35 | width: 100%; 36 | } 37 | 38 | input, select, button { 39 | height: 30px; 40 | margin-bottom: 10px; 41 | } 42 | 43 | .search__button { 44 | color: #fff; 45 | border: none; 46 | background-color: #5e92f3; 47 | text-align: center; 48 | width: 50%; 49 | cursor: pointer; 50 | transition: background-color .3s; 51 | } 52 | 53 | .search__button:hover { 54 | background-color: #003c8f; 55 | } 56 | 57 | .secondary { 58 | background-color: #5472d3; 59 | } 60 | 61 | .secondary:hover { 62 | background-color: #002171; 63 | } 64 | 65 | .search__select, 66 | .search__exclude, 67 | .search__exclude > input { 68 | width: 100%; 69 | } 70 | 71 | 72 | .footer { 73 | width: 100%; 74 | text-align: center; 75 | display: block; 76 | font-size: 10px; 77 | text-decoration: none; 78 | color: #000; 79 | } -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 |
28 | 29 | 30 | 31 |
32 | OpenSource (Github) 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', async () => { 2 | 3 | const searchTermInput = document.querySelector('#searchTermInput'); 4 | const searchButton = document.querySelector('#searchButton'); 5 | const searchTypeSelect = document.querySelector('#searchTypeSelect'); 6 | const searchTimeSelect = document.querySelector('#searchTimeSelect'); 7 | const searchEngineSelect = document.querySelector('#searchEngineSelect'); 8 | const copyButton = document.querySelector('#copyButton'); 9 | const excludeWordsInput = document.querySelector('#excludeWordsInput'); 10 | const excludeSitesInput = document.querySelector('#excludeSitesInput'); 11 | 12 | const searchEngines = { 13 | 'Google': 'google.com/search?q=', 14 | 'DuckDuckGo': 'duckduckgo.com/?q=', 15 | 'DuckDuckGo (nojs)': 'duckduckgo.com/html?q=', 16 | 'Startpage': 'startpage.com/do/search?query=', 17 | 'Searx': 'searx.me/?q=', 18 | 'metager': 'metager.de/meta/meta.ger3?eingabe=' 19 | }; 20 | 21 | searchEngineSelect.innerHTML = String(await generateSearchEngineOptionsHTML()); 22 | 23 | const presets = await loadPresets(); 24 | 25 | searchTypeSelect.innerHTML = String(generateSearchTypeOptionsHTML()); 26 | 27 | searchButton.addEventListener('click', search); 28 | copyButton.addEventListener('click', copyQuery); 29 | 30 | document.addEventListener('keyup', e => { 31 | if (e.which === 13) search(); 32 | }); 33 | 34 | async function search() { 35 | if (!searchTermInput.value) return; 36 | 37 | const query = await buildQuery(); 38 | 39 | const timespan = getTimeParameter(searchTimeSelect.value); 40 | 41 | const searchEngine = searchEngines[searchEngineSelect.value]; 42 | 43 | const url = buildURL(searchEngine, query, timespan); 44 | 45 | browser.tabs.create({ 46 | url, 47 | active: true 48 | }); 49 | } 50 | 51 | async function copyQuery () { 52 | if (!searchTermInput.value) return; 53 | 54 | const query = await buildQuery(); 55 | await navigator.clipboard.writeText(query); 56 | } 57 | 58 | async function buildQuery () { 59 | const presetName = searchTypeSelect.value; 60 | const includeNames = getPresetIncludes(presetName); 61 | const includes = getContentFromIncludes(includeNames); 62 | 63 | const { prepend = '', append = '' } = presets[presetName].searchTerm || {}; 64 | const searchTerms = searchTermInput.value.split(' ').map(term => `${prepend}${term.trim()}${append}`).join('.'); 65 | 66 | const excludeWords = makeParamWithOrList('-insite', excludeWordsInput.value); 67 | const excludeSites = makeParamWithOrList('-inurl', excludeSitesInput.value); 68 | 69 | await saveSearchEngine(searchEngineSelect.value); 70 | 71 | return `${searchTerms} ${includes} ${excludeWords} ${excludeSites}`; 72 | } 73 | 74 | function buildURL (searchEngine, query, timespan) { 75 | return `https://${searchEngine}${encodeURIComponent(query)}${timespan}`; 76 | } 77 | 78 | async function loadPresets () { 79 | let { presets } = await browser.storage.sync.get('presets'); 80 | 81 | if (true || !presets) { 82 | const data = await fetch('/presets.json'); 83 | presets = await data.json(); 84 | 85 | await browser.storage.sync.set({ presets }); 86 | } 87 | return presets; 88 | } 89 | 90 | function getPresetIncludes(original) { 91 | if (!presets[original]) return []; 92 | return [].concat(presets[original].include || []).reduce( 93 | (r, name) => [...r, ...getPresetIncludes(name)], 94 | [original] 95 | ); 96 | } 97 | 98 | function getContentFromIncludes (includes) { 99 | const contents = includes.map(incName => { 100 | return presets[incName].content ? presets[incName].content.join(' ') : ''; 101 | }); 102 | return contents.join(' '); 103 | } 104 | 105 | function makeParamWithOrList (name, values) { 106 | if (!values.length) return ''; 107 | 108 | values = values.replace(/\s*,\s*/g, '|'); 109 | return `${name}:(${values})`; 110 | } 111 | 112 | function getTimeParameter (value = '') { 113 | return `&tbs=qdr:${value}`; 114 | } 115 | 116 | function generateSearchTypeOptionsHTML () { 117 | const presetNames = Object.keys(presets); 118 | const options = presetNames.map(key => { 119 | const { hidden, title } = presets[key]; 120 | if (!hidden) return ``; 121 | }); 122 | 123 | return options.join(' '); 124 | } 125 | 126 | async function generateSearchEngineOptionsHTML () { 127 | const { searchEngine } = await browser.storage.sync.get('searchEngine'); 128 | 129 | const searchEngineNames = Object.keys(searchEngines); 130 | const options = searchEngineNames.map(engine => { 131 | const selected = engine === searchEngine ? 'selected' : ''; 132 | return ``; 133 | }); 134 | 135 | return options.join(''); 136 | } 137 | 138 | async function saveSearchEngine (searchEngine) { 139 | await browser.storage.sync.set({ searchEngine }); 140 | } 141 | }); -------------------------------------------------------------------------------- /presets.json: -------------------------------------------------------------------------------- 1 | { 2 | "videos": { 3 | "title": "Videos", 4 | "include": ["videoFormats", "commons"], 5 | "hidden": false 6 | }, 7 | "gdrive_videos": { 8 | "title": "Videos (Google Drive)", 9 | "content": ["site:drive.google.com"], 10 | "include": ["videoFormats"] 11 | }, 12 | "images": { 13 | "title": "Images", 14 | "searchTerm": { 15 | "prepend": "", 16 | "append": "" 17 | }, 18 | "include": ["imageFormats", "commons"], 19 | "hidden": false 20 | }, 21 | "gdrive_images": { 22 | "title": "Images (Google Drive)", 23 | "content": ["site:drive.google.com"], 24 | "include": ["imageFormats"] 25 | }, 26 | "imagesWpUploads": { 27 | "title": "Images (wp_uploads)", 28 | "content": ["inurl:\"/wp-content/uploads/\""], 29 | "include": ["imageFormats", "commons"] 30 | }, 31 | "audio": { 32 | "title": "Audio", 33 | "include": ["audioFormats", "commons"], 34 | "hidden": false 35 | }, 36 | "gdrive_audio": { 37 | "title": "Audio (Google Drive)", 38 | "content": ["site:drive.google.com"], 39 | "include": ["audioFormats"] 40 | }, 41 | "documents": { 42 | "title": "Documents", 43 | "include": ["documentFormats", "commons"], 44 | "hidden": false 45 | }, 46 | "gdrive_documents": { 47 | "title": "Documents (Google Drive)", 48 | "content": ["site:drive.google.com"], 49 | "include": ["documentFormats"] 50 | }, 51 | "documentsWpUploads": { 52 | "title": "Documents (wp_uploads)", 53 | "content": ["inurl:\"/wp-content/uploads/\""], 54 | "include": ["documentFormats", "commons"] 55 | }, 56 | "executables": { 57 | "title": "Executables", 58 | "include": ["executableFormats", "commons"], 59 | "hidden": false 60 | }, 61 | "archives": { 62 | "title": "Archives", 63 | "include": ["archiveFormats", "commons"], 64 | "hidden": false 65 | }, 66 | "gdrive_archives": { 67 | "title": "Archives (Google Drive)", 68 | "content": ["site:drive.google.com"], 69 | "include": ["archiveFormats"] 70 | }, 71 | "videoFormats": { 72 | "content": ["(avi|mkv|mov|mp4|mpg|wmv)"], 73 | "hidden": true 74 | }, 75 | "audioFormats": { 76 | "content": ["(ac3|flac|m4a|mp3|ogg|wav|wma)"], 77 | "hidden": true 78 | }, 79 | "documentFormats": { 80 | "content": ["(CBZ|CBR|CHM|DOC|DOCX|EPUB|MOBI|ODT|PDF|RTF|txt)"], 81 | "hidden": true 82 | }, 83 | "imageFormats": { 84 | "content": ["(bmp|gif|jpg|jpeg|png|psd|tif|tiff)"], 85 | "hidden": true 86 | }, 87 | "executableFormats": { 88 | "content": ["(exe|apk)"], 89 | "hidden": true 90 | }, 91 | "archiveFormats": { 92 | "content": ["(iso|rar|tar|zip|7z)"], 93 | "hidden": true 94 | }, 95 | "commons": { 96 | "content": [ 97 | "-inurl:(jsp|pl|php|html|aspx|htm|cf|shtml)", 98 | "-inurl:(index_of|listen77|mp3raid|mp3toss|mp3drug|index_of|wallywashis)", 99 | "intitle:\"index.of.\"" 100 | ], 101 | "hidden": true 102 | }, 103 | "indexof": { 104 | "title": "index of", 105 | "content": [ 106 | "intitle:\"index.of.\"", 107 | "allintitle:\"index of\"", 108 | "\"index of /pub\"", 109 | "parent directory" 110 | ], 111 | "hidden": false 112 | } 113 | } -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevgk/OD-Search/f52565bde5b17aab60e35d1f557bfb4ef7e2e077/screenshot.png --------------------------------------------------------------------------------