├── .browserslistrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
├── label-actions.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ ├── label-actions.yml
│ └── lockdown.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── gulpfile.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
├── action
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── assets
│ ├── fonts
│ │ └── roboto.css
│ ├── icons
│ │ ├── app
│ │ │ └── icon.svg
│ │ ├── engines
│ │ │ ├── 123rf-dark.svg
│ │ │ ├── 123rf.svg
│ │ │ ├── adobestock.svg
│ │ │ ├── alamy-dark.svg
│ │ │ ├── alamy.svg
│ │ │ ├── alibabaChina.svg
│ │ │ ├── allEngines.svg
│ │ │ ├── ascii2d-dark.svg
│ │ │ ├── ascii2d.svg
│ │ │ ├── auDesign.svg
│ │ │ ├── auTrademark.svg
│ │ │ ├── baidu.svg
│ │ │ ├── bing.svg
│ │ │ ├── clipretrieval-dark.svg
│ │ │ ├── depositphotos-dark.svg
│ │ │ ├── depositphotos.svg
│ │ │ ├── dreamstime.svg
│ │ │ ├── esearch.svg
│ │ │ ├── freepik-dark.svg
│ │ │ ├── freepik.svg
│ │ │ ├── getty-dark.svg
│ │ │ ├── getty.svg
│ │ │ ├── googleImages.svg
│ │ │ ├── googleLens.svg
│ │ │ ├── icons8.svg
│ │ │ ├── ikea.svg
│ │ │ ├── immerse-dark.svg
│ │ │ ├── iqdb.png
│ │ │ ├── istock-dark.svg
│ │ │ ├── istock.svg
│ │ │ ├── jpDesign-dark.svg
│ │ │ ├── jpDesign.svg
│ │ │ ├── kagi.svg
│ │ │ ├── lenso-dark.svg
│ │ │ ├── lenso.svg
│ │ │ ├── lexica-dark.svg
│ │ │ ├── lexica.svg
│ │ │ ├── lykdat-dark.svg
│ │ │ ├── lykdat.svg
│ │ │ ├── nzTrademark-dark.svg
│ │ │ ├── nzTrademark.svg
│ │ │ ├── pimeyes-dark.svg
│ │ │ ├── pimeyes.svg
│ │ │ ├── pinterest.svg
│ │ │ ├── pixta-dark.svg
│ │ │ ├── pixta.svg
│ │ │ ├── pond5-dark.svg
│ │ │ ├── pond5.svg
│ │ │ ├── qihoo.svg
│ │ │ ├── repostSleuth.png
│ │ │ ├── saucenao-dark.svg
│ │ │ ├── saucenao.svg
│ │ │ ├── shein.svg
│ │ │ ├── shutterstock.svg
│ │ │ ├── sogou.svg
│ │ │ ├── stocksy-dark.svg
│ │ │ ├── stocksy.svg
│ │ │ ├── taobao.svg
│ │ │ ├── tineye.png
│ │ │ ├── tmview-dark.svg
│ │ │ ├── tmview.svg
│ │ │ ├── unsplash-dark.svg
│ │ │ ├── unsplash.svg
│ │ │ ├── whatanime.png
│ │ │ ├── wildberries.svg
│ │ │ ├── wipo.svg
│ │ │ └── yandex.svg
│ │ ├── misc
│ │ │ ├── broken-image-dark.svg
│ │ │ ├── broken-image.svg
│ │ │ ├── close.svg
│ │ │ ├── content-copy.svg
│ │ │ ├── crop-free-light.svg
│ │ │ ├── error.svg
│ │ │ ├── favorite-filled.svg
│ │ │ ├── favorite-light.svg
│ │ │ ├── help-light.svg
│ │ │ ├── image-light.svg
│ │ │ ├── image-link-light.svg
│ │ │ ├── image.svg
│ │ │ ├── ios-share-light.svg
│ │ │ ├── ios-share.svg
│ │ │ ├── keep-filled-light.svg
│ │ │ ├── keep-light.svg
│ │ │ ├── link-light.svg
│ │ │ ├── more-vert.svg
│ │ │ ├── open-in-new-light.svg
│ │ │ ├── open-in-new.svg
│ │ │ ├── open-with.svg
│ │ │ ├── settings-light.svg
│ │ │ ├── settings.svg
│ │ │ ├── share-light.svg
│ │ │ ├── share.svg
│ │ │ ├── spinner.svg
│ │ │ ├── swap-horiz.svg
│ │ │ ├── swap-vert.svg
│ │ │ └── upload-light.svg
│ │ └── sponsors
│ │ │ ├── lenso-dark.svg
│ │ │ └── lenso.svg
│ ├── locales
│ │ └── en
│ │ │ ├── messages-firefox.json
│ │ │ ├── messages-safari.json
│ │ │ └── messages.json
│ └── manifest
│ │ ├── chrome.json
│ │ ├── edge.json
│ │ ├── firefox.json
│ │ ├── opera.json
│ │ ├── safari.json
│ │ └── samsung.json
├── background
│ ├── index.html
│ └── main.js
├── base
│ └── main.js
├── browse
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── capture
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── confirm
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── content
│ ├── main.js
│ └── style.css
├── contribute
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── engines
│ ├── 123rf.js
│ ├── adobestock.js
│ ├── alamy.js
│ ├── alibabaChina.js
│ ├── ascii2d.js
│ ├── auDesign.js
│ ├── auTrademark.js
│ ├── baidu.js
│ ├── bing.js
│ ├── branddb.js
│ ├── css
│ │ └── bing.css
│ ├── depositphotos.js
│ ├── dreamstime.js
│ ├── esearch.js
│ ├── freepik.js
│ ├── getty.js
│ ├── googleImages.js
│ ├── googleLens.js
│ ├── icons8.js
│ ├── ikea.js
│ ├── iqdb.js
│ ├── istock.js
│ ├── jpDesign.js
│ ├── kagi.js
│ ├── lenso.js
│ ├── lexica.js
│ ├── lykdat.js
│ ├── madridMonitor.js
│ ├── nzTrademark.js
│ ├── pimeyes.js
│ ├── pixta.js
│ ├── pond5.js
│ ├── qihoo.js
│ ├── repostSleuth.js
│ ├── saucenao.js
│ ├── shein.js
│ ├── shutterstock.js
│ ├── sogou.js
│ ├── stocksy.js
│ ├── taobao.js
│ ├── tineye.js
│ ├── tmview.js
│ ├── unsplash.js
│ ├── whatanime.js
│ ├── wildberries.js
│ └── yandex.js
├── options
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── parse
│ └── main.js
├── search
│ ├── App.vue
│ ├── index.html
│ └── main.js
├── select
│ ├── App.vue
│ ├── index.html
│ ├── main.js
│ └── pointer.css
├── storage
│ ├── config.json
│ ├── init.js
│ ├── revisions
│ │ ├── local
│ │ │ ├── 20210820184257_support_event_pages.js
│ │ │ ├── 20210919175209_add_image_sharing_options.js
│ │ │ ├── 20210928090443_add_unsplash.js
│ │ │ ├── 20211011114043_configure_engines.js
│ │ │ ├── 20211106103932_add_shein.js
│ │ │ ├── 20211111071410_add_lykdat.js
│ │ │ ├── 20211204124507_add_wildberries.js
│ │ │ ├── 20211213014048_add_lastengineaccesscheck.js
│ │ │ ├── 20211213191049_add_setcontextmenuevent.js
│ │ │ ├── 20220108063511_add_google_lens.js
│ │ │ ├── 20220321163741_add_clipboard_support.js
│ │ │ ├── 20220505174634_detect_alternative_image_sizes.js
│ │ │ ├── 20220516051842_update_search_mode_ids.js
│ │ │ ├── 20220516124148_open_images.js
│ │ │ ├── 20220814142801_configure_engines.js
│ │ │ ├── 20220924083846_add_lexica.js
│ │ │ ├── 20230415111029_add_theme_support.js
│ │ │ ├── 20230531103023_remove_search_engines.js
│ │ │ ├── 20230601115626_add_kagi.js
│ │ │ ├── 20230624145626_add_search_engines.js
│ │ │ ├── 20230716121432_add_freepik.js
│ │ │ ├── 20231009185955_remove_unsplash.js
│ │ │ ├── 20231102164602_add_icons8.js
│ │ │ ├── 20240514170322_add_appversion.js
│ │ │ ├── 20240529183556_update_search_engines.js
│ │ │ ├── 20240619180111_add_menuchangeevent.js
│ │ │ ├── 20240624161944_remove_search_engines.js
│ │ │ ├── 20250102095603_add_lenso.ai.js
│ │ │ ├── 20250218121640_remove_google_images.js
│ │ │ ├── 20250317134215_add_back_google_images.js
│ │ │ ├── 20250413115022_add_unsplash.js
│ │ │ ├── 20250511143502_remove_karma_decay.js
│ │ │ ├── BJXdcUXOG.js
│ │ │ ├── BJguWEHcbz.js
│ │ │ ├── Bk42MzXdW.js
│ │ │ ├── HJ5MKKGhW.js
│ │ │ ├── Hy1tD8ANb.js
│ │ │ ├── IMMjiccGj.js
│ │ │ ├── K9Esw2jiQ3.js
│ │ │ ├── LX20x6G8l.js
│ │ │ ├── S18hLi5tG.js
│ │ │ ├── S1ebtZ3Uzz.js
│ │ │ ├── S1kNNadHZ.js
│ │ │ ├── SJzIWmjKz.js
│ │ │ ├── ShjDMM87.js
│ │ │ ├── SklYTwQOYf.js
│ │ │ ├── SkwaU8NlX.js
│ │ │ ├── Syy800KkQ.js
│ │ │ ├── TshBYj8anA.js
│ │ │ ├── UhWEtK9gMh.js
│ │ │ ├── d8CIdomCW.js
│ │ │ ├── d8IK4KCtVm.js
│ │ │ ├── ekhOvNiTsF.js
│ │ │ ├── ggrr9C9pgV.js
│ │ │ ├── r1H3rgx1X.js
│ │ │ ├── r1Pvd36nz.js
│ │ │ ├── ryY8H0EWf.js
│ │ │ ├── ryekyizAg.js
│ │ │ └── yLciyvS5He.js
│ │ └── session
│ │ │ └── 20240514122825_initial_version.js
│ └── storage.js
├── tab
│ ├── index.html
│ └── main.js
├── utils
│ ├── app.js
│ ├── common.js
│ ├── config.js
│ ├── data.js
│ ├── engines.js
│ ├── registry.js
│ ├── scripts.js
│ └── vuetify.js
└── view
│ ├── App.vue
│ ├── index.html
│ └── main.js
└── webpack.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | [chrome]
2 | Chrome >= 123
3 | Edge >= 123
4 | Opera >= 109
5 |
6 | [edge]
7 | Edge >= 123
8 |
9 | [firefox]
10 | Firefox >= 115
11 | FirefoxAndroid >= 115
12 |
13 | [opera]
14 | Opera >= 109
15 |
16 | [safari]
17 | Safari >= 17
18 | iOS >= 17
19 |
20 | [samsung]
21 | Samsung >= 14
22 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: dessant
2 | patreon: dessant
3 | custom:
4 | - https://armin.dev/go/paypal
5 | - https://armin.dev/go/bitcoin
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report if something isn't working as expected
4 |
5 | ---
6 |
7 | **System**
8 |
9 |
10 | * OS name: [e.g. Windows, Ubuntu]
11 | * OS version: [e.g. 10]
12 | * Browser name: [e.g. Chrome, Firefox]
13 | * Browser version: [e.g. 60]
14 | * Extension version: [e.g. 1.0.0]
15 |
16 | **Bug description**
17 |
23 |
24 | **Logs**
25 |
26 |
30 | ```
31 | // REPLACE WITH LOGS
32 | ```
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 |
9 |
10 | **Describe the solution you'd like**
11 |
12 |
13 | **Describe alternatives you've considered**
14 |
15 |
16 | **Additional context**
17 |
18 |
--------------------------------------------------------------------------------
/.github/label-actions.yml:
--------------------------------------------------------------------------------
1 | # Configuration for Label Actions - https://github.com/dessant/label-actions
2 |
3 | incomplete:
4 | issues:
5 | comment: >
6 | @{issue-author}, the issue does not contain enough information
7 | to reproduce the bug. Please open a new bug report and fill out
8 | the issue template with the requested data.
9 | close: true
10 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | This project does not accept pull requests. Please use issues to report bugs or suggest new features.
2 |
--------------------------------------------------------------------------------
/.github/workflows/label-actions.yml:
--------------------------------------------------------------------------------
1 | name: 'Label Actions'
2 |
3 | on:
4 | issues:
5 | types: [labeled, unlabeled]
6 |
7 | permissions:
8 | contents: read
9 | issues: write
10 |
11 | jobs:
12 | action:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: dessant/label-actions@v4
16 |
--------------------------------------------------------------------------------
/.github/workflows/lockdown.yml:
--------------------------------------------------------------------------------
1 | name: 'Repo Lockdown'
2 |
3 | on:
4 | pull_request_target:
5 | types: opened
6 |
7 | permissions:
8 | pull-requests: write
9 |
10 | jobs:
11 | action:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: dessant/repo-lockdown@v4
15 | with:
16 | exclude-pr-created-before: '2021-08-18T00:00:00Z'
17 | pr-comment: 'This project does not accept pull requests. Please use issues to report bugs or suggest new features.'
18 | process-only: 'prs'
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.assets/
2 | /app/
3 | /artifacts/
4 | /dist/
5 |
6 | /report.json
7 | /report.html
8 |
9 | /web-ext-config.mjs
10 |
11 | node_modules/
12 | /npm-debug.log
13 |
14 | /.vscode
15 |
16 | xcuserdata/
17 |
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.12.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | *.md
3 |
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | singleQuote: true
2 | bracketSpacing: false
3 | arrowParens: 'avoid'
4 | trailingComma: 'none'
5 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('node:path');
2 |
3 | const corejsVersion = require(
4 | path.join(path.dirname(require.resolve('core-js')), 'package.json')
5 | ).version;
6 |
7 | module.exports = function (api) {
8 | api.cache(true);
9 |
10 | const presets = [
11 | [
12 | '@babel/env',
13 | {
14 | modules: false,
15 | bugfixes: true,
16 | useBuiltIns: 'usage',
17 | corejs: {version: corejsVersion}
18 | }
19 | ]
20 | ];
21 |
22 | const plugins = [];
23 |
24 | const ignore = [
25 | new RegExp(`node_modules\\${path.sep}(?!(vueton|wesa)\\${path.sep}).*`)
26 | ];
27 |
28 | const parserOpts = {plugins: ['importAttributes']};
29 |
30 | return {presets, plugins, ignore, parserOpts};
31 | };
32 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const postcssPresetEnv = require('postcss-preset-env');
2 | const cssnano = require('cssnano');
3 |
4 | module.exports = function (api) {
5 | const plugins = [postcssPresetEnv()];
6 |
7 | if (api.env === 'production') {
8 | plugins.push(cssnano({zindex: false, discardUnused: false}));
9 | }
10 |
11 | return {plugins};
12 | };
13 |
--------------------------------------------------------------------------------
/src/action/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/action/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/assets/fonts/roboto.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Roboto';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: url('./files/roboto-latin-400-normal.woff2') format('woff2'),
6 | local('Roboto'), local('Roboto-Regular');
7 | }
8 |
9 | @font-face {
10 | font-family: 'Roboto';
11 | font-style: normal;
12 | font-weight: 500;
13 | src: url('./files/roboto-latin-500-normal.woff2') format('woff2'),
14 | local('Roboto Medium'), local('Roboto-Medium');
15 | }
16 |
17 | @font-face {
18 | font-family: 'Roboto';
19 | font-style: normal;
20 | font-weight: 700;
21 | src: url('./files/roboto-latin-700-normal.woff2') format('woff2'),
22 | local('Roboto Bold'), local('Roboto-Bold');
23 | }
24 |
--------------------------------------------------------------------------------
/src/assets/icons/app/icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/123rf-dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/123rf.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/adobestock.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/alamy-dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/alamy.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/alibabaChina.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/allEngines.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/ascii2d-dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/ascii2d.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/auDesign.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/auTrademark.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/baidu.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/clipretrieval-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/depositphotos-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/depositphotos.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/dreamstime.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/freepik-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/freepik.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/getty-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/getty.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/googleImages.svg:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/googleLens.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/icons8.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/ikea.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/immerse-dark.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/iqdb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dessant/search-by-image/870fb4b5196c0b60711822206f457910824037ef/src/assets/icons/engines/iqdb.png
--------------------------------------------------------------------------------
/src/assets/icons/engines/istock-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/istock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/kagi.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lenso-dark.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lenso.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lexica-dark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lexica.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lykdat-dark.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/lykdat.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pimeyes-dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pimeyes.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pinterest.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pixta-dark.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pixta.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pond5-dark.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/pond5.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/repostSleuth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dessant/search-by-image/870fb4b5196c0b60711822206f457910824037ef/src/assets/icons/engines/repostSleuth.png
--------------------------------------------------------------------------------
/src/assets/icons/engines/saucenao-dark.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/saucenao.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/shein.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/shutterstock.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/sogou.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/tineye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dessant/search-by-image/870fb4b5196c0b60711822206f457910824037ef/src/assets/icons/engines/tineye.png
--------------------------------------------------------------------------------
/src/assets/icons/engines/tmview-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/tmview.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/unsplash-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/unsplash.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/whatanime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dessant/search-by-image/870fb4b5196c0b60711822206f457910824037ef/src/assets/icons/engines/whatanime.png
--------------------------------------------------------------------------------
/src/assets/icons/engines/wildberries.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/engines/yandex.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/broken-image-dark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/broken-image.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/content-copy.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/crop-free-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/favorite-filled.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/favorite-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/help-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/image-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/image-link-light.svg:
--------------------------------------------------------------------------------
1 |
4 |
10 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/image.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/ios-share-light.svg:
--------------------------------------------------------------------------------
1 |
3 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/ios-share.svg:
--------------------------------------------------------------------------------
1 |
3 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/keep-filled-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/keep-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/link-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/more-vert.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/open-in-new-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/open-in-new.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/open-with.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/settings-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/settings.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/share-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/share.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/spinner.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/swap-horiz.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/swap-vert.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/misc/upload-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/locales/en/messages-firefox.json:
--------------------------------------------------------------------------------
1 | {
2 | "menuItemTitle_allEngines": {
3 | "message": "All Search Engines",
4 | "description": "Title of the menu item."
5 | },
6 |
7 | "menuItemTitle_viewImage": {
8 | "message": "Open Image",
9 | "description": "Title of the menu item."
10 | },
11 |
12 | "menuItemTitle_shareImage": {
13 | "message": "Share Image",
14 | "description": "Title of the menu item."
15 | },
16 |
17 | "optionValue_action_searchModeAction_selectImage": {
18 | "message": "Select Image",
19 | "description": "Value of the option."
20 | },
21 |
22 | "mainMenuItemTitle_allEngines": {
23 | "message": "Search All Engines for Image",
24 | "description": "Title of the menu item."
25 | },
26 |
27 | "mainMenuItemTitle_engine": {
28 | "message": "Search $ENGINE$ for Image",
29 | "description": "Title of the menu item.",
30 | "placeholders": {
31 | "engine": {
32 | "content": "$1",
33 | "example": "Google"
34 | }
35 | }
36 | },
37 |
38 | "optionSectionTitle_engines": {
39 | "message": "Search Engines",
40 | "description": "Title of the options section."
41 | },
42 |
43 | "optionSectionTitle_contextmenu": {
44 | "message": "Context Menu",
45 | "description": "Title of the options section."
46 | },
47 |
48 | "optionSectionTitle_toolbar": {
49 | "message": "Browser Toolbar",
50 | "description": "Title of the options section."
51 | },
52 |
53 | "optionSectionTitleMobile_toolbar": {
54 | "message": "Browser Menu",
55 | "description": "Title of the options section."
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/assets/locales/en/messages-safari.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionDescription": {
3 | "message": "Effortless reverse image searches, with support for a vast selection of search engines.",
4 | "description": "Description of the extension."
5 | },
6 |
7 | "menuItemTitle_allEngines": {
8 | "message": "All Search Engines",
9 | "description": "Title of the menu item."
10 | },
11 |
12 | "menuItemTitle_viewImage": {
13 | "message": "Open Image",
14 | "description": "Title of the menu item."
15 | },
16 |
17 | "menuItemTitle_shareImage": {
18 | "message": "Share Image",
19 | "description": "Title of the menu item."
20 | },
21 |
22 | "optionValue_action_searchModeAction_selectImage": {
23 | "message": "Select Image",
24 | "description": "Value of the option."
25 | },
26 |
27 | "mainMenuItemTitle_allEngines": {
28 | "message": "Search All Engines for Image",
29 | "description": "Title of the menu item."
30 | },
31 |
32 | "mainMenuItemTitle_engine": {
33 | "message": "Search $ENGINE$ for Image",
34 | "description": "Title of the menu item.",
35 | "placeholders": {
36 | "engine": {
37 | "content": "$1",
38 | "example": "Google"
39 | }
40 | }
41 | },
42 |
43 | "optionSectionTitle_engines": {
44 | "message": "Search Engines",
45 | "description": "Title of the options section."
46 | },
47 |
48 | "optionSectionTitle_contextmenu": {
49 | "message": "Context Menu",
50 | "description": "Title of the options section."
51 | },
52 |
53 | "optionSectionTitle_toolbar": {
54 | "message": "Browser Toolbar",
55 | "description": "Title of the options section."
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/background/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/browse/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/browse/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/capture/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/capture/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/confirm/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/confirm/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/content/style.css:
--------------------------------------------------------------------------------
1 | :host {
2 | all: initial !important;
3 | width: 0px !important;
4 | height: 0px !important;
5 | }
6 |
7 | #select {
8 | all: initial;
9 | position: fixed;
10 | bottom: env(safe-area-inset-bottom, 0px);
11 | left: 50%;
12 | width: 100%;
13 | max-width: 416px;
14 | height: 64px;
15 | transform: translateX(-50%);
16 | z-index: 2147483647;
17 | }
18 |
19 | #capture,
20 | #confirm {
21 | all: initial;
22 | position: fixed;
23 | top: 0;
24 | right: 0;
25 | bottom: 0;
26 | left: 0;
27 | width: 100%;
28 | height: 100%;
29 | z-index: 2147483647;
30 | }
31 |
32 | .hidden {
33 | display: none !important;
34 | }
35 |
36 | .fade-in:not(.hidden) {
37 | animation-name: fadeIn !important;
38 | animation-timing-function: cubic-bezier(0.5, 0, 0.75, 0) !important;
39 | animation-fill-mode: both !important;
40 | animation-duration: 0.1s !important;
41 | animation-delay: 0.05s !important;
42 | }
43 |
44 | @keyframes fadeIn {
45 | 0% {
46 | opacity: 0;
47 | }
48 |
49 | 100% {
50 | opacity: 1;
51 | }
52 | }
53 |
54 | .theme-light {
55 | color-scheme: light !important;
56 | }
57 |
58 | .theme-dark {
59 | color-scheme: dark !important;
60 | }
61 |
--------------------------------------------------------------------------------
/src/contribute/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
58 |
59 |
69 |
--------------------------------------------------------------------------------
/src/contribute/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/contribute/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto', '700 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/engines/123rf.js:
--------------------------------------------------------------------------------
1 | import {validateUrl} from 'utils/app';
2 | import {runOnce} from 'utils/common';
3 | import {initSearch, prepareImageForUpload, sendReceipt} from 'utils/engines';
4 |
5 | const engine = '123rf';
6 |
7 | async function search({session, search, image, storageIds}) {
8 | image = await prepareImageForUpload({
9 | image,
10 | engine,
11 | target: 'api'
12 | });
13 |
14 | const data = new FormData();
15 | data.append('image_base64', image.imageDataUrl);
16 |
17 | const rsp = await fetch(
18 | 'https://www.123rf.com/apicore/search/reverse/upload',
19 | {
20 | mode: 'cors',
21 | method: 'POST',
22 | body: data
23 | }
24 | );
25 |
26 | if (rsp.status !== 200) {
27 | throw new Error(`API response: ${rsp.status}, ${await rsp.text()}`);
28 | }
29 |
30 | const rspText = await rsp.text();
31 |
32 | // JSON response may start with HTML
33 | const searchData = JSON.parse(rspText.substring(rspText.indexOf('{')));
34 |
35 | const tabUrl =
36 | 'https://www.123rf.com/reverse-search/?fid=' + searchData.data.fid;
37 |
38 | await sendReceipt(storageIds);
39 |
40 | if (validateUrl(tabUrl)) {
41 | window.location.replace(tabUrl);
42 | }
43 | }
44 |
45 | function init() {
46 | initSearch(search, engine, taskId);
47 | }
48 |
49 | if (runOnce('search')) {
50 | init();
51 | }
52 |
--------------------------------------------------------------------------------
/src/engines/adobestock.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'adobestock';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('button[aria-label="Find similar"]')).click();
8 |
9 | const inputSelector = 'input[type=file]';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | await sendReceipt(storageIds);
15 |
16 | input.dispatchEvent(new Event('change', {bubbles: true}));
17 | }
18 |
19 | function init() {
20 | initSearch(search, engine, taskId);
21 | }
22 |
23 | if (runOnce('search')) {
24 | init();
25 | }
26 |
--------------------------------------------------------------------------------
/src/engines/alamy.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'alamy';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('button[data-testid="searchByImageLong"]')).click();
8 |
9 | const inputSelector = '#upload-an-image-tab input[type=file]';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | await sendReceipt(storageIds);
15 |
16 | input.dispatchEvent(new Event('change', {bubbles: true}));
17 | }
18 |
19 | function init() {
20 | initSearch(search, engine, taskId);
21 | }
22 |
23 | if (runOnce('search')) {
24 | init();
25 | }
26 |
--------------------------------------------------------------------------------
/src/engines/alibabaChina.js:
--------------------------------------------------------------------------------
1 | import {findNode, executeScriptMainContext, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'alibabaChina';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | await executeScriptMainContext({func: 'alibabaChinaPatchContextScript'});
8 |
9 | const inputSelector = 'input.image-file-reader-wrapper';
10 | const input = await findNode(inputSelector);
11 |
12 | input.click();
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | window.setTimeout(() => {
19 | input.dispatchEvent(new Event('change', {bubbles: true}));
20 | }, 100);
21 | }
22 |
23 | function init() {
24 | initSearch(search, engine, taskId);
25 | }
26 |
27 | if (runOnce('search')) {
28 | init();
29 | }
30 |
--------------------------------------------------------------------------------
/src/engines/ascii2d.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'ascii2d';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = '#file-form';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | await sendReceipt(storageIds);
13 |
14 | (await findNode('#file_upload')).submit();
15 | }
16 |
17 | function init() {
18 | initSearch(search, engine, taskId);
19 | }
20 |
21 | if (runOnce('search')) {
22 | init();
23 | }
24 |
--------------------------------------------------------------------------------
/src/engines/auDesign.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'auDesign';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = 'input[type=file]';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | input.dispatchEvent(new Event('change'));
13 |
14 | (await findNode('.popup-content .buttons button')).click();
15 |
16 | await sendReceipt(storageIds);
17 |
18 | (
19 | await Promise.race([
20 | findNode('.as-search-button'), // desktop
21 | findNode('.qs-search-button') // mobile
22 | ])
23 | ).click();
24 | }
25 |
26 | function init() {
27 | initSearch(search, engine, taskId);
28 | }
29 |
30 | if (runOnce('search')) {
31 | init();
32 | }
33 |
--------------------------------------------------------------------------------
/src/engines/auTrademark.js:
--------------------------------------------------------------------------------
1 | import {findNode, processNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'auTrademark';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // go to desktop version on mobile
8 | processNode(
9 | '#pageContent a[onclick^="document.cookie=\'_fullMobile=true"]',
10 | node => node.click(),
11 | {throwError: false}
12 | );
13 |
14 | // go to advanced search on mobile
15 | if (window.location.pathname.startsWith('/trademarks/search/quick')) {
16 | await processNode('a#goToAdvancedSearch', node => node.click());
17 | }
18 |
19 | await findNode('.advanced-search div#sideImageUpload');
20 |
21 | const inputSelector = 'input.dz-hidden-input';
22 | const input = await findNode(inputSelector);
23 |
24 | await setFileInputData(inputSelector, input, image);
25 |
26 | input.dispatchEvent(new Event('change'));
27 |
28 | await findNode('div.cropper-container');
29 |
30 | await sendReceipt(storageIds);
31 |
32 | (
33 | await findNode('#qa-search-submit:not(.disabled)', {
34 | observerOptions: {attributes: true, attributeFilter: ['class']}
35 | })
36 | ).click();
37 | }
38 |
39 | function init() {
40 | initSearch(search, engine, taskId);
41 | }
42 |
43 | if (runOnce('search')) {
44 | init();
45 | }
46 |
--------------------------------------------------------------------------------
/src/engines/baidu.js:
--------------------------------------------------------------------------------
1 | import {validateUrl} from 'utils/app';
2 | import {findNode, isMobile, runOnce} from 'utils/common';
3 | import {
4 | initSearch,
5 | prepareImageForUpload,
6 | setFileInputData,
7 | sendReceipt
8 | } from 'utils/engines';
9 |
10 | const engine = 'baidu';
11 |
12 | async function search({session, search, image, storageIds}) {
13 | const mobile = await isMobile();
14 |
15 | image = await prepareImageForUpload({
16 | image,
17 | engine,
18 | target: mobile ? 'api' : 'ui'
19 | });
20 |
21 | if (mobile) {
22 | const data = new FormData();
23 | data.append('tn', 'pc');
24 | data.append('from', 'pc');
25 | data.append('range', '{"page_from": "searchIndex"}');
26 |
27 | if (search.assetType === 'image') {
28 | data.append('image', image.imageBlob, image.imageFilename);
29 | data.append('image_source', 'PC_UPLOAD_SEARCH_FILE');
30 | } else {
31 | data.append('image', image.imageUrl);
32 | data.append('image_source', 'PC_UPLOAD_SEARCH_URL');
33 | }
34 |
35 | const rsp = await fetch('https://graph.baidu.com/upload', {
36 | mode: 'cors',
37 | method: 'POST',
38 | body: data
39 | });
40 |
41 | if (rsp.status !== 200) {
42 | throw new Error(`API response: ${rsp.status}, ${await rsp.text()}`);
43 | }
44 |
45 | const tabUrl = (await rsp.json()).data.url;
46 |
47 | await sendReceipt(storageIds);
48 |
49 | if (validateUrl(tabUrl)) {
50 | window.location.replace(tabUrl);
51 | }
52 | } else {
53 | (await findNode('.soutu-btn', {timeout: 120000})).click();
54 |
55 | if (search.assetType === 'image') {
56 | const inputSelector = 'input.upload-pic';
57 | const input = await findNode(inputSelector);
58 |
59 | await setFileInputData(inputSelector, input, image);
60 |
61 | await sendReceipt(storageIds);
62 |
63 | input.dispatchEvent(new Event('change'));
64 | } else {
65 | const input = await findNode('input#soutu-url-kw');
66 | input.value = image.imageUrl;
67 |
68 | await sendReceipt(storageIds);
69 |
70 | (await findNode('.soutu-url-btn')).click();
71 | }
72 | }
73 | }
74 |
75 | function init() {
76 | initSearch(search, engine, taskId);
77 | }
78 |
79 | if (runOnce('search')) {
80 | init();
81 | }
82 |
--------------------------------------------------------------------------------
/src/engines/bing.js:
--------------------------------------------------------------------------------
1 | import {findNode, isMobile, runOnce} from 'utils/common';
2 | import {
3 | initSearch,
4 | prepareImageForUpload,
5 | setFileInputData,
6 | sendReceipt
7 | } from 'utils/engines';
8 |
9 | const engine = 'bing';
10 |
11 | async function search({session, search, image, storageIds}) {
12 | const mobile = await isMobile();
13 |
14 | image = await prepareImageForUpload({
15 | image,
16 | engine,
17 | target: mobile ? 'api' : 'ui',
18 | newType: 'image/jpeg'
19 | });
20 |
21 | if (mobile) {
22 | const form = document.createElement('form');
23 | form.setAttribute('data-c45ng3u9', '');
24 | form.id = 'sbi-upload-form';
25 | form.method = 'POST';
26 | form.enctype = 'multipart/form-data';
27 | form.action = `https://www.bing.com/images/search?view=detailv2&iss=sbiupload&FORM=SBIHMP&sbifnm=${image.imageFilename}`;
28 |
29 | const input = document.createElement('input');
30 | input.setAttribute('data-c45ng3u9', '');
31 | input.name = 'imageBin';
32 | input.type = 'hidden';
33 |
34 | form.appendChild(input);
35 | document.body.appendChild(form);
36 |
37 | await sendReceipt(storageIds);
38 |
39 | input.value = image.imageDataUrl.substring(
40 | image.imageDataUrl.indexOf(',') + 1
41 | );
42 |
43 | form.submit();
44 | } else {
45 | (await findNode('#sb_sbi')).click();
46 |
47 | const inputSelector = 'input#sb_fileinput';
48 | const input = await findNode(inputSelector);
49 |
50 | await setFileInputData(inputSelector, input, image);
51 |
52 | await sendReceipt(storageIds);
53 |
54 | input.dispatchEvent(new Event('change'));
55 | }
56 | }
57 |
58 | function init() {
59 | initSearch(search, engine, taskId);
60 | }
61 |
62 | if (runOnce('search')) {
63 | init();
64 | }
65 |
--------------------------------------------------------------------------------
/src/engines/branddb.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'branddb';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | await findNode('body[style^="opacity: 1"]', {
8 | observerOptions: {attributes: true, attributeFilter: ['class']}
9 | });
10 |
11 | const inputSelector = 'input#fileInput';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | input.dispatchEvent(new Event('change'));
17 |
18 | // wait for image to load
19 | await findNode('.b-icon--edit');
20 |
21 | await sendReceipt(storageIds);
22 |
23 | window.setTimeout(async () => {
24 | (
25 | await findNode(
26 | 'button.search.b-button--is-type_primary:not(.b-button--is-disabled)'
27 | )
28 | ).click();
29 | }, 100);
30 | }
31 |
32 | function init() {
33 | initSearch(search, engine, taskId);
34 | }
35 |
36 | if (runOnce('search')) {
37 | init();
38 | }
39 |
--------------------------------------------------------------------------------
/src/engines/css/bing.css:
--------------------------------------------------------------------------------
1 | body > form#sbi-upload-form[data-c45ng3u9],
2 | body > form#sbi-upload-form[data-c45ng3u9] > input[data-c45ng3u9] {
3 | all: initial !important;
4 | display: none !important;
5 | }
6 |
--------------------------------------------------------------------------------
/src/engines/depositphotos.js:
--------------------------------------------------------------------------------
1 | import {findNode, makeDocumentVisible, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'depositphotos';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (
8 | await findNode(
9 | 'body:not(.preload) button[data-qa="SearchByImageButtonV2"]',
10 | {
11 | observerOptions: {attributes: true, attributeFilter: ['class']}
12 | }
13 | )
14 | ).click();
15 |
16 | const inputSelector = 'input[type=file]';
17 | const input = await findNode(inputSelector);
18 |
19 | await setFileInputData(inputSelector, input, image);
20 |
21 | await sendReceipt(storageIds);
22 |
23 | input.dispatchEvent(new Event('change', {bubbles: true}));
24 | }
25 |
26 | function init() {
27 | makeDocumentVisible();
28 | initSearch(search, engine, taskId);
29 | }
30 |
31 | if (runOnce('search')) {
32 | init();
33 | }
34 |
--------------------------------------------------------------------------------
/src/engines/dreamstime.js:
--------------------------------------------------------------------------------
1 | import {
2 | findNode,
3 | processNode,
4 | isMobile,
5 | executeScriptMainContext,
6 | runOnce
7 | } from 'utils/common';
8 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
9 | import {targetEnv} from 'utils/config';
10 |
11 | const engine = 'dreamstime';
12 |
13 | async function search({session, search, image, storageIds}) {
14 | if (targetEnv === 'safari' && (await isMobile())) {
15 | // hide noncritical upload error
16 | executeScriptMainContext({func: 'hideAlert'});
17 | }
18 |
19 | const inputSelector = 'input[type="file"]';
20 |
21 | processNode(inputSelector, function (node) {
22 | node.addEventListener('click', ev => ev.preventDefault(), {
23 | capture: true,
24 | once: true
25 | });
26 | });
27 |
28 | (await findNode('button.search-by-image__btn')).click();
29 |
30 | const input = await findNode(inputSelector);
31 |
32 | await setFileInputData(inputSelector, input, image, {patchInput: true});
33 |
34 | await sendReceipt(storageIds);
35 |
36 | input.dispatchEvent(new Event('change'));
37 | }
38 |
39 | function init() {
40 | initSearch(search, engine, taskId);
41 | }
42 |
43 | if (runOnce('search')) {
44 | init();
45 | }
46 |
--------------------------------------------------------------------------------
/src/engines/esearch.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'esearch';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = 'input.fileUploader-basic';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | input.dispatchEvent(new Event('change'));
13 |
14 | await findNode('div.imageViewer');
15 |
16 | await sendReceipt(storageIds);
17 |
18 | (await findNode('#basicSearchBigButton')).click();
19 | }
20 |
21 | function init() {
22 | initSearch(search, engine, taskId);
23 | }
24 |
25 | if (runOnce('search')) {
26 | init();
27 | }
28 |
--------------------------------------------------------------------------------
/src/engines/freepik.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce, sleep} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'freepik';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (
8 | await findNode('form > div > button[data-state="closed"]:last-of-type')
9 | ).click();
10 |
11 | const inputSelector = 'input[type=file]';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | input.dispatchEvent(new Event('change', {bubbles: true}));
19 |
20 | await sleep(1000);
21 |
22 | (await findNode('.bg-surface-1 button[type=submit]')).click();
23 | }
24 |
25 | function init() {
26 | initSearch(search, engine, taskId);
27 | }
28 |
29 | if (runOnce('search')) {
30 | init();
31 | }
32 |
--------------------------------------------------------------------------------
/src/engines/getty.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce, sleep} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'getty';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // wait for search service to load
8 | await findNode('#onetrust-consent-sdk');
9 | await sleep(1000);
10 |
11 | (
12 | await Promise.race([
13 | findNode('button[class*="SearchByImageButton"]'),
14 | findNode('button[data-testid="search-by-image-button"]') // new layout
15 | ])
16 | ).click();
17 |
18 | const inputSelector = 'input[type=file]';
19 | const input = await findNode(inputSelector);
20 |
21 | await setFileInputData(inputSelector, input, image);
22 |
23 | await sendReceipt(storageIds);
24 |
25 | input.dispatchEvent(new Event('change', {bubbles: true}));
26 | }
27 |
28 | function init() {
29 | initSearch(search, engine, taskId);
30 | }
31 |
32 | if (runOnce('search')) {
33 | init();
34 | }
35 |
--------------------------------------------------------------------------------
/src/engines/googleImages.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'googleImages';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | await sendReceipt(storageIds);
8 |
9 | const targetNode = await Promise.race([
10 | findNode('div#search', {timeout: 10000, throwError: false}), // desktop
11 | findNode('div#rso', {timeout: 10000, throwError: false}) // mobile
12 | ]);
13 |
14 | if (targetNode && !targetNode.children.length) {
15 | targetNode.style.margin = '6px';
16 | targetNode.style.padding = '16px';
17 | targetNode.style.border = '1px solid #fdd663';
18 | targetNode.style.borderRadius = '24px';
19 | targetNode.style.backgroundColor = '#feefc3';
20 |
21 | targetNode.style.fontSize = '13px';
22 | targetNode.style.fontWeight = '700';
23 | targetNode.style.lineHeight = '20px';
24 | targetNode.style.color = '#2d3436';
25 |
26 | targetNode.innerHTML = `Google has replaced the legacy reverse image search service with Google Lens.
27 | It may still be possible to view the legacy search results by updating your Google Account settings,
28 | visit our help page for more details.
29 | Search by Image supports a wide variety of image search services, including Google Lens.
30 | Visit the extension's options to enable additional search engines.`;
31 | }
32 | }
33 |
34 | function init() {
35 | initSearch(search, engine, taskId);
36 | }
37 |
38 | if (runOnce('search')) {
39 | init();
40 | }
41 |
--------------------------------------------------------------------------------
/src/engines/googleLens.js:
--------------------------------------------------------------------------------
1 | import {findNode, processNode, runOnce, sleep} from 'utils/common';
2 | import {
3 | setFileInputData,
4 | initSearch,
5 | sendReceipt,
6 | unsetUserAgent
7 | } from 'utils/engines';
8 |
9 | const engine = 'googleLens';
10 |
11 | async function search({session, search, image, storageIds}) {
12 | const inputSelector = 'input[type="file"]';
13 |
14 | async function clickButton() {
15 | await processNode('div[data-base-lens-url]', async function (node) {
16 | await sleep(1000);
17 |
18 | if (!document.querySelector(inputSelector)) {
19 | node.click();
20 | }
21 | });
22 | }
23 |
24 | // handle consent popup
25 | processNode(
26 | `//div[@role="dialog"
27 | and contains(., "g.co/privacytools")
28 | and .//a[starts-with(@href, "https://policies.google.com/technologies/cookies")]
29 | and .//a[starts-with(@href, "https://policies.google.com/privacy")]
30 | and .//a[starts-with(@href, "https://policies.google.com/terms")]
31 | ]`,
32 | function (node) {
33 | if (node) {
34 | node.querySelectorAll('button')[2].click();
35 |
36 | clickButton();
37 | }
38 | },
39 | {throwError: false, selectorType: 'xpath'}
40 | );
41 |
42 | await clickButton();
43 |
44 | await unsetUserAgent(storageIds);
45 |
46 | if (search.assetType === 'image') {
47 | const input = await findNode(inputSelector);
48 |
49 | await setFileInputData(inputSelector, input, image);
50 |
51 | await sendReceipt(storageIds);
52 |
53 | input.dispatchEvent(new Event('change'));
54 | } else {
55 | const input = await findNode(
56 | `//div[count(child::*)=2]/input[following-sibling::div[@role="button"]]`,
57 | {selectorType: 'xpath'}
58 | );
59 |
60 | input.value = image.imageUrl;
61 |
62 | await sendReceipt(storageIds);
63 |
64 | input.nextElementSibling.click();
65 | }
66 | }
67 |
68 | function init() {
69 | initSearch(search, engine, taskId);
70 | }
71 |
72 | if (runOnce('search')) {
73 | init();
74 | }
75 |
--------------------------------------------------------------------------------
/src/engines/icons8.js:
--------------------------------------------------------------------------------
1 | import {findNode, makeDocumentVisible, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'icons8';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('button.search-autocomplete__img-trigger')).click();
8 |
9 | const inputSelector = 'input#file-input';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | await sendReceipt(storageIds);
15 |
16 | input.dispatchEvent(new Event('input'));
17 | }
18 |
19 | function init() {
20 | makeDocumentVisible();
21 | initSearch(search, engine, taskId);
22 | }
23 |
24 | if (runOnce('search')) {
25 | init();
26 | }
27 |
--------------------------------------------------------------------------------
/src/engines/ikea.js:
--------------------------------------------------------------------------------
1 | import {
2 | findNode,
3 | processNode,
4 | makeDocumentVisible,
5 | runOnce
6 | } from 'utils/common';
7 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
8 |
9 | const engine = 'ikea';
10 |
11 | async function search({session, search, image, storageIds}) {
12 | // go to regional site
13 | processNode('.region-picker a.website-link', node => node.click(), {
14 | throwError: false
15 | });
16 |
17 | (await findNode('#search-box__visualsearch')).click();
18 |
19 | const inputSelector = 'input[type=file]';
20 | const input = await findNode(inputSelector);
21 |
22 | await setFileInputData(inputSelector, input, image);
23 |
24 | await sendReceipt(storageIds);
25 |
26 | input.dispatchEvent(new Event('change'));
27 | }
28 |
29 | function init() {
30 | makeDocumentVisible();
31 | initSearch(search, engine, taskId);
32 | }
33 |
34 | if (runOnce('search')) {
35 | init();
36 | }
37 |
--------------------------------------------------------------------------------
/src/engines/iqdb.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'iqdb';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = '#file';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | await sendReceipt(storageIds);
13 |
14 | (await findNode('form')).submit();
15 | }
16 |
17 | function init() {
18 | initSearch(search, engine, taskId);
19 | }
20 |
21 | if (runOnce('search')) {
22 | init();
23 | }
24 |
--------------------------------------------------------------------------------
/src/engines/istock.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce, sleep} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'istock';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // wait for search service to load
8 | await findNode('#onetrust-consent-sdk');
9 | await sleep(1000);
10 |
11 | (
12 | await Promise.race([
13 | findNode('button[class*="SearchByImageButton"]'),
14 | findNode('button[data-testid="search-by-image-button"]') // new layout
15 | ])
16 | ).click();
17 |
18 | const inputSelector = 'input[type=file]';
19 | const input = await findNode(inputSelector);
20 |
21 | await setFileInputData(inputSelector, input, image);
22 |
23 | await sendReceipt(storageIds);
24 |
25 | input.dispatchEvent(new Event('change', {bubbles: true}));
26 | }
27 |
28 | function init() {
29 | initSearch(search, engine, taskId);
30 | }
31 |
32 | if (runOnce('search')) {
33 | init();
34 | }
35 |
--------------------------------------------------------------------------------
/src/engines/jpDesign.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'jpDesign';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // wait for page
8 | await findNode('#photo > img');
9 |
10 | const inputSelector = '#ImageFile';
11 | const input = await findNode(inputSelector);
12 |
13 | await setFileInputData(inputSelector, input, image);
14 |
15 | input.dispatchEvent(new Event('change'));
16 |
17 | await findNode('#photo_image');
18 |
19 | await sendReceipt(storageIds);
20 |
21 | (await findNode('#searchForm')).removeAttribute('target');
22 | (await findNode('.action input[type=submit]')).click();
23 | }
24 |
25 | function init() {
26 | initSearch(search, engine, taskId);
27 | }
28 |
29 | if (runOnce('search')) {
30 | init();
31 | }
32 |
--------------------------------------------------------------------------------
/src/engines/kagi.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {initSearch, setFileInputData, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'kagi';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('.img_search_state_checkbox_label')).click();
8 |
9 | if (search.assetType === 'image') {
10 | const inputSelector = 'input[type="file"]';
11 | const input = await findNode(inputSelector);
12 |
13 | await setFileInputData(inputSelector, input, image);
14 |
15 | await sendReceipt(storageIds);
16 |
17 | input.dispatchEvent(new Event('change'));
18 |
19 | // Upload image on mobile
20 | const button = document.querySelector(
21 | '.iu_form_box form:not([hidden]) button[type="submit"]'
22 | );
23 | if (button) {
24 | function submit() {
25 | button.click();
26 | }
27 |
28 | const intervalId = window.setInterval(submit, 2000);
29 | window.setTimeout(function () {
30 | window.clearInterval(intervalId);
31 | }, 10000);
32 |
33 | window.setTimeout(submit, 600);
34 | }
35 | } else {
36 | const input = await findNode('.iu_url_search_box form input#url');
37 |
38 | input.value = image.imageUrl;
39 |
40 | await sendReceipt(storageIds);
41 |
42 | (await findNode('.iu_url_search_box form button')).click();
43 | }
44 | }
45 |
46 | function init() {
47 | initSearch(search, engine, taskId);
48 | }
49 |
50 | if (runOnce('search')) {
51 | init();
52 | }
53 |
--------------------------------------------------------------------------------
/src/engines/lenso.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'lenso';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | if (search.assetType === 'image') {
8 | const inputSelector = 'input[type=file]';
9 | const input = await findNode(inputSelector);
10 |
11 | await setFileInputData(inputSelector, input, image);
12 |
13 | await sendReceipt(storageIds);
14 |
15 | input.dispatchEvent(new Event('change'));
16 |
17 | const modal = await findNode('.manage-consents-modal', {
18 | timeout: 10000,
19 | throwError: false
20 | });
21 |
22 | if (modal) {
23 | (
24 | await findNode('label[for="privacy-policy"]', {rootNode: modal})
25 | ).click();
26 |
27 | (
28 | await findNode('button.perfom-search-btn:not([disabled])', {
29 | rootNode: modal,
30 | observerOptions: {attributes: true}
31 | })
32 | ).click();
33 | }
34 | } else {
35 | await sendReceipt(storageIds);
36 |
37 | (
38 | await findNode('.search-by-url .cta-btn', {
39 | timeout: 10000,
40 | throwError: false
41 | })
42 | ).click();
43 | }
44 | }
45 |
46 | function init() {
47 | initSearch(search, engine, taskId);
48 | }
49 |
50 | if (runOnce('search')) {
51 | init();
52 | }
53 |
--------------------------------------------------------------------------------
/src/engines/lexica.js:
--------------------------------------------------------------------------------
1 | import {findNode, executeScriptMainContext, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'lexica';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | await executeScriptMainContext({func: 'lexicaOverrideEventDispatch'});
8 |
9 | (await findNode('input#main-search')).nextElementSibling.click();
10 |
11 | const inputSelector = 'input[type="file"]';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | input.dispatchEvent(new Event('change'));
19 | }
20 |
21 | function init() {
22 | initSearch(search, engine, taskId);
23 | }
24 |
25 | if (runOnce('search')) {
26 | init();
27 | }
28 |
--------------------------------------------------------------------------------
/src/engines/lykdat.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'lykdat';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = '.top-search-image-box input[type=file]';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | await sendReceipt(storageIds);
13 |
14 | input.dispatchEvent(new Event('change', {bubbles: true}));
15 |
16 | (await findNode('.finisher button')).click();
17 | }
18 |
19 | function init() {
20 | initSearch(search, engine, taskId);
21 | }
22 |
23 | if (runOnce('search')) {
24 | init();
25 | }
26 |
--------------------------------------------------------------------------------
/src/engines/madridMonitor.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 | import {targetEnv} from 'utils/config';
4 |
5 | const engine = 'madridMonitor';
6 |
7 | async function search({session, search, image, storageIds}) {
8 | (await findNode('#imageModeLink')).click();
9 |
10 | await findNode('.fileTarget-open');
11 |
12 | const inputSelector = 'input#imageFileUpload';
13 | const input = await findNode(inputSelector);
14 |
15 | await setFileInputData(inputSelector, input, image, {
16 | patchInput: targetEnv === 'safari'
17 | });
18 |
19 | input.dispatchEvent(new Event('change'));
20 |
21 | // wait for image to load
22 | await findNode('.ui-icon-pencil');
23 |
24 | // select Concept strategy
25 | (await findNode('a[data-hasqtip="82"]')).click();
26 |
27 | // deselect all image types
28 | (await findNode('a[data-hasqtip="88"]')).click();
29 | (await findNode('a[data-hasqtip="89"]')).click();
30 |
31 | await sendReceipt(storageIds);
32 |
33 | window.setTimeout(async () => {
34 | (
35 | await findNode('#image_search_container .searchButtonContainer a')
36 | ).click();
37 | }, 100);
38 | }
39 |
40 | function init() {
41 | initSearch(search, engine, taskId);
42 | }
43 |
44 | if (runOnce('search')) {
45 | init();
46 | }
47 |
--------------------------------------------------------------------------------
/src/engines/nzTrademark.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'nzTrademark';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('#logoCheckButton')).click();
8 |
9 | const inputSelector = '#imageSearchDialogUploadButton';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | input.dispatchEvent(new Event('change'));
15 |
16 | await sendReceipt(storageIds);
17 |
18 | (
19 | await findNode('#imageSearchDialogNextButton:not([disabled])', {
20 | observerOptions: {attributes: true, attributeFilter: ['disabled']}
21 | })
22 | ).click();
23 |
24 | const features = await findNode(
25 | '#imageSearchDialogMainStep1_2:not(.hidden)',
26 | {
27 | timeout: 30000,
28 | throwError: false,
29 | observerOptions: {attributes: true, attributeFilter: ['class']}
30 | }
31 | );
32 | if (features) {
33 | (await findNode('#imageSearchDialogSkipButton')).click();
34 | }
35 | }
36 |
37 | function init() {
38 | initSearch(search, engine, taskId);
39 | }
40 |
41 | if (runOnce('search')) {
42 | init();
43 | }
44 |
--------------------------------------------------------------------------------
/src/engines/pimeyes.js:
--------------------------------------------------------------------------------
1 | import {findNode, processNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'pimeyes';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = '.upload-file input#file-input';
8 |
9 | processNode(inputSelector, function (node) {
10 | node.addEventListener('click', ev => ev.preventDefault(), {
11 | capture: true,
12 | once: true
13 | });
14 | });
15 |
16 | (await findNode('.upload-bar button[aria-label="upload photo" i]')).click();
17 |
18 | const input = await findNode(inputSelector);
19 |
20 | await setFileInputData(inputSelector, input, image);
21 |
22 | await sendReceipt(storageIds);
23 |
24 | input.dispatchEvent(new Event('change'));
25 |
26 | const searchButton = await findNode('.start-search-inner > button', {
27 | throwError: false
28 | });
29 |
30 | // button is missing when no faces were detected
31 | if (searchButton) {
32 | if (searchButton.classList.contains('disabled')) {
33 | await findNode('.permissions input[type=checkbox]');
34 |
35 | for (const checkbox of document.querySelectorAll(
36 | '.permissions input[type=checkbox]'
37 | )) {
38 | if (!checkbox.checked) {
39 | checkbox.click();
40 | }
41 | }
42 |
43 | (
44 | await findNode('.start-search-inner > button:not(.disabled)', {
45 | observerOptions: {attributes: true, attributeFilter: ['class']}
46 | })
47 | ).click();
48 | } else {
49 | searchButton.click();
50 | }
51 | }
52 | }
53 |
54 | function init() {
55 | initSearch(search, engine, taskId);
56 | }
57 |
58 | if (runOnce('search')) {
59 | init();
60 | }
61 |
--------------------------------------------------------------------------------
/src/engines/pixta.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'pixta';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const mobileLayout = window.matchMedia('(max-width: 768px)').matches;
8 |
9 | if (mobileLayout) {
10 | (await findNode('div.global-header-sp i.fa-search')).click();
11 |
12 | (
13 | await findNode('.global-search-form-sp__search-by-image-btn i.fa-camera')
14 | ).click();
15 | } else {
16 | (
17 | await findNode('div.search-image-button.search-image-button--top')
18 | ).click();
19 | }
20 |
21 | const inputSelector = mobileLayout
22 | ? 'input#upload-photo[type="file"]'
23 | : 'input#image[type="file"]';
24 | const input = await findNode(inputSelector);
25 |
26 | await setFileInputData(inputSelector, input, image);
27 |
28 | await sendReceipt(storageIds);
29 |
30 | input.dispatchEvent(new Event('change'));
31 | }
32 |
33 | function init() {
34 | initSearch(search, engine, taskId);
35 | }
36 |
37 | if (runOnce('search')) {
38 | init();
39 | }
40 |
--------------------------------------------------------------------------------
/src/engines/pond5.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'pond5';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (
8 | await findNode('div#main form.SiteSearch button.js-reverseSearchInputIcon')
9 | ).click();
10 |
11 | const inputSelector = 'input#vissimFileSelector';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | input.dispatchEvent(new Event('change', {bubbles: true}));
19 | }
20 |
21 | function init() {
22 | initSearch(search, engine, taskId);
23 | }
24 |
25 | if (runOnce('search')) {
26 | init();
27 | }
28 |
--------------------------------------------------------------------------------
/src/engines/qihoo.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'qihoo';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | let inputSelector;
8 | let input;
9 | if (document.head.querySelector('meta[name^="apple-mobile"]')) {
10 | // mobile
11 | inputSelector = '.g-header-st-form input[type="file"]';
12 | input = await findNode(inputSelector, {timeout: 120000});
13 | } else {
14 | // desktop
15 | inputSelector = 'input#stUpload';
16 | input = await findNode(inputSelector);
17 | }
18 |
19 | await setFileInputData(inputSelector, input, image);
20 |
21 | await sendReceipt(storageIds);
22 |
23 | input.dispatchEvent(new Event('change'));
24 | }
25 |
26 | function init() {
27 | initSearch(search, engine, taskId);
28 | }
29 |
30 | if (runOnce('search')) {
31 | init();
32 | }
33 |
--------------------------------------------------------------------------------
/src/engines/repostSleuth.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'repostSleuth';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | if (search.assetType === 'image') {
8 | const inputSelector = 'input[type=file]';
9 | const input = await findNode(inputSelector);
10 |
11 | await setFileInputData(inputSelector, input, image);
12 |
13 | await sendReceipt(storageIds);
14 |
15 | input.dispatchEvent(new Event('change'));
16 | } else {
17 | const input = await findNode(
18 | '//input[preceding-sibling::label[contains(., "Image URL")]]',
19 | {selectorType: 'xpath'}
20 | );
21 |
22 | input.value = image.imageUrl;
23 |
24 | await sendReceipt(storageIds);
25 |
26 | input.dispatchEvent(new Event('input'));
27 | }
28 |
29 | const button = await findNode(
30 | '.v-main button.primary:not(.v-btn--disabled)',
31 | {observerOptions: {attributes: true, attributeFilter: ['class']}}
32 | );
33 |
34 | window.setTimeout(() => button.click(), 300);
35 | }
36 |
37 | function init() {
38 | initSearch(search, engine, taskId);
39 | }
40 |
41 | if (runOnce('search')) {
42 | init();
43 | }
44 |
--------------------------------------------------------------------------------
/src/engines/saucenao.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'saucenao';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const autoSubmit = await findNode('input#auto-cb');
8 | if (!autoSubmit.checked) {
9 | autoSubmit.click();
10 | }
11 |
12 | if (search.assetType === 'image') {
13 | const inputSelector = 'input#fileInput';
14 | const input = await findNode(inputSelector);
15 |
16 | await setFileInputData(inputSelector, input, image);
17 |
18 | await sendReceipt(storageIds);
19 |
20 | input.dispatchEvent(new Event('change'));
21 | } else {
22 | const input = await findNode('input#urlInput');
23 |
24 | await sendReceipt(storageIds);
25 |
26 | input.value = image.imageUrl;
27 |
28 | // input.blur() is only dispatched when the tab is active
29 | input.dispatchEvent(new Event('blur'));
30 | }
31 | }
32 |
33 | function init() {
34 | initSearch(search, engine, taskId);
35 | }
36 |
37 | if (runOnce('search')) {
38 | init();
39 | }
40 |
--------------------------------------------------------------------------------
/src/engines/shein.js:
--------------------------------------------------------------------------------
1 | import {findNode, makeDocumentVisible, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'shein';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('.search-input_upload span.sui-icon-common__wrap')).click();
8 |
9 | const inputSelector = '.search-upload input[type="file"]';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | await sendReceipt(storageIds);
15 |
16 | input.dispatchEvent(new Event('change'));
17 | }
18 |
19 | function init() {
20 | makeDocumentVisible();
21 | initSearch(search, engine, taskId);
22 | }
23 |
24 | if (runOnce('search')) {
25 | init();
26 | }
27 |
--------------------------------------------------------------------------------
/src/engines/shutterstock.js:
--------------------------------------------------------------------------------
1 | import {findNode, isMobile, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'shutterstock';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | if (await isMobile()) {
8 | // some elements are loaded only after the first user interaction
9 | window.dispatchEvent(new Event('touchstart'));
10 | }
11 |
12 | (await findNode('.MuiSelect-select[aria-label*="image"]')).dispatchEvent(
13 | new MouseEvent('mousedown', {bubbles: true})
14 | );
15 |
16 | (await findNode('li[data-value="reverseImageSearch"]')).click();
17 |
18 | const inputSelector = 'input[type="file"]';
19 | const input = await findNode(inputSelector);
20 |
21 | await setFileInputData(inputSelector, input, image);
22 |
23 | await sendReceipt(storageIds);
24 |
25 | input.dispatchEvent(new Event('change', {bubbles: true}));
26 | }
27 |
28 | function init() {
29 | initSearch(search, engine, taskId);
30 | }
31 |
32 | if (runOnce('search')) {
33 | init();
34 | }
35 |
--------------------------------------------------------------------------------
/src/engines/sogou.js:
--------------------------------------------------------------------------------
1 | import {findNode, isMobile, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'sogou';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | if (!(await isMobile())) {
8 | (await findNode('a#cameraIco', {timeout: 120000})).click();
9 | }
10 |
11 | const inputSelector = 'input[type="file"]';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | input.dispatchEvent(new Event('change'));
19 | }
20 |
21 | function init() {
22 | initSearch(search, engine, taskId);
23 | }
24 |
25 | if (runOnce('search')) {
26 | init();
27 | }
28 |
--------------------------------------------------------------------------------
/src/engines/stocksy.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'stocksy';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | (await findNode('button[name="triggerVisualSearch"]')).click();
8 |
9 | const inputSelector = 'input#vs-file';
10 | const input = await findNode(inputSelector);
11 |
12 | await setFileInputData(inputSelector, input, image);
13 |
14 | await sendReceipt(storageIds);
15 |
16 | input.dispatchEvent(new Event('change', {bubbles: true}));
17 | }
18 |
19 | function init() {
20 | initSearch(search, engine, taskId);
21 | }
22 |
23 | if (runOnce('search')) {
24 | init();
25 | }
26 |
--------------------------------------------------------------------------------
/src/engines/taobao.js:
--------------------------------------------------------------------------------
1 | import {findNode, executeScriptMainContext, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'taobao';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | await executeScriptMainContext({func: 'taobaoPatchContext'});
8 |
9 | (await findNode('div.image-search-icon-wrapper', {timeout: 120000})).click();
10 |
11 | const inputSelector = 'input[type="file"]';
12 | const input = await findNode(inputSelector);
13 |
14 | await setFileInputData(inputSelector, input, image);
15 |
16 | await sendReceipt(storageIds);
17 |
18 | window.setTimeout(() => {
19 | input.dispatchEvent(new Event('change', {bubbles: true}));
20 | }, 100);
21 |
22 | (
23 | await findNode('div#image-search-upload-button.upload-button-active', {
24 | observerOptions: {attributes: true, attributeFilter: ['class']}
25 | })
26 | ).click();
27 | }
28 |
29 | function init() {
30 | initSearch(search, engine, taskId);
31 | }
32 |
33 | if (runOnce('search')) {
34 | init();
35 | }
36 |
--------------------------------------------------------------------------------
/src/engines/tineye.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'tineye';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = 'input#upload-box';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | await sendReceipt(storageIds);
13 |
14 | input.dispatchEvent(new Event('change'));
15 | }
16 |
17 | function init() {
18 | // skip Cloudflare challenge
19 | if (
20 | !document
21 | .querySelector('noscript')
22 | .textContent.includes('')
23 | ) {
24 | initSearch(search, engine, taskId);
25 | }
26 | }
27 |
28 | if (runOnce('search')) {
29 | init();
30 | }
31 |
--------------------------------------------------------------------------------
/src/engines/tmview.js:
--------------------------------------------------------------------------------
1 | import {findNode, processNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'tmview';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // previous search may be cached
8 | let removeImage = true;
9 | processNode(
10 | '.image-remove button',
11 | function (node) {
12 | if (node && removeImage) {
13 | node.click();
14 | }
15 | },
16 | {throwError: false}
17 | );
18 |
19 | const inputSelector = 'input[type=file]';
20 | const input = await findNode(inputSelector);
21 |
22 | removeImage = false;
23 | await setFileInputData(inputSelector, input, image);
24 |
25 | input.dispatchEvent(new Event('change', {bubbles: true}));
26 |
27 | await findNode('.image-remove button');
28 |
29 | await sendReceipt(storageIds);
30 |
31 | (await findNode('button[data-test-id=search-button]')).click();
32 | }
33 |
34 | function init() {
35 | initSearch(search, engine, taskId);
36 | }
37 |
38 | if (runOnce('search')) {
39 | init();
40 | }
41 |
--------------------------------------------------------------------------------
/src/engines/whatanime.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'whatanime';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | const inputSelector = 'input[type=file]';
8 | const input = await findNode(inputSelector);
9 |
10 | await setFileInputData(inputSelector, input, image);
11 |
12 | await sendReceipt(storageIds);
13 |
14 | input.dispatchEvent(new Event('change', {bubbles: true}));
15 | }
16 |
17 | function init() {
18 | initSearch(search, engine, taskId);
19 | }
20 |
21 | if (runOnce('search')) {
22 | init();
23 | }
24 |
--------------------------------------------------------------------------------
/src/engines/wildberries.js:
--------------------------------------------------------------------------------
1 | import {findNode, runOnce, sleep} from 'utils/common';
2 | import {setFileInputData, initSearch, sendReceipt} from 'utils/engines';
3 |
4 | const engine = 'wildberries';
5 |
6 | async function search({session, search, image, storageIds}) {
7 | // wait for search service to load
8 | await sleep(1000);
9 |
10 | const inputSelector = 'input[type=file][data-link*="searchByImage"]';
11 | const input = await findNode(inputSelector);
12 |
13 | await setFileInputData(inputSelector, input, image);
14 |
15 | await sendReceipt(storageIds);
16 |
17 | input.dispatchEvent(new Event('change'));
18 | }
19 |
20 | function init() {
21 | initSearch(search, engine, taskId);
22 | }
23 |
24 | if (runOnce('search')) {
25 | init();
26 | }
27 |
--------------------------------------------------------------------------------
/src/engines/yandex.js:
--------------------------------------------------------------------------------
1 | import {validateUrl, getContentXHR} from 'utils/app';
2 | import {makeDocumentVisible, runOnce} from 'utils/common';
3 | import {
4 | initSearch,
5 | prepareImageForUpload,
6 | sendReceipt,
7 | getValidHostname,
8 | uploadCallback
9 | } from 'utils/engines';
10 |
11 | const engine = 'yandex';
12 |
13 | function showResults(xhr) {
14 | if (xhr.status === 413) {
15 | largeImageNotify(engine, '8');
16 | return;
17 | }
18 |
19 | const params = JSON.parse(xhr.responseText).blocks[0].params.url;
20 | const tabUrl = `https://${getValidHostname()}/images/search?${params}`;
21 |
22 | if (validateUrl(tabUrl)) {
23 | window.location.replace(tabUrl);
24 | }
25 | }
26 |
27 | async function searchApi({image, storageIds} = {}) {
28 | const hostname = getValidHostname();
29 | const url =
30 | `https://${hostname}/images/touch/search?rpt=imageview&format=json` +
31 | `&request={"blocks":[{"block":"cbir-uploader__get-cbir-id"}]}`;
32 |
33 | const data = new FormData();
34 | data.append('upfile', image.imageBlob);
35 |
36 | const xhr = getContentXHR();
37 | xhr.addEventListener('load', function () {
38 | sendReceipt(storageIds);
39 |
40 | uploadCallback(this, showResults, engine);
41 | });
42 | xhr.open('POST', url);
43 | xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
44 | xhr.setRequestHeader(
45 | 'Accept',
46 | 'application/json, text/javascript, */*; q=0.01'
47 | );
48 | xhr.send(data);
49 | }
50 |
51 | async function search({session, search, image, storageIds}) {
52 | image = await prepareImageForUpload({
53 | image,
54 | engine,
55 | target: 'api'
56 | });
57 |
58 | await searchApi({image, storageIds});
59 | }
60 |
61 | function init() {
62 | makeDocumentVisible();
63 | if (!window.location.pathname.startsWith('/showcaptcha')) {
64 | initSearch(search, engine, taskId);
65 | }
66 | }
67 |
68 | if (runOnce('search')) {
69 | init();
70 | }
71 |
--------------------------------------------------------------------------------
/src/options/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/options/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/search/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/search/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/select/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/select/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------
/src/select/pointer.css:
--------------------------------------------------------------------------------
1 | video,
2 | audio {
3 | pointer-events: none !important;
4 | }
5 |
6 | @media (hover: hover) {
7 | * {
8 | cursor: pointer !important;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/storage/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "revisions": {
3 | "local": [
4 | "ryekyizAg",
5 | "Hy1tD8ANb",
6 | "S1kNNadHZ",
7 | "Bk42MzXdW",
8 | "HJ5MKKGhW",
9 | "ryY8H0EWf",
10 | "BJguWEHcbz",
11 | "S1ebtZ3Uzz",
12 | "BJXdcUXOG",
13 | "SklYTwQOYf",
14 | "S18hLi5tG",
15 | "SJzIWmjKz",
16 | "r1Pvd36nz",
17 | "r1H3rgx1X",
18 | "Syy800KkQ",
19 | "SkwaU8NlX",
20 | "yLciyvS5He",
21 | "ekhOvNiTsF",
22 | "ShjDMM87",
23 | "LX20x6G8l",
24 | "d8CIdomCW",
25 | "d8IK4KCtVm",
26 | "UhWEtK9gMh",
27 | "TshBYj8anA",
28 | "ggrr9C9pgV",
29 | "IMMjiccGj",
30 | "K9Esw2jiQ3",
31 | "20210820184257_support_event_pages",
32 | "20210919175209_add_image_sharing_options",
33 | "20210928090443_add_unsplash",
34 | "20211011114043_configure_engines",
35 | "20211106103932_add_shein",
36 | "20211111071410_add_lykdat",
37 | "20211204124507_add_wildberries",
38 | "20211213014048_add_lastengineaccesscheck",
39 | "20211213191049_add_setcontextmenuevent",
40 | "20220108063511_add_google_lens",
41 | "20220321163741_add_clipboard_support",
42 | "20220505174634_detect_alternative_image_sizes",
43 | "20220516051842_update_search_mode_ids",
44 | "20220516124148_open_images",
45 | "20220814142801_configure_engines",
46 | "20220924083846_add_lexica",
47 | "20230415111029_add_theme_support",
48 | "20230531103023_remove_search_engines",
49 | "20230601115626_add_kagi",
50 | "20230624145626_add_search_engines",
51 | "20230716121432_add_freepik",
52 | "20231009185955_remove_unsplash",
53 | "20231102164602_add_icons8",
54 | "20240514170322_add_appversion",
55 | "20240529183556_update_search_engines",
56 | "20240619180111_add_menuchangeevent",
57 | "20240624161944_remove_search_engines",
58 | "20250102095603_add_lenso.ai",
59 | "20250218121640_remove_google_images",
60 | "20250317134215_add_back_google_images",
61 | "20250413115022_add_unsplash",
62 | "20250511143502_remove_karma_decay"
63 | ],
64 | "session": [
65 | "20240514122825_initial_version"
66 | ]
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/storage/init.js:
--------------------------------------------------------------------------------
1 | import {migrate} from 'wesa';
2 |
3 | import {isStorageArea} from './storage';
4 |
5 | async function initStorage({area = 'local', data = null, silent = false} = {}) {
6 | const context = {
7 | getAvailableRevisions: async ({area} = {}) =>
8 | (
9 | await import(/* webpackMode: "eager" */ 'storage/config.json', {
10 | with: {type: 'json'}
11 | })
12 | ).revisions[area],
13 | getCurrentRevision: async ({area} = {}) =>
14 | (await browser.storage[area].get('storageVersion')).storageVersion,
15 | getRevision: async ({area, revision} = {}) =>
16 | import(
17 | /* webpackMode: "eager" */ `storage/revisions/${area}/${revision}.js`
18 | )
19 | };
20 |
21 | if (area === 'local') {
22 | await migrateLegacyStorage();
23 | }
24 |
25 | return migrate(context, {area, data, silent});
26 | }
27 |
28 | async function migrateLegacyStorage() {
29 | if (await isStorageArea({area: 'sync'})) {
30 | const {storageVersion: syncVersion} =
31 | await browser.storage.sync.get('storageVersion');
32 | if (syncVersion && syncVersion.length < 14) {
33 | const {storageVersion: localVersion} =
34 | await browser.storage.local.get('storageVersion');
35 |
36 | if (!localVersion || localVersion.length < 14) {
37 | const syncData = await browser.storage.sync.get(null);
38 | await browser.storage.local.clear();
39 | await browser.storage.local.set(syncData);
40 | await browser.storage.sync.clear();
41 | }
42 | }
43 | }
44 | }
45 |
46 | export {initStorage};
47 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20210820184257_support_event_pages.js:
--------------------------------------------------------------------------------
1 | const message = 'Support event pages';
2 |
3 | const revision = '20210820184257_support_event_pages';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 |
8 | changes.taskRegistry = {lastTaskStart: 0, tabs: {}, tasks: {}};
9 | changes.storageRegistry = {};
10 | changes.lastStorageCleanup = 0;
11 |
12 | changes.storageVersion = revision;
13 | return browser.storage.local.set(changes);
14 | }
15 |
16 | export {message, revision, upgrade};
17 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20210919175209_add_image_sharing_options.js:
--------------------------------------------------------------------------------
1 | const message = 'Add image sharing options';
2 |
3 | const revision = '20210919175209_add_image_sharing_options';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | shareImageContextMenu: true,
8 | shareImageAction: true,
9 | convertSharedImage: true
10 | };
11 |
12 | changes.storageVersion = revision;
13 | return browser.storage.local.set(changes);
14 | }
15 |
16 | export {message, revision, upgrade};
17 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20210928090443_add_unsplash.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Unsplash';
2 |
3 | const revision = '20210928090443_add_unsplash';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['unsplash'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211011114043_configure_engines.js:
--------------------------------------------------------------------------------
1 | import {targetEnv} from 'utils/config';
2 |
3 | const message = 'Configure engines';
4 |
5 | const revision = '20211011114043_configure_engines';
6 |
7 | async function upgrade() {
8 | const changes = {};
9 |
10 | if (targetEnv === 'safari') {
11 | const {engines, disabledEngines} = await browser.storage.local.get([
12 | 'engines',
13 | 'disabledEngines'
14 | ]);
15 |
16 | const restoreEngines = ['iqdb', 'jpDesign'];
17 |
18 | changes.engines = engines.concat(restoreEngines);
19 | changes.disabledEngines = disabledEngines.concat(restoreEngines);
20 | }
21 |
22 | changes.storageVersion = revision;
23 | return browser.storage.local.set(changes);
24 | }
25 |
26 | export {message, revision, upgrade};
27 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211106103932_add_shein.js:
--------------------------------------------------------------------------------
1 | const message = 'Add SHEIN';
2 |
3 | const revision = '20211106103932_add_shein';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['shein'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211111071410_add_lykdat.js:
--------------------------------------------------------------------------------
1 | const message = 'Add LykDat';
2 |
3 | const revision = '20211111071410_add_lykdat';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['lykdat'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211204124507_add_wildberries.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Wildberries';
2 |
3 | const revision = '20211204124507_add_wildberries';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['wildberries'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211213014048_add_lastengineaccesscheck.js:
--------------------------------------------------------------------------------
1 | const message = 'Add lastEngineAccessCheck';
2 |
3 | const revision = '20211213014048_add_lastengineaccesscheck';
4 |
5 | async function upgrade() {
6 | const changes = {lastEngineAccessCheck: 0};
7 |
8 | changes.storageVersion = revision;
9 | return browser.storage.local.set(changes);
10 | }
11 |
12 | export {message, revision, upgrade};
13 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20211213191049_add_setcontextmenuevent.js:
--------------------------------------------------------------------------------
1 | const message = 'Add setContextMenuEvent';
2 |
3 | const revision = '20211213191049_add_setcontextmenuevent';
4 |
5 | async function upgrade() {
6 | const changes = {setContextMenuEvent: 0};
7 |
8 | changes.storageVersion = revision;
9 | return browser.storage.local.set(changes);
10 | }
11 |
12 | export {message, revision, upgrade};
13 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220108063511_add_google_lens.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from 'utils/common';
2 | import {targetEnv} from 'utils/config';
3 |
4 | const message = 'Add Google Lens';
5 |
6 | const revision = '20220108063511_add_google_lens';
7 |
8 | async function upgrade() {
9 | const changes = {};
10 | const {engines, disabledEngines} = await browser.storage.local.get([
11 | 'engines',
12 | 'disabledEngines'
13 | ]);
14 | if (!(targetEnv === 'safari' && (await isMobile()))) {
15 | const newEngines = ['googleLens'];
16 |
17 | if (targetEnv === 'samsung') {
18 | engines.push('mailru');
19 | disabledEngines.push('mailru');
20 | }
21 |
22 | changes.engines = engines.concat(newEngines);
23 | changes.disabledEngines = disabledEngines.concat(newEngines);
24 | }
25 |
26 | changes.storageVersion = revision;
27 | return browser.storage.local.set(changes);
28 | }
29 |
30 | export {message, revision, upgrade};
31 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220321163741_add_clipboard_support.js:
--------------------------------------------------------------------------------
1 | const message = 'Add clipboard support';
2 |
3 | const revision = '20220321163741_add_clipboard_support';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | autoPasteAction: true,
8 | confirmPaste: true
9 | };
10 |
11 | changes.storageVersion = revision;
12 | return browser.storage.local.set(changes);
13 | }
14 |
15 | export {message, revision, upgrade};
16 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220505174634_detect_alternative_image_sizes.js:
--------------------------------------------------------------------------------
1 | const message = 'Detect alternative image sizes';
2 |
3 | const revision = '20220505174634_detect_alternative_image_sizes';
4 |
5 | async function upgrade() {
6 | const changes = {detectAltImageDimension: false};
7 |
8 | changes.storageVersion = revision;
9 | return browser.storage.local.set(changes);
10 | }
11 |
12 | export {message, revision, upgrade};
13 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220516051842_update_search_mode_ids.js:
--------------------------------------------------------------------------------
1 | const message = 'Update search mode IDs';
2 |
3 | const revision = '20220516051842_update_search_mode_ids';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 |
8 | const {searchModeContextMenu, searchModeAction} =
9 | await browser.storage.local.get([
10 | 'searchModeContextMenu',
11 | 'searchModeAction'
12 | ]);
13 |
14 | const searchModes = {
15 | select: 'selectUrl',
16 | selectUpload: 'selectImage',
17 | upload: 'browse'
18 | };
19 |
20 | if (searchModes[searchModeContextMenu]) {
21 | changes.searchModeContextMenu = searchModes[searchModeContextMenu];
22 | }
23 | if (searchModes[searchModeAction]) {
24 | changes.searchModeAction = searchModes[searchModeAction];
25 | }
26 |
27 | changes.storageVersion = revision;
28 | return browser.storage.local.set(changes);
29 | }
30 |
31 | export {message, revision, upgrade};
32 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220516124148_open_images.js:
--------------------------------------------------------------------------------
1 | const message = 'Open images';
2 |
3 | const revision = '20220516124148_open_images';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | viewImageContextMenu: true,
8 | viewImageAction: true,
9 | viewImageUseViewer: true
10 | };
11 |
12 | changes.storageVersion = revision;
13 | return browser.storage.local.set(changes);
14 | }
15 |
16 | export {message, revision, upgrade};
17 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220814142801_configure_engines.js:
--------------------------------------------------------------------------------
1 | import {isMobile} from 'utils/common';
2 | import {targetEnv} from 'utils/config';
3 |
4 | const message = 'Configure engines';
5 |
6 | const revision = '20220814142801_configure_engines';
7 |
8 | async function upgrade() {
9 | const changes = {};
10 |
11 | if (targetEnv === 'safari' && (await isMobile())) {
12 | const {engines, disabledEngines} = await browser.storage.local.get([
13 | 'engines',
14 | 'disabledEngines'
15 | ]);
16 |
17 | const newEngines = ['googleLens'];
18 |
19 | changes.engines = engines.concat(newEngines);
20 | changes.disabledEngines = disabledEngines.concat(newEngines);
21 | }
22 |
23 | changes.storageVersion = revision;
24 | return browser.storage.local.set(changes);
25 | }
26 |
27 | export {message, revision, upgrade};
28 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20220924083846_add_lexica.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Lexica';
2 |
3 | const revision = '20220924083846_add_lexica';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['lexica'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20230415111029_add_theme_support.js:
--------------------------------------------------------------------------------
1 | import {getDayPrecisionEpoch} from 'utils/common';
2 |
3 | const message = 'Add theme support';
4 |
5 | const revision = '20230415111029_add_theme_support';
6 |
7 | async function upgrade() {
8 | const changes = {
9 | appTheme: 'auto', // auto, light, dark
10 | showContribPage: true,
11 | showEngineIcons: true,
12 | contribPageLastAutoOpen: 0,
13 | pinActionToolbarViewImage: true,
14 | pinActionToolbarShareImage: false,
15 | pinActionToolbarOptions: false,
16 | pinActionToolbarContribute: true
17 | };
18 |
19 | const {installTime, searchCount} = await browser.storage.local.get([
20 | 'installTime',
21 | 'searchCount'
22 | ]);
23 | changes.installTime = getDayPrecisionEpoch(installTime);
24 | changes.useCount = searchCount;
25 |
26 | await browser.storage.local.remove([
27 | 'searchCount',
28 | 'viewImageAction',
29 | 'shareImageAction'
30 | ]);
31 |
32 | changes.storageVersion = revision;
33 | return browser.storage.local.set(changes);
34 | }
35 |
36 | export {message, revision, upgrade};
37 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20230531103023_remove_search_engines.js:
--------------------------------------------------------------------------------
1 | const message = 'Remove search engines';
2 |
3 | const revision = '20230531103023_remove_search_engines';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | const removeEngines = ['jingdong', 'mailru'];
13 |
14 | changes.engines = engines.filter(function (item) {
15 | return !removeEngines.includes(item);
16 | });
17 | changes.disabledEngines = disabledEngines.filter(function (item) {
18 | return !removeEngines.includes(item);
19 | });
20 |
21 | changes.storageVersion = revision;
22 | return browser.storage.local.set(changes);
23 | }
24 |
25 | export {message, revision, upgrade};
26 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20230601115626_add_kagi.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Kagi';
2 |
3 | const revision = '20230601115626_add_kagi';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['kagi'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20230624145626_add_search_engines.js:
--------------------------------------------------------------------------------
1 | const message = 'Add search engines';
2 |
3 | const revision = '20230624145626_add_search_engines';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = [
12 | 'haveibeentrained',
13 | 'enterpix',
14 | 'immerse',
15 | 'clipretrieval'
16 | ];
17 |
18 | changes.engines = engines.concat(newEngines);
19 | changes.disabledEngines = disabledEngines.concat(newEngines);
20 |
21 | changes.storageVersion = revision;
22 | return browser.storage.local.set(changes);
23 | }
24 |
25 | export {message, revision, upgrade};
26 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20230716121432_add_freepik.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Freepik';
2 |
3 | const revision = '20230716121432_add_freepik';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['freepik'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20231009185955_remove_unsplash.js:
--------------------------------------------------------------------------------
1 | const message = 'Remove Unsplash';
2 |
3 | const revision = '20231009185955_remove_unsplash';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | const removeEngines = ['unsplash'];
13 |
14 | changes.engines = engines.filter(function (item) {
15 | return !removeEngines.includes(item);
16 | });
17 | changes.disabledEngines = disabledEngines.filter(function (item) {
18 | return !removeEngines.includes(item);
19 | });
20 |
21 | changes.storageVersion = revision;
22 | return browser.storage.local.set(changes);
23 | }
24 |
25 | export {message, revision, upgrade};
26 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20231102164602_add_icons8.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Icons8';
2 |
3 | const revision = '20231102164602_add_icons8';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['icons8'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20240514170322_add_appversion.js:
--------------------------------------------------------------------------------
1 | const message = 'Add appVersion';
2 |
3 | const revision = '20240514170322_add_appversion';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | appVersion: '',
8 | menuItems: [],
9 | privateMenuItems: []
10 | };
11 |
12 | changes.storageVersion = revision;
13 | return browser.storage.local.set(changes);
14 | }
15 |
16 | export {message, revision, upgrade};
17 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20240529183556_update_search_engines.js:
--------------------------------------------------------------------------------
1 | const message = 'Update search engines';
2 |
3 | const revision = '20240529183556_update_search_engines';
4 |
5 | async function upgrade(context) {
6 | const changes = {};
7 |
8 | const {engines, disabledEngines} = await browser.storage.local.get([
9 | 'engines',
10 | 'disabledEngines'
11 | ]);
12 |
13 | // Move Google Lens to the top
14 | engines.splice(0, 0, engines.splice(engines.indexOf('googleLens'), 1)[0]);
15 |
16 | // Enable Google Lens if Google Images is enabled
17 | if (
18 | disabledEngines.includes('googleLens') &&
19 | !disabledEngines.includes('google')
20 | ) {
21 | disabledEngines.splice(disabledEngines.indexOf('googleLens'), 1);
22 | }
23 |
24 | // Disable Google Images for new installations
25 | if (context.install) {
26 | disabledEngines.push('google');
27 | }
28 |
29 | changes.engines = engines;
30 | changes.disabledEngines = disabledEngines;
31 |
32 | changes.storageVersion = revision;
33 | return browser.storage.local.set(changes);
34 | }
35 |
36 | export {message, revision, upgrade};
37 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20240619180111_add_menuchangeevent.js:
--------------------------------------------------------------------------------
1 | const message = 'Add menuChangeEvent';
2 |
3 | const revision = '20240619180111_add_menuchangeevent';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | menuChangeEvent: 0,
8 | privateMenuChangeEvent: 0
9 | };
10 |
11 | await browser.storage.local.remove('setContextMenuEvent');
12 |
13 | changes.storageVersion = revision;
14 | return browser.storage.local.set(changes);
15 | }
16 |
17 | export {message, revision, upgrade};
18 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20240624161944_remove_search_engines.js:
--------------------------------------------------------------------------------
1 | const message = 'Remove search engines';
2 |
3 | const revision = '20240624161944_remove_search_engines';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | const removeEngines = [
13 | 'haveibeentrained',
14 | 'enterpix',
15 | 'immerse',
16 | 'clipretrieval'
17 | ];
18 |
19 | changes.engines = engines.filter(function (item) {
20 | return !removeEngines.includes(item);
21 | });
22 | changes.disabledEngines = disabledEngines.filter(function (item) {
23 | return !removeEngines.includes(item);
24 | });
25 |
26 | changes.storageVersion = revision;
27 | return browser.storage.local.set(changes);
28 | }
29 |
30 | export {message, revision, upgrade};
31 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20250102095603_add_lenso.ai.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Lenso.ai';
2 |
3 | const revision = '20250102095603_add_lenso.ai';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngine = 'lenso';
12 |
13 | const enabledEngineCount = engines.length - disabledEngines.length;
14 |
15 | engines.splice(1, 0, newEngine);
16 | changes.engines = engines;
17 |
18 | if (enabledEngineCount <= 1) {
19 | disabledEngines.push(newEngine);
20 | }
21 | if (enabledEngineCount === 8 && !disabledEngines.includes('alamy')) {
22 | disabledEngines.push('alamy');
23 | }
24 | changes.disabledEngines = disabledEngines;
25 |
26 | changes.storageVersion = revision;
27 | return browser.storage.local.set(changes);
28 | }
29 |
30 | export {message, revision, upgrade};
31 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20250218121640_remove_google_images.js:
--------------------------------------------------------------------------------
1 | const message = 'Remove Google Images';
2 |
3 | const revision = '20250218121640_remove_google_images';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | engines.splice(engines.indexOf('google'), 1);
13 | changes.engines = engines;
14 |
15 | if (disabledEngines.includes('google')) {
16 | disabledEngines.splice(disabledEngines.indexOf('google'), 1);
17 | } else if (disabledEngines.includes('googleLens')) {
18 | disabledEngines.splice(disabledEngines.indexOf('googleLens'), 1);
19 | }
20 | changes.disabledEngines = disabledEngines;
21 |
22 | changes.storageVersion = revision;
23 | return browser.storage.local.set(changes);
24 | }
25 |
26 | export {message, revision, upgrade};
27 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20250317134215_add_back_google_images.js:
--------------------------------------------------------------------------------
1 | import {targetEnv} from 'utils/config';
2 |
3 | const message = 'Add back Google Images';
4 |
5 | const revision = '20250317134215_add_back_google_images';
6 |
7 | async function upgrade() {
8 | const changes = {};
9 | const {engines, disabledEngines} = await browser.storage.local.get([
10 | 'engines',
11 | 'disabledEngines'
12 | ]);
13 |
14 | if (!['chrome', 'opera'].includes(targetEnv)) {
15 | engines.push('googleImages');
16 | disabledEngines.push('googleImages');
17 |
18 | changes.engines = engines;
19 | changes.disabledEngines = disabledEngines;
20 | }
21 |
22 | changes.storageVersion = revision;
23 | return browser.storage.local.set(changes);
24 | }
25 |
26 | export {message, revision, upgrade};
27 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20250413115022_add_unsplash.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Unsplash';
2 |
3 | const revision = '20250413115022_add_unsplash';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['unsplash'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/20250511143502_remove_karma_decay.js:
--------------------------------------------------------------------------------
1 | const message = 'Remove Karma Decay';
2 |
3 | const revision = '20250511143502_remove_karma_decay';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | const removeEngines = ['karmaDecay'];
13 |
14 | changes.engines = engines.filter(function (item) {
15 | return !removeEngines.includes(item);
16 | });
17 | changes.disabledEngines = disabledEngines.filter(function (item) {
18 | return !removeEngines.includes(item);
19 | });
20 |
21 | changes.storageVersion = revision;
22 | return browser.storage.local.set(changes);
23 | }
24 |
25 | export {message, revision, upgrade};
26 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/BJXdcUXOG.js:
--------------------------------------------------------------------------------
1 | const message = 'Add WhatAnime';
2 |
3 | const revision = 'BJXdcUXOG';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('whatanime');
13 | changes.disabledEngines = disabledEngines.concat('whatanime');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/BJguWEHcbz.js:
--------------------------------------------------------------------------------
1 | const message = 'Add installTime and searchCount';
2 |
3 | const revision = 'BJguWEHcbz';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | changes.installTime = Date.now();
8 | changes.searchCount = 0;
9 |
10 | changes.storageVersion = revision;
11 | return browser.storage.local.set(changes);
12 | }
13 |
14 | export {message, revision, upgrade};
15 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/Bk42MzXdW.js:
--------------------------------------------------------------------------------
1 | const message = 'Merge searchAllEngines options';
2 |
3 | const revision = 'Bk42MzXdW';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 |
8 | const {searchAllEngines, searchAllEnginesLocation} =
9 | await browser.storage.local.get([
10 | 'searchAllEngines',
11 | 'searchAllEnginesLocation'
12 | ]);
13 |
14 | await browser.storage.local.remove([
15 | 'searchAllEngines',
16 | 'searchAllEnginesLocation'
17 | ]);
18 |
19 | if (searchAllEngines) {
20 | changes.searchAllEnginesContextMenu =
21 | searchAllEnginesLocation === 'menu' ? 'main' : 'sub';
22 | } else {
23 | changes.searchAllEnginesContextMenu = 'false';
24 | }
25 |
26 | changes.storageVersion = revision;
27 | return browser.storage.local.set(changes);
28 | }
29 |
30 | export {message, revision, upgrade};
31 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/HJ5MKKGhW.js:
--------------------------------------------------------------------------------
1 | const message =
2 | 'Add showInContextMenu, searchAllEnginesAction and searchModeAction options';
3 |
4 | const revision = 'HJ5MKKGhW';
5 |
6 | async function upgrade() {
7 | const changes = {
8 | showInContextMenu: true,
9 | searchAllEnginesAction: 'sub', // 'main', 'sub', 'false'
10 | searchModeAction: 'select' // 'select', 'selectUpload', 'capture', 'upload', 'url'
11 | };
12 |
13 | changes.storageVersion = revision;
14 | return browser.storage.local.set(changes);
15 | }
16 |
17 | export {message, revision, upgrade};
18 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/Hy1tD8ANb.js:
--------------------------------------------------------------------------------
1 | const message = 'Add imgFullParse option';
2 |
3 | const revision = 'Hy1tD8ANb';
4 |
5 | async function upgrade() {
6 | const changes = {imgFullParse: false};
7 |
8 | changes.storageVersion = revision;
9 | return browser.storage.local.set(changes);
10 | }
11 |
12 | export {message, revision, upgrade};
13 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/IMMjiccGj.js:
--------------------------------------------------------------------------------
1 | const message = 'Hide unavailable engines';
2 |
3 | const revision = 'IMMjiccGj';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const missingEngines = [
12 | 'wayfair',
13 | 'birchlane',
14 | 'allmodern',
15 | 'jossandmain',
16 | 'perigold'
17 | ];
18 |
19 | changes.engines = engines.filter(function (item) {
20 | return !missingEngines.includes(item);
21 | });
22 | changes.disabledEngines = disabledEngines.filter(function (item) {
23 | return !missingEngines.includes(item);
24 | });
25 |
26 | changes.storageVersion = revision;
27 | return browser.storage.local.set(changes);
28 | }
29 |
30 | export {message, revision, upgrade};
31 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/K9Esw2jiQ3.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Reddit Repost Sleuth';
2 |
3 | const revision = 'K9Esw2jiQ3';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('repostSleuth');
13 | changes.disabledEngines = disabledEngines.concat('repostSleuth');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/LX20x6G8l.js:
--------------------------------------------------------------------------------
1 | const message = 'Add trademark and design patent search engines';
2 |
3 | const revision = 'LX20x6G8l';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = [
12 | 'esearch',
13 | 'tmview',
14 | 'branddb',
15 | 'madridMonitor',
16 | 'auTrademark',
17 | 'auDesign',
18 | 'nzTrademark',
19 | 'jpDesign'
20 | ];
21 |
22 | changes.engines = engines.concat(newEngines);
23 | changes.disabledEngines = disabledEngines.concat(newEngines);
24 |
25 | changes.storageVersion = revision;
26 | return browser.storage.local.set(changes);
27 | }
28 |
29 | export {message, revision, upgrade};
30 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/S18hLi5tG.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Iqdb';
2 |
3 | const revision = 'S18hLi5tG';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('iqdb');
13 | changes.disabledEngines = disabledEngines.concat('iqdb');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/S1ebtZ3Uzz.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Karma Decay';
2 |
3 | const revision = 'S1ebtZ3Uzz';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('karmaDecay');
13 | changes.disabledEngines = disabledEngines.concat('karmaDecay');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/S1kNNadHZ.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Sogou engine';
2 |
3 | const revision = 'S1kNNadHZ';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('sogou');
13 | changes.disabledEngines = disabledEngines.concat('sogou');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/SJzIWmjKz.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Ascii2d';
2 |
3 | const revision = 'SJzIWmjKz';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('ascii2d');
13 | changes.disabledEngines = disabledEngines.concat('ascii2d');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/ShjDMM87.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Dreamstime, Alamy and 123RF';
2 |
3 | const revision = 'ShjDMM87';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['dreamstime', 'alamy', '123rf'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/SklYTwQOYf.js:
--------------------------------------------------------------------------------
1 | const message = 'Add SauceNAO';
2 |
3 | const revision = 'SklYTwQOYf';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('saucenao');
13 | changes.disabledEngines = disabledEngines.concat('saucenao');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/SkwaU8NlX.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Qihoo';
2 |
3 | const revision = 'SkwaU8NlX';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('qihoo');
13 | changes.disabledEngines = disabledEngines.concat('qihoo');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/Syy800KkQ.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Pinterest';
2 |
3 | const revision = 'Syy800KkQ';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('pinterest');
13 | changes.disabledEngines = disabledEngines.concat('pinterest');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/TshBYj8anA.js:
--------------------------------------------------------------------------------
1 | import {isAndroid} from 'utils/common';
2 | import {targetEnv} from 'utils/config';
3 |
4 | const message = 'Configure search engines';
5 |
6 | const revision = 'TshBYj8anA';
7 |
8 | async function upgrade() {
9 | const changes = {};
10 |
11 | const {installTime} = await browser.storage.local.get('installTime');
12 |
13 | let {engines, disabledEngines} = await browser.storage.local.get([
14 | 'engines',
15 | 'disabledEngines'
16 | ]);
17 |
18 | const hiddenEngines = [];
19 |
20 | if (targetEnv === 'samsung') {
21 | engines.splice(engines.indexOf('auDesign'), 0, 'auTrademark');
22 | disabledEngines.push('auTrademark', 'dreamstime');
23 | } else if (targetEnv === 'safari') {
24 | hiddenEngines.push('iqdb', 'jpDesign');
25 | }
26 |
27 | if ((await isAndroid()) || targetEnv === 'safari') {
28 | hiddenEngines.push('jingdong', 'taobao', 'alibabaChina');
29 | }
30 |
31 | if (hiddenEngines.length) {
32 | engines = engines.filter(function (item) {
33 | return !hiddenEngines.includes(item);
34 | });
35 | disabledEngines = disabledEngines.filter(function (item) {
36 | return !hiddenEngines.includes(item);
37 | });
38 | }
39 |
40 | if (Date.now() - installTime < 60000) {
41 | if (targetEnv === 'samsung') {
42 | disabledEngines.push('dreamstime');
43 | }
44 |
45 | const enabledEngines = ['sogou', 'shutterstock', 'alamy'];
46 |
47 | disabledEngines = disabledEngines.filter(function (item) {
48 | return !enabledEngines.includes(item);
49 | });
50 | }
51 |
52 | engines.splice(
53 | engines.indexOf('baidu') + 1,
54 | 0,
55 | engines.splice(engines.indexOf('sogou'), 1)[0]
56 | );
57 |
58 | changes.engines = engines;
59 | changes.disabledEngines = disabledEngines;
60 |
61 | changes.storageVersion = revision;
62 | return browser.storage.local.set(changes);
63 | }
64 |
65 | export {message, revision, upgrade};
66 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/UhWEtK9gMh.js:
--------------------------------------------------------------------------------
1 | const message = 'Add bypassImageHostBlocking';
2 |
3 | const revision = 'UhWEtK9gMh';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | changes.bypassImageHostBlocking = true;
8 |
9 | changes.storageVersion = revision;
10 | return browser.storage.local.set(changes);
11 | }
12 |
13 | export {message, revision, upgrade};
14 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/d8CIdomCW.js:
--------------------------------------------------------------------------------
1 | const message = 'Add PimEyes, Stocksy United, Pond5, PIXTA and Wayfair';
2 |
3 | const revision = 'd8CIdomCW';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['pimeyes', 'stocksy', 'pond5', 'pixta', 'wayfair'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/d8IK4KCtVm.js:
--------------------------------------------------------------------------------
1 | import {targetEnv} from 'utils/config';
2 |
3 | const message = 'Configure engines for Samsung Internet';
4 |
5 | const revision = 'd8IK4KCtVm';
6 |
7 | async function upgrade() {
8 | const changes = {};
9 |
10 | if (targetEnv === 'samsung') {
11 | const {engines, disabledEngines} = await browser.storage.local.get([
12 | 'engines',
13 | 'disabledEngines'
14 | ]);
15 |
16 | const enabledEngines = ['shutterstock', 'dreamstime'];
17 |
18 | const hiddenEngines = [
19 | 'mailru',
20 | 'jingdong',
21 | 'taobao',
22 | 'alibabaChina',
23 | 'auTrademark'
24 | ];
25 |
26 | changes.engines = engines.filter(function (item) {
27 | return !hiddenEngines.includes(item);
28 | });
29 | changes.disabledEngines = disabledEngines.filter(function (item) {
30 | return !enabledEngines.includes(item) && !hiddenEngines.includes(item);
31 | });
32 | }
33 |
34 | changes.storageVersion = revision;
35 | return browser.storage.local.set(changes);
36 | }
37 |
38 | export {message, revision, upgrade};
39 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/ekhOvNiTsF.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Mail.ru';
2 |
3 | const revision = 'ekhOvNiTsF';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 |
12 | changes.engines = engines.concat('mailru');
13 | changes.disabledEngines = disabledEngines.concat('mailru');
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/ggrr9C9pgV.js:
--------------------------------------------------------------------------------
1 | const message = 'Add new search engines';
2 |
3 | const revision = 'ggrr9C9pgV';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = [
12 | 'birchlane',
13 | 'allmodern',
14 | 'jossandmain',
15 | 'perigold',
16 | 'ikea'
17 | ];
18 |
19 | changes.engines = engines.concat(newEngines);
20 | changes.disabledEngines = disabledEngines.concat(newEngines);
21 |
22 | changes.storageVersion = revision;
23 | return browser.storage.local.set(changes);
24 | }
25 |
26 | export {message, revision, upgrade};
27 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/r1H3rgx1X.js:
--------------------------------------------------------------------------------
1 | const message = 'Add stock photo engines';
2 |
3 | const revision = 'r1H3rgx1X';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 |
8 | const {engines, disabledEngines} = await browser.storage.local.get([
9 | 'engines',
10 | 'disabledEngines'
11 | ]);
12 | const newEngines = [
13 | 'getty',
14 | 'istock',
15 | 'shutterstock',
16 | 'adobestock',
17 | 'depositphotos'
18 | ];
19 |
20 | changes.engines = engines.concat(newEngines);
21 | changes.disabledEngines = disabledEngines.concat(newEngines);
22 |
23 | changes.storageVersion = revision;
24 | return browser.storage.local.set(changes);
25 | }
26 |
27 | export {message, revision, upgrade};
28 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/r1Pvd36nz.js:
--------------------------------------------------------------------------------
1 | const message = 'Add searchModeContextMenu';
2 |
3 | const revision = 'r1Pvd36nz';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | searchModeContextMenu: 'select' // 'select', 'selectUpload', 'capture'
8 | };
9 |
10 | changes.storageVersion = revision;
11 | return browser.storage.local.set(changes);
12 | }
13 |
14 | export {message, revision, upgrade};
15 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/ryY8H0EWf.js:
--------------------------------------------------------------------------------
1 | const message = 'Add contribPageLastOpen';
2 |
3 | const revision = 'ryY8H0EWf';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | changes.contribPageLastOpen = 0;
8 |
9 | changes.storageVersion = revision;
10 | return browser.storage.local.set(changes);
11 | }
12 |
13 | export {message, revision, upgrade};
14 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/ryekyizAg.js:
--------------------------------------------------------------------------------
1 | const message = 'Initial version';
2 |
3 | const revision = 'ryekyizAg';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | engines: ['google', 'bing', 'yandex', 'baidu', 'tineye'],
8 | disabledEngines: [],
9 | searchAllEngines: true,
10 | searchAllEnginesLocation: 'submenu', // 'menu', 'submenu'
11 | tabInBackgound: false,
12 | localGoogle: true
13 | };
14 |
15 | changes.storageVersion = revision;
16 | return browser.storage.local.set(changes);
17 | }
18 |
19 | export {message, revision, upgrade};
20 |
--------------------------------------------------------------------------------
/src/storage/revisions/local/yLciyvS5He.js:
--------------------------------------------------------------------------------
1 | const message = 'Add Jingdong, Taobao and Alibaba China';
2 |
3 | const revision = 'yLciyvS5He';
4 |
5 | async function upgrade() {
6 | const changes = {};
7 | const {engines, disabledEngines} = await browser.storage.local.get([
8 | 'engines',
9 | 'disabledEngines'
10 | ]);
11 | const newEngines = ['jingdong', 'taobao', 'alibabaChina'];
12 |
13 | changes.engines = engines.concat(newEngines);
14 | changes.disabledEngines = disabledEngines.concat(newEngines);
15 |
16 | changes.storageVersion = revision;
17 | return browser.storage.local.set(changes);
18 | }
19 |
20 | export {message, revision, upgrade};
21 |
--------------------------------------------------------------------------------
/src/storage/revisions/session/20240514122825_initial_version.js:
--------------------------------------------------------------------------------
1 | const message = 'Initial version';
2 |
3 | const revision = '20240514122825_initial_version';
4 |
5 | async function upgrade() {
6 | const changes = {
7 | platformInfo: null,
8 | menuChangeEvent: 0,
9 | privateMenuChangeEvent: 0,
10 | tabRevisions: []
11 | };
12 |
13 | changes.storageVersion = revision;
14 | return browser.storage.session.set(changes);
15 | }
16 |
17 | export {message, revision, upgrade};
18 |
--------------------------------------------------------------------------------
/src/tab/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | New Tab
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/tab/main.js:
--------------------------------------------------------------------------------
1 | async function getLocationData() {
2 | const token = new URL(window.location.href).searchParams.get('id');
3 |
4 | return browser.runtime.sendMessage({
5 | id: 'storageRequest',
6 | asyncResponse: true,
7 | saveReceipt: true,
8 | storageId: token
9 | });
10 | }
11 |
12 | function setLocation(tabUrl, keepHistory) {
13 | if (keepHistory) {
14 | window.location.href = tabUrl;
15 | } else {
16 | window.location.replace(tabUrl);
17 | }
18 | }
19 |
20 | async function setupTab(data) {
21 | return browser.runtime.sendMessage({id: 'setupTab', data});
22 | }
23 |
24 | async function start() {
25 | const data = await getLocationData();
26 |
27 | if (data.tabSetupData) {
28 | await setupTab(data.tabSetupData);
29 | }
30 |
31 | setLocation(data.tabUrl, data.keepHistory);
32 | }
33 |
34 | function init() {
35 | start();
36 | }
37 |
38 | init();
39 |
--------------------------------------------------------------------------------
/src/utils/config.js:
--------------------------------------------------------------------------------
1 | const targetEnv = process.env.TARGET_ENV;
2 |
3 | const enableContributions = process.env.ENABLE_CONTRIBUTIONS === 'true';
4 |
5 | const storageRevisions = {
6 | local: process.env.STORAGE_REVISION_LOCAL,
7 | session: process.env.STORAGE_REVISION_SESSION
8 | };
9 |
10 | const appVersion = process.env.APP_VERSION;
11 |
12 | const mv3 = process.env.MV3 === 'true';
13 |
14 | export {targetEnv, enableContributions, storageRevisions, appVersion, mv3};
15 |
--------------------------------------------------------------------------------
/src/utils/vuetify.js:
--------------------------------------------------------------------------------
1 | import {createVuetify} from 'vuetify';
2 |
3 | import {getAppTheme, addThemeListener} from 'utils/app';
4 |
5 | const LightTheme = {
6 | dark: false,
7 | colors: {
8 | background: '#FFFFFF',
9 | surface: '#FFFFFF',
10 | primary: '#6750A4',
11 | secondary: '#625B71'
12 | }
13 | };
14 |
15 | const DarkTheme = {
16 | dark: true,
17 | colors: {
18 | background: '#1C1B1F',
19 | surface: '#1C1B1F',
20 | primary: '#D0BCFF',
21 | secondary: '#CCC2DC'
22 | }
23 | };
24 |
25 | async function configTheme(vuetify, {theme = ''} = {}) {
26 | async function setTheme({theme = '', dispatchChange = true} = {}) {
27 | if (!theme) {
28 | theme = await getAppTheme();
29 | }
30 |
31 | document.documentElement.style.setProperty('color-scheme', theme);
32 | vuetify.theme.global.name.value = theme;
33 |
34 | if (dispatchChange) {
35 | document.dispatchEvent(new CustomEvent('themeChange', {detail: theme}));
36 | }
37 | }
38 |
39 | addThemeListener(setTheme);
40 |
41 | await setTheme({theme, dispatchChange: false});
42 | }
43 |
44 | async function configVuetify(app) {
45 | const theme = await getAppTheme();
46 |
47 | const vuetify = createVuetify({
48 | theme: {
49 | themes: {light: LightTheme, dark: DarkTheme},
50 | defaultTheme: theme
51 | },
52 | defaults: {
53 | VDialog: {
54 | eager: true
55 | },
56 | VSelect: {
57 | eager: true
58 | },
59 | VSnackbar: {
60 | eager: true
61 | },
62 | VMenu: {
63 | eager: true
64 | }
65 | }
66 | });
67 |
68 | await configTheme(vuetify, {theme});
69 |
70 | app.use(vuetify);
71 | }
72 |
73 | export {configTheme, configVuetify};
74 |
--------------------------------------------------------------------------------
/src/view/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/view/main.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 |
3 | import {configApp, loadFonts} from 'utils/app';
4 | import {configVuetify} from 'utils/vuetify';
5 | import App from './App';
6 |
7 | async function init() {
8 | await loadFonts(['400 14px Roboto', '500 14px Roboto']);
9 |
10 | const app = createApp(App);
11 |
12 | await configApp(app);
13 | await configVuetify(app);
14 |
15 | app.mount('body');
16 | }
17 |
18 | init();
19 |
--------------------------------------------------------------------------------