├── .gitattributes
├── .gitignore
├── .opera
└── manifest.json
├── .work
├── chrome_market
│ ├── descr.en.txt
│ ├── descr.es.txt
│ ├── descr.fr.txt
│ ├── descr.hu.txt
│ ├── descr.sr.txt
│ ├── descr.uk.txt
│ ├── promo_img
│ │ ├── promo_1400x560.png
│ │ ├── promo_1400x560.psd
│ │ ├── promo_440x280.png
│ │ ├── promo_440x280.psd
│ │ ├── promo_920x680.png
│ │ └── promo_920x680.psd
│ └── screenshots
│ │ └── en
│ │ ├── 1.jpg
│ │ └── 2.jpg
├── icons_psd
│ ├── 128.psd
│ ├── 19.psd
│ ├── 38.psd
│ └── 48.psd
└── opera_market
│ ├── 64.png
│ ├── descr.en.txt
│ ├── descr.es.txt
│ ├── descr.fr.txt
│ ├── descr.hu.txt
│ ├── descr.sr.txt
│ ├── descr.uk.txt
│ ├── promo
│ ├── 300x188.png
│ └── 300x188.psd
│ └── screenshots
│ ├── 1.jpg
│ └── 2.jpg
├── _locales
├── en
│ └── messages.json
├── es
│ └── messages.json
├── fr
│ └── messages.json
├── hu
│ └── messages.json
├── sr
│ └── messages.json
└── uk
│ └── messages.json
├── build
├── build.json
├── build.py
├── releases
│ ├── 0.1.1.zip
│ ├── 0.1.3.zip
│ ├── 0.1.zip
│ ├── 0.2.1.zip
│ ├── 0.2.10.zip
│ ├── 0.2.2.zip
│ ├── 0.2.3.zip
│ ├── 0.2.4.zip
│ ├── 0.2.5.zip
│ ├── 0.2.6.zip
│ ├── 0.2.7.zip
│ ├── 0.2.8.1.zip
│ ├── 0.2.8.zip
│ ├── 0.2.9.zip
│ ├── 0.2.zip
│ ├── 0.3.0.zip
│ └── changelog.txt
└── releases_opera
│ ├── 0.1.1.nex
│ ├── 0.1.2.nex
│ ├── 0.1.3.nex
│ ├── 0.2.1.nex
│ ├── 0.2.10.nex
│ ├── 0.2.2.nex
│ ├── 0.2.3.nex
│ ├── 0.2.4.nex
│ ├── 0.2.5.nex
│ ├── 0.2.6.nex
│ ├── 0.2.7.nex
│ ├── 0.2.8.1.nex
│ ├── 0.2.8.nex
│ ├── 0.2.9.nex
│ └── 0.2.nex
├── css
├── options.css
└── popup.css
├── img
├── ext_icons
│ ├── 128.png
│ ├── 16.png
│ ├── 32.png
│ └── 48.png
├── help_screen.png
├── pin-26.png
└── ua_1px.png
├── js
├── background.js
├── backup.js
├── helpers
│ ├── localizer.js
│ └── quick_options.js
├── options.js
├── popup.js
├── storage.js
└── utils.js
├── lib
└── jsTabs
│ ├── tabs.css
│ └── tabs.js
├── manifest.json
├── options.html
├── popup.html
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
3 |
--------------------------------------------------------------------------------
/.opera/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "__MSG_extName__",
3 | "description": "__MSG_extDescr__",
4 | "version": "0.2.10",
5 | "manifest_version": 2,
6 |
7 | "developer": { "name": "Mykhailo Onikiienko",
8 | "url": "http://onikienko.pp.ua" },
9 |
10 | "default_locale": "en",
11 | "icons": {
12 | "16": "img/ext_icons/16.png",
13 | "48": "img/ext_icons/48.png",
14 | "128": "img/ext_icons/128.png"
15 | },
16 |
17 | "browser_action": {
18 | "default_icon": {
19 | "19": "img/ext_icons/19.png",
20 | "38": "img/ext_icons/38.png"
21 | },
22 | "default_title": "__MSG_extName__",
23 | "default_popup": "popup.html"
24 | },
25 |
26 | "background": {
27 | "scripts": ["js/storage.js", "js/background.js"],
28 | "persistent": true
29 | },
30 | "options_page": "options.html",
31 |
32 | "permissions": [
33 | "storage",
34 | "tabs",
35 | "opera://favicon/"
36 | ]
37 | }
--------------------------------------------------------------------------------
/.work/chrome_market/descr.en.txt:
--------------------------------------------------------------------------------
1 | ► Save opened tabs in the group.
2 | ✓ Tab Groups are stored in the sync Chrome storage. Sign in to the browser to get them on other PC.
3 | ✓ Assign a group name.
4 | ✓ Edit, add new tabs, delete, move the group from the list.
5 |
6 | ► Session Manager provides quick and convenient access to the latest browser sessions.
7 | ✓ Access to the currently open (and earlier) browser session.
8 | ✓ Recover any session in one click.
9 | ✓ !!! Sessions are not synchronized.
10 |
11 |
12 | ☀ Tips:
13 | ● To quickly save all open tabs in a group, open the extension window and press "ENTER." Later you can edit the name of the group.
14 | ● "Ctrl + click" on group name (or session) - open tabs in a new window.
15 |
16 |
17 | Tab Hamster source code: https://github.com/onikienko/TabHamster
18 |
--------------------------------------------------------------------------------
/.work/chrome_market/descr.es.txt:
--------------------------------------------------------------------------------
1 | ► Guarda pestañas abiertas como un grupo.
2 | ✓ Los Grupos de Pestañas se guardan el espacio de almacenamiento para sincronización. Ingrese a su cuenta en el navegador para obtenerlos en otro PC.
3 | ✓ Asigne un nombre al grupo.
4 | ✓ Edite, agregue nuevas pestañas, elimine, mueva el grupo dentro de la lista.
5 |
6 | ► El Administrador de Sesiones brinda una rápida y conveniente forma de acceso a las ultimas sesiones de navegación.
7 | ✓ Acceda a la sesión del navegador actualmente abierta, así como a las anteriores.
8 | ✓ Recupere rápidamente cualquier sesión con un sólo clic.
9 | ✓! Las sesiones NO se sincronizan.
10 |
11 |
12 | ☀ Consejos:
13 | ● Para guardar rápidamente todas las pestañas abiertas en un grupo, abra la ventana de la extensión y presione 'ENTER'. Luego, puede editar el nombre del grupo.
14 | ● 'Ctrl + click' en el nombre de un grupo (o sesión) lo abrirá en una nueva ventana.
15 |
16 |
17 | Código fuente de Tab Hamster: https://github.com/onikienko/TabHamster
--------------------------------------------------------------------------------
/.work/chrome_market/descr.fr.txt:
--------------------------------------------------------------------------------
1 | ► Enregistrer les onglets ouverts dans le groupe.
2 | ✓ Les groupes d'onglets sont synchronisés avec l'espace de stockage de Chrome. Connectez-vous au navigateur afin de les récupérer sur un autre PC.
3 | ✓ Attribuer un nom de groupe.
4 | ✓ Modifier, ajouter de nouveaux onglets, supprimer, déplacer le groupe de la liste.
5 |
6 | ► Le Gestionnaire de Session fournit un accès rapide et pratique aux dernières sessions du navigateur.
7 | ✓ Accès à la session actuellement ouverte (et versions antérieures) du navigateur.
8 | ✓ Récupération d'une session en un seul clic.
9 | ✓! Les sessions ne sont pas synchronisés.
10 |
11 |
12 | ☀ Conseils:
13 | ● Pour sauvegarder rapidement tous les onglets ouverts dans un groupe, ouvrez la fenêtre d'extension et appuyez sur "ENTRER". Plus tard, vous pouvez modifier le nom du groupe.
14 | ● "Ctrl + clic" sur le nom du groupe (ou session) - ouvre les onglets dans une nouvelle fenêtre.
15 |
16 |
17 | Tab Hamster source code : https://github.com/onikienko/TabHamster
18 |
--------------------------------------------------------------------------------
/.work/chrome_market/descr.hu.txt:
--------------------------------------------------------------------------------
1 | ► A megnyitott fülek csoportokba történő mentése.
2 | ✓ A fül csoportok a Chrome szinkronizációs adatbázisában tárolódnak. Ezeket a bejelentkezés után egy másik számítógépen is elérhetjük.
3 | ✓ Név hozzárendelése a csoportokhoz.
4 | ✓ Szerkesztés, új fülek hozzáadása, törlés és a csoportok mozgatása a listán.
5 |
6 | ► A folyamatmenedzselő gyors és kényelmes hozzáférést biztosít a Chrome folyamatokhoz.
7 | ✓ Hozzáférés az éppen megnyitott (és a korábbi) böngészési folyamatokhoz.
8 | ✓ Egy kattintással visszaállítható bármelyik folyamat.
9 | ✓! A folyamatok nem szinkronizálódnak.
10 |
11 |
12 | ☀ Tippek:
13 | ● Gyorsan el tudjuk menteni az összes megnyitott fület egy csoportba, ha a kiterjesztés ablakában megnyomjuk az "ENTER" billentyűt. A csoportot később tudjuk szerkeszteni.
14 | ● "Ctrl + klikk' a mentett csoport (vagy folyamat) nevén új ablakba nyitja meg a füleket.
15 |
16 | Tab Hamster forráskód: https://github.com/onikienko/TabHamster
17 |
--------------------------------------------------------------------------------
/.work/chrome_market/descr.sr.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/descr.sr.txt
--------------------------------------------------------------------------------
/.work/chrome_market/descr.uk.txt:
--------------------------------------------------------------------------------
1 | ► Зберігай відкриті вкладки (таби) в групи.
2 | ✓ Всі збережені групи зберігаються в сховище Chrome. Це сховище синхронізується. Це дає можливість користуватися ними на різних машинах (дома, в офісі).
3 | ✓ Для роботи синхронізації треба виконати вхід в обліковий запис Chrome.
4 | ✓ Призначай групам ім'я.
5 | ✓ Редагуй, додавай нові таби, видаляй, переміщуй групи.
6 |
7 | ► Менеджер сесій - швидкий та зручний доступ до 30 останніх сесій браузера.
8 | ✓ Доступ до відкритих зараз (та раніше) вкладок браузера.
9 | ✓ Швидке відновлення будь-якої сесії.
10 | ✓ !!! Сесії не синхронізуються.
11 |
12 |
13 | ☀ Підказки:
14 | ● Для того, щоб швидко зберегти всі відкриті таби в групу, відкрий вікно розширення та натисни клавішу 'ENTER' на клавіатурі. Нова група буде мати ім'я з поточних дати та часу. (Пізніше можна буде відредагувати)
15 | ● "Ctrl + клік" на імені групи (або сесії) - відкрити всі вкладки в новому вікні.
16 |
17 |
18 | Tab Hamster source code: https://github.com/onikienko/TabHamster
19 |
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_1400x560.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_1400x560.png
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_1400x560.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_1400x560.psd
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_440x280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_440x280.png
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_440x280.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_440x280.psd
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_920x680.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_920x680.png
--------------------------------------------------------------------------------
/.work/chrome_market/promo_img/promo_920x680.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/promo_img/promo_920x680.psd
--------------------------------------------------------------------------------
/.work/chrome_market/screenshots/en/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/screenshots/en/1.jpg
--------------------------------------------------------------------------------
/.work/chrome_market/screenshots/en/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/chrome_market/screenshots/en/2.jpg
--------------------------------------------------------------------------------
/.work/icons_psd/128.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/icons_psd/128.psd
--------------------------------------------------------------------------------
/.work/icons_psd/19.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/icons_psd/19.psd
--------------------------------------------------------------------------------
/.work/icons_psd/38.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/icons_psd/38.psd
--------------------------------------------------------------------------------
/.work/icons_psd/48.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/icons_psd/48.psd
--------------------------------------------------------------------------------
/.work/opera_market/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/64.png
--------------------------------------------------------------------------------
/.work/opera_market/descr.en.txt:
--------------------------------------------------------------------------------
1 | ► Save open tabs in the group.
2 | ✓ Tab Groups are stored in the sync Opera storage. Sign in to browser to get them on other PC.
3 | ✓ Assign a group name.
4 | ✓ Edit, add new tabs, delete, move the group from the list.
5 |
6 | ► Session Manager provides quick and convenient access to the latest Opera sessions.
7 | ✓ Access to the currently open (and earlier) browser session.
8 | ✓ Rapid recovery of any session in one click.
9 | ✓! Sessions are not synchronized.
10 |
11 |
12 | ☀ Tips:
13 | ● To quickly save all open tabs in a group, open the extension window and press "ENTER". Later, you can edit the name of the group.
14 | ● "Ctrl + click" on group name (or session) - open tabs in a new window.
--------------------------------------------------------------------------------
/.work/opera_market/descr.es.txt:
--------------------------------------------------------------------------------
1 | ► Guarda pestañas abiertas como un grupo.
2 | ✓ Los Grupos de Pestañas se guardan el espacio de almacenamiento para sincronización. Ingrese a su cuenta en el navegador para obtenerlos en otro PC.
3 | ✓ Asigne un nombre al grupo.
4 | ✓ Edite, agregue nuevas pestañas, elimine, mueva el grupo dentro de la lista.
5 |
6 | ► El Administrador de Sesiones brinda una rápida y conveniente forma de acceso a las ultimas sesiones de navegación.
7 | ✓ Acceda a la sesión del navegador actualmente abierta, así como a las anteriores.
8 | ✓ Recupere rápidamente cualquier sesión con un sólo clic.
9 | ✓! Las sesiones NO se sincronizan.
10 |
11 |
12 | ☀ Consejos:
13 | ● Para guardar rápidamente todas las pestañas abiertas en un grupo, abra la ventana de la extensión y presione 'ENTER'. Luego, puede editar el nombre del grupo.
14 | ● 'Ctrl + click' en el nombre de un grupo (o sesión) lo abrirá en una nueva ventana.
15 |
16 |
17 | Código fuente de Tab Hamster: https://github.com/onikienko/TabHamster
--------------------------------------------------------------------------------
/.work/opera_market/descr.fr.txt:
--------------------------------------------------------------------------------
1 | ► Enregistrer les onglets ouverts dans le groupe.
2 | ✓ Les groupes d'onglets sont synchronisés avec l'espace de stockage d'Opéra. Connectez-vous au navigateur afin de les récupérer sur un autre PC.
3 | ✓ Attribuer un nom de groupe.
4 | ✓ Modifier, ajouter de nouveaux onglets, supprimer, déplacer le groupe de la liste.
5 |
6 | ► Le Gestionnaire de Session fournit un accès rapide et pratique aux dernières sessions d'Opéra.
7 | ✓ Accès à la session actuellement ouverte (et versions antérieures) du navigateur.
8 | ✓ Récupération d'une session en un seul clic.
9 | ✓! Les sessions ne sont pas synchronisés.
10 |
11 |
12 | ☀ Conseils:
13 | ● Pour sauvegarder rapidement tous les onglets ouverts dans un groupe, ouvrez la fenêtre d'extension et appuyez sur "ENTRER". Plus tard, vous pouvez modifier le nom du groupe.
14 | ● "Ctrl + clic" sur le nom du groupe (ou session) - ouvre les onglets dans une nouvelle fenêtre.
15 |
--------------------------------------------------------------------------------
/.work/opera_market/descr.hu.txt:
--------------------------------------------------------------------------------
1 | ► A megnyitott fülek csoportokba történő mentése.
2 | ✓ A fül csoportok az Opera szinkronizációs adatbázisában tárolódnak. Ezeket a bejelentkezés után egy másik számítógépen
3 | is elérhetjük.
4 | ✓ Név hozzárendelése a csoportokhoz.
5 | ✓ Szerkesztés, új fülek hozzáadása, törlés és a csoportok mozgatása a listán.
6 |
7 | ► A folyamatmenedzselő gyors és kényelmes hozzáférést biztosít az Opera folyamatokhoz.
8 | ✓ Hozzáférés az éppen megnyitott (és a korábbi) böngészési folyamatokhoz.
9 | ✓ Egy kattintással visszaállítható bármelyik folyamat.
10 | ✓! A folyamatok nem szinkronizálódnak.
11 |
12 |
13 | ☀ Tippek:
14 | ● Gyorsan el tudjuk menteni az összes megnyitott fület egy csoportba, ha a kiterjesztés ablakában megnyomjuk az "ENTER"
15 | billentyűt. A csoportot később tudjuk szerkeszteni.
16 | ● "Ctrl + klikk' a mentett csoport (vagy folyamat) nevén új ablakba nyitja meg a füleket.
17 |
--------------------------------------------------------------------------------
/.work/opera_market/descr.sr.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/descr.sr.txt
--------------------------------------------------------------------------------
/.work/opera_market/descr.uk.txt:
--------------------------------------------------------------------------------
1 | ► Зберігай відкриті вкладки (таби) в групи.
2 | ✓ Всі збережені групи записуються в сховище Opera. Це сховище сінхронізується. Це дає вам можливість користуватися ними на різних машинах (дома, в офісі).
3 | ✓ Для роботи сінхронізаціі треба виконати вхід в Opera аккаунт.
4 | ✓ Призначай групам ім'я.
5 | ✓ Редагуйте, додавайте нові таби, видаляйте, переміщуйте групи.
6 |
7 | ► Менеджер сесій - швидкий та зручний доступ до 30 останніх сесій бравзера.
8 | ✓ Доступ до відкритих зараз (та раніше) вкладок бравзера.
9 | ✓ Швидке поновлення будь якої сесії.
10 | ✓ !!! Сесії не сінхронізуються.
11 |
12 |
13 | ☀ Підказки:
14 | ● Щоб швидко зберегти всі відкриті таби в групу, відкрий вікно розширення та натисни клавішу 'ENTER' на клавіатурі. Нова група буде мати ім'я з поточних дати та часу. (Пізніше можна буде відредагувати)
15 | ● "Ctrl + клік" на імені групи (або сесії) - відкрити всі вкладки в новому вікні.
16 |
17 |
18 | Tab Hamster source code: https://github.com/onikienko/TabHamster
--------------------------------------------------------------------------------
/.work/opera_market/promo/300x188.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/promo/300x188.png
--------------------------------------------------------------------------------
/.work/opera_market/promo/300x188.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/promo/300x188.psd
--------------------------------------------------------------------------------
/.work/opera_market/screenshots/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/screenshots/1.jpg
--------------------------------------------------------------------------------
/.work/opera_market/screenshots/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/.work/opera_market/screenshots/2.jpg
--------------------------------------------------------------------------------
/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extName": {
3 | "message": "TabHamster",
4 | "description": "do not translate extension name"
5 | },
6 | "extDescr": {
7 | "message": "Session manager and tabs saver. With sync",
8 | "description": ""
9 | },
10 |
11 | "p_optionsBtn": {
12 | "message": "Settings",
13 | "description": "Prefix 'p_' - popup"
14 | },
15 | "p_helpBtn": {
16 | "message": "Help",
17 | "description": "Title for help button"
18 | },
19 | "p_GroupNameToSave": {
20 | "message": "New group from opened tabs",
21 | "description": "Placeholder in text input"
22 | },
23 | "p_groupBtn": {
24 | "message": "Save",
25 | "description": "Btn hint"
26 | },
27 | "p_tabName_Saved": {
28 | "message": "Saved groups",
29 | "description": ""
30 | },
31 | "p_tabName_Sessions": {
32 | "message": "Sessions",
33 | "description": ""
34 | },
35 | "p_synStorageUsage_title": {
36 | "message": "Browser's sync storage usage",
37 | "description": ""
38 | },
39 |
40 | "p_newGroupName_popup": {
41 | "message": "New group name:",
42 | "description": "Modal window dialog"
43 | },
44 | "p_addLinkTitle_popup": {
45 | "message": "Title",
46 | "description": "Modal window dialog"
47 | },
48 | "p_addLinkLink_popup": {
49 | "message": "URL",
50 | "description": "Modal window dialog"
51 | },
52 | "p_overwriteGroup_popup": {
53 | "message": "The group with the same name already exists. Do you want overwrite old one with current tabs?",
54 | "description": "Modal window dialog"
55 | },
56 | "p_delGroup_btn_title": {
57 | "message": "Del group"
58 | },
59 | "p_editGroupName_btn_title": {
60 | "message": "Edit group name"
61 | },
62 | "p_groupInNewWindow_btn_title": {
63 | "message": "Open in new window (Ctrl + click)"
64 | },
65 | "p_addLinkToGroup_btn_title": {
66 | "message": "New link to group"
67 | },
68 | "p_delLinkFromGroup_btn_title": {
69 | "message": "Delete"
70 | },
71 | "p_editLink_btn_title": {
72 | "message": "Edit"
73 | },
74 | "p_upGroup_btn_title": {
75 | "message": "Up"
76 | },
77 | "p_downGroup_btn_title": {
78 | "message": "Down"
79 | },
80 | "p_windowsNumbers_text": {
81 | "message": "Win:"
82 | },
83 | "p_tabsNumbers_text": {
84 | "message": "Tabs:"
85 | },
86 | "p_window_text": {
87 | "message": "Window"
88 | },
89 | "p_quotaBitesPerItem_error": {
90 | "message": "Can't save to sync storage. Too many tabs. Try to close one more."
91 | },
92 | "p_quotaBites_error": {
93 | "message": "Sync storage is full. Del unused groups."
94 | },
95 | "p_syncStorageDefault_error": {
96 | "message": "Sync storage error. Try again later."
97 | },
98 | "p_currentSession": {
99 | "message": "Current"
100 | },
101 |
102 |
103 | "o_tabs_help": {
104 | "message": "Help",
105 | "description": "Tab name. Prefix 'o_' for options page"
106 | },
107 | "o_tabs_backup": {
108 | "message": "Backup / Restore",
109 | "description": "Tab name"
110 | },
111 | "o_tabs_main": {
112 | "message": "Settings",
113 | "description": "Settings tab"
114 | },
115 | "o_tabs_about": {
116 | "message": "About",
117 | "description": "About tab"
118 | },
119 | "o_error": {
120 | "message": "Save error",
121 | "description": "Error during save settings"
122 | },
123 | "o_saved": {
124 | "message": "Saved",
125 | "description": "Settings saved OK"
126 | },
127 | "o_savedTabs_legend": {
128 | "message": "Filter for tabs saving (not sessions)",
129 | "description": "Legend tag"
130 | },
131 | "o_savedTabs_pinned": {
132 | "message": "Add pinned tabs to set",
133 | "description": ""
134 | },
135 | "o_savedTabs_cur_win": {
136 | "message": "Current window only",
137 | "description": ""
138 | },
139 | "o_common_legend": {
140 | "message": "Main",
141 | "description": ""
142 | },
143 | "o_dateFormat_text": {
144 | "message": "Date format",
145 | "description": ""
146 | },
147 | "o_dateFormat_eu": {
148 | "message": "DD-MM-YYYY",
149 | "description": "EU date format"
150 | },
151 | "o_dateFormat_us": {
152 | "message": "YYYY-MM-DD",
153 | "description": "US date format"
154 | },
155 | "o_help_syncStorage": {
156 | "message": "Tab Groups are stored in the sync storage. Sign in to browser to get them on other PC.",
157 | "description": ""
158 | },
159 | "o_help_syncStorage_0": {
160 | "message": "Sessions are not sync.",
161 | "description": ""
162 | },
163 | "o_help_syncStorage_1": {
164 | "message": "Sync storage is limited in size. You can see storage usage (No.1 on the screenshot).",
165 | "description": ""
166 | },
167 | "o_help_syncStorage_2": {
168 | "message": "There is also a limit on the size of the recorded data at a time. It is approximately 17 - 24 tabs.",
169 | "description": ""
170 | },
171 | "o_help_tipsLegend": {
172 | "message": "Tips",
173 | "description": ""
174 | },
175 | "o_help_quickAdd": {
176 | "message": "To quickly save all open tabs in a group, just open extension window and press 'ENTER'.",
177 | "description": ""
178 | },
179 | "o_help_showLinks": {
180 | "message": "To view tabs in saved group click on gray triangle (No.2 on the screenshot)",
181 | "description": ""
182 | },
183 | "o_help_inNewWindow": {
184 | "message": "'Ctrl + click' on group name (or session) will open it in a new window. Or click on the icon next to the group name (No.3 on the screenshot)",
185 | "description": ""
186 | },
187 | "o_help_editGroup": {
188 | "message": "You can edit, delete, change position for every saved group (No.4 on the screenshot)",
189 | "description": ""
190 | },
191 | "o_help_overFavicon": {
192 | "message": "Place mouse pointer over favicon to view url of tab.",
193 | "description": ""
194 | },
195 | "o_help_settings": {
196 | "message": "Look at the extensions settings",
197 | "description": ""
198 | },
199 | "o_sessionWatcher_toggle": {
200 | "message": "Watch sessions. Unchecked will disable the automatic session saving. Extension will reload automatically!!!",
201 | "description": ""
202 | },
203 |
204 | "o_backup_backupLegend": {
205 | "message": "Backup ",
206 | "description": ""
207 | },
208 | "o_backup_backupInstruction": {
209 | "message": "Copy text from text area below (CTRL+A to select whole text and CTRL+C to copy). Then past (CTRL+V) and save text in a file. For example TabHamsterBackup.txt. Use UTF-8 encoding for this file.",
210 | "description": ""
211 | },
212 | "o_backup_backupInstructionMore": {
213 | "message": "You will be able to use this data to restore your saved groups and extension options. You cannot restore sessions.",
214 | "description": ""
215 | },
216 |
217 | "o_backup_restoreLegend": {
218 | "message": "Restore",
219 | "description": ""
220 | },
221 | "o_backup_restoreInstruction": {
222 | "message": "Past your previously saved backup in this text area. Make sure that you past UTF-8 encoded text. Then press 'Restore...' button. Extension will be automatically restarted.",
223 | "description": ""
224 | },
225 | "o_backup_restoreInstructionMore": {
226 | "message": "Attention! All your currently saved groups and options will be erased.",
227 | "description": ""
228 | },
229 | "o_backup_restoreButton": {
230 | "message": "Restore options and saved groups",
231 | "description": ""
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/_locales/es/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extName": {
3 | "message": "TabHamster",
4 | "description": "no traducir el nombre de la extensión"
5 | },
6 | "extDescr": {
7 | "message": "Administrador de sesiones y almacén de pestañas. Con sincronización.",
8 | "description": ""
9 | },
10 |
11 | "p_optionsBtn": {
12 | "message": "Opciones",
13 | "description": "Prefix 'p_' - popup"
14 | },
15 | "p_helpBtn": {
16 | "message": "Ayuda",
17 | "description": "Título para el botón de ayuda"
18 | },
19 | "p_GroupNameToSave": {
20 | "message": "Pestañas a un nuevo grupo",
21 | "description": "Indicador de lugar para ingreso de texto"
22 | },
23 | "p_groupBtn": {
24 | "message": "Guardar",
25 | "description": "Ayuda para Botón"
26 | },
27 | "p_tabName_Saved": {
28 | "message": "Grupos guardados",
29 | "description": ""
30 | },
31 | "p_tabName_Sessions": {
32 | "message": "Sesiones",
33 | "description": ""
34 | },
35 | "p_synStorageUsage_title": {
36 | "message": "Uso del espacio de sincronización del Navegador",
37 | "description": ""
38 | },
39 |
40 | "p_newGroupName_popup": {
41 | "message": "Nuevo nombre para el grupo:",
42 | "description": ""
43 | },
44 | "p_addLinkTitle_popup": {
45 | "message": "Título",
46 | "description": ""
47 | },
48 | "p_addLinkLink_popup": {
49 | "message": "URL",
50 | "description": ""
51 | },
52 | "p_delGroup_btn_title": {
53 | "message": "Eliminar grupo"
54 | },
55 | "p_editGroupName_btn_title": {
56 | "message": "Editar nombre del grupo"
57 | },
58 | "p_groupInNewWindow_btn_title": {
59 | "message": "Abrir en una nueva ventana (Ctrl + click)"
60 | },
61 | "p_addLinkToGroup_btn_title": {
62 | "message": "Nuevo vínculo a grupo"
63 | },
64 | "p_delLinkFromGroup_btn_title": {
65 | "message": "Eliminar"
66 | },
67 | "p_editLink_btn_title": {
68 | "message": "Editar"
69 | },
70 | "p_upGroup_btn_title": {
71 | "message": "Subir"
72 | },
73 | "p_downGroup_btn_title": {
74 | "message": "Bajar"
75 | },
76 | "p_windowsNumbers_text": {
77 | "message": "Ventana:"
78 | },
79 | "p_tabsNumbers_text": {
80 | "message": "Pestaña:"
81 | },
82 | "p_window_text": {
83 | "message": "Ventana"
84 | },
85 | "p_quotaBitesPerItem_error": {
86 | "message": "No se puede guardar al almacenamiento de sincronización. Muchas Pestañas. Intente cerrar alguna."
87 | },
88 | "p_quotaBites_error": {
89 | "message": "El espacio de Sincronización está lleno. Eliminar grupos en desuso."
90 | },
91 | "p_syncStorageDefault_error": {
92 | "message": "Error al almacenar sincronización. Intente nuevamente luego."
93 | },
94 | "p_currentSession": {
95 | "message": "Actual"
96 | },
97 |
98 |
99 | "o_tabs_help": {
100 | "message": "Ayuda",
101 | "description": "Nombre de la Pestaña. Prefijo 'o_' por Opciones"
102 | },
103 | "o_tabs_main": {
104 | "message": "Opciones",
105 | "description": "Pestaña Opciones"
106 | },
107 | "o_tabs_about": {
108 | "message": "Acerca de",
109 | "description": "Pestaña con información del producto"
110 | },
111 | "o_error": {
112 | "message": "Error al guardar",
113 | "description": "Error al guardar las opciones"
114 | },
115 | "o_saved": {
116 | "message": "Guardado",
117 | "description": "Opciones correctamente guardadas"
118 | },
119 | "o_savedTabs_legend": {
120 | "message": "Filtro para guardado de pestañas (no sesiones)",
121 | "description": "Etiqueta para leyenda"
122 | },
123 | "o_savedTabs_pinned": {
124 | "message": "Agregar pestañas Fijas al conjunto",
125 | "description": ""
126 | },
127 | "o_savedTabs_cur_win": {
128 | "message": "Únicamente ventana actual",
129 | "description": ""
130 | },
131 | "o_common_legend": {
132 | "message": "Principal",
133 | "description": ""
134 | },
135 | "o_dateFormat_text": {
136 | "message": "Formato de fecha",
137 | "description": ""
138 | },
139 | "o_dateFormat_eu": {
140 | "message": "DD-MM-YYYY",
141 | "description": "Formato europeo"
142 | },
143 | "o_dateFormat_us": {
144 | "message": "YYYY-MM-DD",
145 | "description": "Formato americano"
146 | },
147 | "o_help_syncStorage": {
148 | "message": "Los grupos de pestañas son guardados en el espacio de sincronización. Inicie sesión en el navegador para obtenerlos en otro dispositivo.",
149 | "description": ""
150 | },
151 | "o_help_syncStorage_0": {
152 | "message": "Las sesiones no se sincronizan.",
153 | "description": ""
154 | },
155 | "o_help_syncStorage_1": {
156 | "message": "El espacio de sincronización es de tamaño limitado. Puede consultar el uso de dicho espacio (Vea No.1 en la captura de pantalla).",
157 | "description": ""
158 | },
159 | "o_help_syncStorage_2": {
160 | "message": "Existe también un limite en el tamaño de la información guardada para un mismo momento. Es de aproximadamente 17 a 24 pestañas.",
161 | "description": ""
162 | },
163 | "o_help_tipsLegend": {
164 | "message": "Consejos",
165 | "description": ""
166 | },
167 | "o_help_quickAdd": {
168 | "message": "Para guardar rápidamente todas las pestañas abiertas en un grupo, abra la ventana de la extensión y presione 'ENTER'.",
169 | "description": ""
170 | },
171 | "o_help_showLinks": {
172 | "message": "Para ver las pestañas guardadas en un grupo presione sobre el triángulo gris (No.2 en la captura de pantalla)",
173 | "description": ""
174 | },
175 | "o_help_inNewWindow": {
176 | "message": "'Ctrl + click' en el nombre de un grupo (o sesión) lo abrirá en una nueva ventana. O presione en el icono al costado del nombre del grupo (No.3 en la captura de pantalla).",
177 | "description": ""
178 | },
179 | "o_help_editGroup": {
180 | "message": "Usted puede editar, eliminar y cambiar la posición para cada grupo guardado (No.4 en la captura de pantalla)",
181 | "description": ""
182 | },
183 | "o_help_overFavicon": {
184 | "message": "Ubique el puntero del ratón sobre el favicon para ver la url de una pestaña.",
185 | "description": ""
186 | },
187 | "o_help_settings": {
188 | "message": "Mire las opciones de la extensión",
189 | "description": ""
190 | }
191 | }
--------------------------------------------------------------------------------
/_locales/fr/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extName": {
3 | "message": "TabHamster",
4 | "description": "ne pas traduire le nom de l'extension"
5 | },
6 | "extDescr": {
7 | "message": "Gestionnaire de session et sauveur d'onglets. Avec synchronisation",
8 | "description": ""
9 | },
10 |
11 | "p_optionsBtn": {
12 | "message": "Paramètres",
13 | "description": "Préfixe 'p_' - popup"
14 | },
15 | "p_helpBtn": {
16 | "message": "Aide",
17 | "description": "Titre pour le bouton d'aide"
18 | },
19 | "p_GroupNameToSave": {
20 | "message": "Nouveau groupe pour les onglets ouverts",
21 | "description": "Espace réservé à la saisie de texte"
22 | },
23 | "p_groupBtn": {
24 | "message": "Sauvegarder",
25 | "description": "Btn hint"
26 | },
27 | "p_tabName_Saved": {
28 | "message": "Groupes Sauvegardés",
29 | "description": ""
30 | },
31 | "p_tabName_Sessions": {
32 | "message": "Sessions",
33 | "description": ""
34 | },
35 | "p_synStorageUsage_title": {
36 | "message": "Utilisation du stockage de synchronisation du navigateur",
37 | "description": ""
38 | },
39 |
40 | "p_newGroupName_popup": {
41 | "message": "Nom du nouveau groupe :",
42 | "description": ""
43 | },
44 | "p_addLinkTitle_popup": {
45 | "message": "Titre",
46 | "description": ""
47 | },
48 | "p_addLinkLink_popup": {
49 | "message": "URL",
50 | "description": ""
51 | },
52 | "p_delGroup_btn_title": {
53 | "message": "Suppr. groupe"
54 | },
55 | "p_editGroupName_btn_title": {
56 | "message": "Modifier le nom du groupe"
57 | },
58 | "p_groupInNewWindow_btn_title": {
59 | "message": "Ouvrir dans une nouvelle fenêtre (Ctrl + clic)"
60 | },
61 | "p_addLinkToGroup_btn_title": {
62 | "message": "Nouveau lien dans le groupe"
63 | },
64 | "p_delLinkFromGroup_btn_title": {
65 | "message": "Supprimer"
66 | },
67 | "p_editLink_btn_title": {
68 | "message": "Modifier"
69 | },
70 | "p_upGroup_btn_title": {
71 | "message": "Haut"
72 | },
73 | "p_downGroup_btn_title": {
74 | "message": "Bas"
75 | },
76 | "p_windowsNumbers_text": {
77 | "message": "Fenêtre :"
78 | },
79 | "p_tabsNumbers_text": {
80 | "message": "Onglets :"
81 | },
82 | "p_window_text": {
83 | "message": "Fenêtre"
84 | },
85 | "p_quotaBitesPerItem_error": {
86 | "message": "Impossible de synchroniser avec l'espace de stockage. Trop d'onglets. Essayez d'en fermer un de plus."
87 | },
88 | "p_quotaBites_error": {
89 | "message": "L'espace de synchronisation est plein. Supprimer des groupes non utilisés."
90 | },
91 | "p_syncStorageDefault_error": {
92 | "message": "Erreur de synchronisation. Essayez plus tard."
93 | },
94 | "p_currentSession": {
95 | "message": "Actuel"
96 | },
97 |
98 |
99 | "o_tabs_help": {
100 | "message": "Aide",
101 | "description": "Nom d'onglet. Préfixe 'o_' pour la page d'options"
102 | },
103 | "o_tabs_main": {
104 | "message": "Paramètres",
105 | "description": "Onglet des paramètres"
106 | },
107 | "o_tabs_about": {
108 | "message": "À Propos",
109 | "description": "Onglet à propos"
110 | },
111 | "o_error": {
112 | "message": "Erreur de sauvegarde",
113 | "description": "Erreur pendant la sauvegarde des paramètres"
114 | },
115 | "o_saved": {
116 | "message": "Sauvegardé",
117 | "description": "Paramètres sauvegardés OK"
118 | },
119 | "o_savedTabs_legend": {
120 | "message": "Filtre pour les onglets sauvegardés (exceptés les sessions)",
121 | "description": "Légende de l'étiquette"
122 | },
123 | "o_savedTabs_pinned": {
124 | "message": "Ajouter les onglets épinglés",
125 | "description": ""
126 | },
127 | "o_savedTabs_cur_win": {
128 | "message": "Fenêtre actuel seulement",
129 | "description": ""
130 | },
131 | "o_common_legend": {
132 | "message": "Principal",
133 | "description": ""
134 | },
135 | "o_dateFormat_text": {
136 | "message": "Format de la date",
137 | "description": ""
138 | },
139 | "o_dateFormat_eu": {
140 | "message": "DD-MM-YYYY",
141 | "description": "Format de date EU"
142 | },
143 | "o_dateFormat_us": {
144 | "message": "YYYY-MM-DD",
145 | "description": "Format de date US"
146 | },
147 | "o_help_syncStorage": {
148 | "message": "Les groupes d'onglets sont stockés dans l'espace de stockage synchronisé. Connectez-vous au navigateur pour les récupérer sur un autre PC.",
149 | "description": ""
150 | },
151 | "o_help_syncStorage_0": {
152 | "message": "Les sessions ne sont pas synchronisés",
153 | "description": ""
154 | },
155 | "o_help_syncStorage_1": {
156 | "message": "L'espace de stockage synchronisé est limité en taille. Vous pouvez voir l'espace utilisé (No.1 sur la capture d'écran).",
157 | "description": ""
158 | },
159 | "o_help_syncStorage_2": {
160 | "message": "Il existe également une limite à la taille de données enregistrées en une fois. Approximativement 17 - 24 onglets.",
161 | "description": ""
162 | },
163 | "o_help_tipsLegend": {
164 | "message": "Astuces",
165 | "description": ""
166 | },
167 | "o_help_quickAdd": {
168 | "message": "Pour rapidement sauvegarder tous les onglets ouvert dans un groupe, ouvrez juste la fenêtre d'extension et appuyez sur 'ENTER'.",
169 | "description": ""
170 | },
171 | "o_help_showLinks": {
172 | "message": "Pour voir les onglets d'un groupe cliquez sur le triangle gris (No.2 sur la capture d'écran)",
173 | "description": ""
174 | },
175 | "o_help_inNewWindow": {
176 | "message": "'Ctrl + clic' sur le nom du groupe (ou de la session) l'ouvrira dans une nouvelle fenêtre. Ou cliquez sur l'icône à côté du nom du groupe (No.3 sur la capture d'écran)",
177 | "description": ""
178 | },
179 | "o_help_editGroup": {
180 | "message": "Vous pouvez modifier, supprimer, changer la position de chaque groupe sauvegardé (No.4 sur la capture d'écran)",
181 | "description": ""
182 | },
183 | "o_help_overFavicon": {
184 | "message": "Placer le curseur de la souris sur le favicon pour voir l'url de l'onglet.",
185 | "description": ""
186 | },
187 | "o_help_settings": {
188 | "message": "Voir les paramètres de l'extension",
189 | "description": ""
190 | },
191 | "o_sessionWatcher_toggle": {
192 | "message": "Regarde les sessions. Décocher permet de désactiver la sauvegarde automatique de la session. Redémarrer le navigateur pour que les changements prennent effet",
193 | "description": ""
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/_locales/hu/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extDescr": {
3 | "description": "",
4 | "message": "Böngészési folyamatok kezelése és fülek mentése. Szinkronizációval"
5 | },
6 | "extName": {
7 | "description": "do not translate extension name",
8 | "message": "TabHamster"
9 | },
10 | "o_common_legend": {
11 | "description": "",
12 | "message": "Fő beállítások"
13 | },
14 | "o_dateFormat_eu": {
15 | "description": "EU date format",
16 | "message": "nap / hónap / év"
17 | },
18 | "o_dateFormat_text": {
19 | "description": "",
20 | "message": "Időformátum"
21 | },
22 | "o_dateFormat_us": {
23 | "description": "US date format",
24 | "message": "év / hónap / nap"
25 | },
26 | "o_error": {
27 | "description": "Error during save settings",
28 | "message": "Hiba a mentésnél"
29 | },
30 | "o_help_editGroup": {
31 | "description": "",
32 | "message": "Egyszerűen tudjuk szerkeszteni, törölni és módosítani a mentett csoportokat (a negyedik pont a képernyőfotón)."
33 | },
34 | "o_help_inNewWindow": {
35 | "description": "",
36 | "message": "'Ctrl + klikk' a mentett csoport (vagy folyamat) nevén új ablakba nyitja meg a füleket. Járható út még a kis ikonokra történő kattintás is. (a harmadik pont a képernyőképen)"
37 | },
38 | "o_help_overFavicon": {
39 | "description": "",
40 | "message": "Ha az egérmutatót a favikonokra mozgatjuk, akkor rögtön meglátjuk a hivatkozásokat."
41 | },
42 | "o_help_quickAdd": {
43 | "description": "",
44 | "message": "A kiterjesztés ablakában az ENTER billentyű lenyomásával azonnal egy csoportba menthetjük a megnyitott füleket."
45 | },
46 | "o_help_settings": {
47 | "description": "",
48 | "message": "Tekintsük át a beállításokat!"
49 | },
50 | "o_help_showLinks": {
51 | "description": "",
52 | "message": "Ahhoz, hogy áttekintsük a csoportba mentett lapokat, kattintsunk a szürke háromszögre (a második pont a képernyőfotón)."
53 | },
54 | "o_help_syncStorage": {
55 | "description": "",
56 | "message": "A fülcsoportok bekerülnek a szinkronizációs adatbázisba. Ezeket a bejelentkezés után egy másik számítógépen is elérhetjük."
57 | },
58 | "o_help_syncStorage_0": {
59 | "description": "",
60 | "message": "A folyamatok nem kerülnek szinkronizálásra."
61 | },
62 | "o_help_syncStorage_1": {
63 | "description": "",
64 | "message": "A szinkronizációs adatbázis mérete limitált. A kihasználtságot mi magunk is ellenőrizhetjük (első pont a lenti képernyőképen)."
65 | },
66 | "o_help_syncStorage_2": {
67 | "description": "",
68 | "message": "Az egy időben történő adatrögzítés limitálva van. Ez körülbelül 17 - 24 fület jelent."
69 | },
70 | "o_help_tipsLegend": {
71 | "description": "",
72 | "message": "Tippek"
73 | },
74 | "o_saved": {
75 | "description": "Settings saved OK",
76 | "message": "Mentve"
77 | },
78 | "o_savedTabs_cur_win": {
79 | "description": "",
80 | "message": "Csak a jelenlegi ablak"
81 | },
82 | "o_savedTabs_legend": {
83 | "description": "Legend tag",
84 | "message": "Szűrő a fülek mentéséhez (nem folyamatok)"
85 | },
86 | "o_savedTabs_pinned": {
87 | "description": "",
88 | "message": "A rögzített füleket is hozzáadja"
89 | },
90 | "o_tabs_about": {
91 | "description": "About tab",
92 | "message": "Névjegy"
93 | },
94 | "o_tabs_help": {
95 | "description": "Tab name. Prefix 'o_' for options page",
96 | "message": "Segítség"
97 | },
98 | "o_tabs_main": {
99 | "description": "Settings tab",
100 | "message": "Beállítások"
101 | },
102 | "p_GroupNameToSave": {
103 | "description": "Placeholder in text input",
104 | "message": "Új csoport a megnyitott fülekkel"
105 | },
106 | "p_addLinkLink_popup": {
107 | "description": "",
108 | "message": "Hivatkozás"
109 | },
110 | "p_addLinkTitle_popup": {
111 | "description": "",
112 | "message": "Cím"
113 | },
114 | "p_addLinkToGroup_btn_title": {
115 | "message": "Új hivatkozás a csoportba"
116 | },
117 | "p_currentSession": {
118 | "message": "Aktuális"
119 | },
120 | "p_delGroup_btn_title": {
121 | "message": "Csoport törlés"
122 | },
123 | "p_delLinkFromGroup_btn_title": {
124 | "message": "Törlés"
125 | },
126 | "p_downGroup_btn_title": {
127 | "message": "Le"
128 | },
129 | "p_editGroupName_btn_title": {
130 | "message": "Csoportnév szerkesztés"
131 | },
132 | "p_editLink_btn_title": {
133 | "message": "Szerkesztés"
134 | },
135 | "p_groupBtn": {
136 | "description": "Btn hint",
137 | "message": "Mentés"
138 | },
139 | "p_groupInNewWindow_btn_title": {
140 | "message": "Megnyitás új ablakban (Ctrl + klikk)"
141 | },
142 | "p_helpBtn": {
143 | "description": "Title for help button",
144 | "message": "Segítség"
145 | },
146 | "p_newGroupName_popup": {
147 | "description": "",
148 | "message": "Az új csoport neve:"
149 | },
150 | "p_optionsBtn": {
151 | "description": "Prefix 'p_' - popup",
152 | "message": "Beállítások"
153 | },
154 | "p_quotaBitesPerItem_error": {
155 | "message": "A szinkronizációs adatbázisba történő mentés nem sikerült. Túl sok a fül. Próbáljon meg bezárni párat."
156 | },
157 | "p_quotaBites_error": {
158 | "message": "A szinkronizációs adatbázis megtelt. Érdemes törölni a használaton kívüli csoportokat."
159 | },
160 | "p_synStorageUsage_title": {
161 | "description": "",
162 | "message": "A böngészők szinkronizációs adatbázisának kihasználtsága"
163 | },
164 | "p_syncStorageDefault_error": {
165 | "message": "Szinkronizációs hiba. Próbálja megismételni a műveletet."
166 | },
167 | "p_tabName_Saved": {
168 | "description": "",
169 | "message": "Mentett csoportok"
170 | },
171 | "p_tabName_Sessions": {
172 | "description": "",
173 | "message": "Folyamatok"
174 | },
175 | "p_tabsNumbers_text": {
176 | "message": "Fülek:"
177 | },
178 | "p_upGroup_btn_title": {
179 | "message": "Fel"
180 | },
181 | "p_window_text": {
182 | "message": "Ablak"
183 | },
184 | "p_windowsNumbers_text": {
185 | "message": "Ablak:"
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/_locales/sr/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extDescr": {
3 | "description": "",
4 | "message": "Менаџер сесија и чувар картица"
5 | },
6 | "extName": {
7 | "description": "do not translate extension name",
8 | "message": "TabHamster"
9 | },
10 | "o_backup_backupInstruction": {
11 | "description": "",
12 | "message": "Копирајте текст са текстуалног поља испод (CTRL+A за избор целог текста и CTRL+C за копирање). Затим налепите (CTRL+V) и сачувајте текст у датотеку, на пример TabHamsterBackup.txt (користите UTF-8 кодирање)."
13 | },
14 | "o_backup_backupInstructionMore": {
15 | "description": "",
16 | "message": "Моћи ћете да користите ове податке за враћање сачуваних група и опција проширења. Не можете вратити сесије."
17 | },
18 | "o_backup_backupLegend": {
19 | "description": "",
20 | "message": "Сачувај резервну копију "
21 | },
22 | "o_backup_restoreButton": {
23 | "description": "",
24 | "message": "Врати опције и сачуване групе"
25 | },
26 | "o_backup_restoreInstruction": {
27 | "description": "",
28 | "message": "Налепите претходно сачувану резервну копију у ово текстуално поље (будите сигурни да сте налепили UTF-8 кодирани текст) а затим притисните дугме 'Врати...'. Проширење ће се аутоматски поново покренути."
29 | },
30 | "o_backup_restoreInstructionMore": {
31 | "description": "",
32 | "message": "Пажња! Све тренутне сачуване групе и опције ће бити избрисане."
33 | },
34 | "o_backup_restoreLegend": {
35 | "description": "",
36 | "message": "Врати"
37 | },
38 | "o_common_legend": {
39 | "description": "",
40 | "message": "Опште"
41 | },
42 | "o_dateFormat_eu": {
43 | "description": "EU date format",
44 | "message": "ДД-ММ-ГГГГ"
45 | },
46 | "o_dateFormat_text": {
47 | "description": "",
48 | "message": "Формат датума"
49 | },
50 | "o_dateFormat_us": {
51 | "description": "US date format",
52 | "message": "ГГГГ-ММ-ДД"
53 | },
54 | "o_error": {
55 | "description": "Error during save settings",
56 | "message": "Грешка приликом чувања подешавања"
57 | },
58 | "o_help_editGroup": {
59 | "description": "",
60 | "message": "Можете уређивати, брисати, мењати позицију сваке сачуване групе (бр.4 на слици)."
61 | },
62 | "o_help_inNewWindow": {
63 | "description": "",
64 | "message": "'Ctrl + клик' на име групе (или сесије) ће отворити картице у новом прозору. Такође и клик на иконицу поред имена групе (бр.3 на слици)."
65 | },
66 | "o_help_overFavicon": {
67 | "description": "",
68 | "message": "Поставите курсор миша преко фавикона да бисте видели УРЛ картице."
69 | },
70 | "o_help_quickAdd": {
71 | "description": "",
72 | "message": "Да бисте брзо сачували све отворене картице као групу, само отворите прозор проширења и притисните 'ENTER'."
73 | },
74 | "o_help_settings": {
75 | "description": "",
76 | "message": "У подешавањима проширења можете одабрати формат датума и изменити филтер за чување картица."
77 | },
78 | "o_help_showLinks": {
79 | "description": "",
80 | "message": "Да бисте видели картице у сачуваној групи кликните на сиви троугао (бр.2 на слици)."
81 | },
82 | "o_help_syncStorage": {
83 | "description": "",
84 | "message": "Групе картица се чувају у складишту синхронизације. Пријавите се својим налогом у прегледачу да бисте их имали на другом рачунару."
85 | },
86 | "o_help_syncStorage_0": {
87 | "description": "",
88 | "message": "Сесије се не синхронизују."
89 | },
90 | "o_help_syncStorage_1": {
91 | "description": "",
92 | "message": "Складиште синхронизације је ограничено величином. Можете видети искоришћеност складишта (бр.1 на слици). Ако је складиште пуно, избришите групе које не користите."
93 | },
94 | "o_help_syncStorage_2": {
95 | "description": "",
96 | "message": "Постоји и ограничење у величини снимљених података у исто време. То је око 17 - 24 картица."
97 | },
98 | "o_help_tipsLegend": {
99 | "description": "",
100 | "message": "Савети"
101 | },
102 | "o_saved": {
103 | "description": "Settings saved OK",
104 | "message": "Сачувано"
105 | },
106 | "o_savedTabs_cur_win": {
107 | "description": "",
108 | "message": "Само тренутни прозор"
109 | },
110 | "o_savedTabs_legend": {
111 | "description": "Legend tag",
112 | "message": "Филтер за чување картица (не сесија)"
113 | },
114 | "o_savedTabs_pinned": {
115 | "description": "",
116 | "message": "Додај закачене картице у групу"
117 | },
118 | "o_sessionWatcher_toggle": {
119 | "description": "",
120 | "message": "Надгледај сесије. Одчекирајте да бисте онемогућили аутоматско чување сесија. Проширење ће се аутоматски поново покренути!!!"
121 | },
122 | "o_tabs_about": {
123 | "description": "About tab",
124 | "message": "Информације"
125 | },
126 | "o_tabs_backup": {
127 | "description": "Tab name",
128 | "message": "Сачувај резервну копију / Врати"
129 | },
130 | "o_tabs_help": {
131 | "description": "Tab name. Prefix 'o_' for options page",
132 | "message": "Помоћ"
133 | },
134 | "o_tabs_main": {
135 | "description": "Settings tab",
136 | "message": "Подешавања"
137 | },
138 | "p_GroupNameToSave": {
139 | "description": "Placeholder in text input",
140 | "message": "Нова група од отворених картица"
141 | },
142 | "p_addLinkLink_popup": {
143 | "description": "Modal window dialog",
144 | "message": "УРЛ"
145 | },
146 | "p_addLinkTitle_popup": {
147 | "description": "Modal window dialog",
148 | "message": "Наслов"
149 | },
150 | "p_addLinkToGroup_btn_title": {
151 | "message": "Додај линк у групу"
152 | },
153 | "p_currentSession": {
154 | "message": "Тренутна"
155 | },
156 | "p_delGroup_btn_title": {
157 | "message": "Избриши групу"
158 | },
159 | "p_delLinkFromGroup_btn_title": {
160 | "message": "Избриши"
161 | },
162 | "p_downGroup_btn_title": {
163 | "message": "Доле"
164 | },
165 | "p_editGroupName_btn_title": {
166 | "message": "Уреди име групе"
167 | },
168 | "p_editLink_btn_title": {
169 | "message": "Уреди"
170 | },
171 | "p_groupBtn": {
172 | "description": "Btn hint",
173 | "message": "Сачувај"
174 | },
175 | "p_groupInNewWindow_btn_title": {
176 | "message": "Отвори у новом прозору (Ctrl + клик)"
177 | },
178 | "p_helpBtn": {
179 | "description": "Title for help button",
180 | "message": "Помоћ"
181 | },
182 | "p_newGroupName_popup": {
183 | "description": "Modal window dialog",
184 | "message": "Ново име групе:"
185 | },
186 | "p_optionsBtn": {
187 | "description": "Prefix 'p_' - popup",
188 | "message": "Подешавања"
189 | },
190 | "p_overwriteGroup_popup": {
191 | "description": "Modal window dialog",
192 | "message": "Група са истим именом већ постоји. Да ли желите да замените стару са тренутним картицама?"
193 | },
194 | "p_quotaBitesPerItem_error": {
195 | "message": "Не може се сачувати у складишту синхронизације. Има превише картица. Покушајте са брисањем једне или више њих."
196 | },
197 | "p_quotaBites_error": {
198 | "message": "Скаладиште синхронизације је пуно. Избриши некоришћене групе."
199 | },
200 | "p_synStorageUsage_title": {
201 | "description": "",
202 | "message": "Искоришћеност складишта синхронизације прегледача"
203 | },
204 | "p_syncStorageDefault_error": {
205 | "message": "Грешка при синхронизовању складишта. Покушајте поново касније."
206 | },
207 | "p_tabName_Saved": {
208 | "description": "",
209 | "message": "Сачуване групе"
210 | },
211 | "p_tabName_Sessions": {
212 | "description": "",
213 | "message": "Сесије"
214 | },
215 | "p_tabsNumbers_text": {
216 | "message": "Картица:"
217 | },
218 | "p_upGroup_btn_title": {
219 | "message": "Горе"
220 | },
221 | "p_window_text": {
222 | "message": "Прозор"
223 | },
224 | "p_windowsNumbers_text": {
225 | "message": "Прозора:"
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/_locales/uk/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extName": {
3 | "message": "TabHamster",
4 | "description": "Ім'я розширення. Не перекладати"
5 | },
6 | "extDescr": {
7 | "message": "Зберігай відкриті вкладки, керуй сесіями браузера",
8 | "description": "Короткий опис розширення"
9 | },
10 |
11 | "p_optionsBtn": {
12 | "message": "Налаштування",
13 | "description": "Підказка для кнопки. Префікс 'p_' вказує, що текст відноситься до popup"
14 | },
15 | "p_helpBtn": {
16 | "message": "Допомога",
17 | "description": "Підказка для кнопки"
18 | },
19 | "p_GroupNameToSave": {
20 | "message": "Нова група з відкритих вкладок",
21 | "description": "Placeholder в текстовому полі для нового набору"
22 | },
23 | "p_groupBtn": {
24 | "message": "Зберегти відкриті в групу",
25 | "description": "Підказка для кнопки."
26 | },
27 | "p_tabName_Saved": {
28 | "message": "Збережені",
29 | "description": "Ім'я таба для збережених груп вкладок"
30 | },
31 | "p_tabName_Sessions": {
32 | "message": "Сесії",
33 | "description": "Ім'я таба для збережених сесій"
34 | },
35 | "p_synStorageUsage_title": {
36 | "message": "Відсоток використання сховища Chrome",
37 | "description": "Хінт для показу зайнятого місця в сховищі"
38 | },
39 |
40 | "p_newGroupName_popup": {
41 | "message": "Нове ім'я групи:",
42 | "description": "Модальне вікно для редагування імені групи вкладок"
43 | },
44 | "p_addLinkTitle_popup": {
45 | "message": "Назва",
46 | "description": "Модальне вікно для редагування імені групи вкладок"
47 | },
48 | "p_addLinkLink_popup": {
49 | "message": "URL",
50 | "description": "Модальне вікно для редагування імені групи вкладок"
51 | },
52 | "p_overwriteGroup_popup": {
53 | "message": "Група з таким ім'ям вже існує. Переписати вже існуючу групу поточним набором вкладок?",
54 | "description": "Модальне вікно "
55 | },
56 | "p_delGroup_btn_title": {
57 | "message": "Видалити групу"
58 | },
59 | "p_editGroupName_btn_title": {
60 | "message": "Змінити ім'я групи"
61 | },
62 | "p_groupInNewWindow_btn_title": {
63 | "message": "Відкрити в новому вікні (Ctrl + клік)"
64 | },
65 | "p_addLinkToGroup_btn_title": {
66 | "message": "Додати посилання в групу"
67 | },
68 | "p_delLinkFromGroup_btn_title": {
69 | "message": "Видалити з групи"
70 | },
71 | "p_editLink_btn_title": {
72 | "message": "Редагувати"
73 | },
74 | "p_upGroup_btn_title": {
75 | "message": "Вгору"
76 | },
77 | "p_downGroup_btn_title": {
78 | "message": "Вниз"
79 | },
80 | "p_windowsNumbers_text": {
81 | "message": "Вікон:"
82 | },
83 | "p_tabsNumbers_text": {
84 | "message": "Табів:"
85 | },
86 | "p_window_text": {
87 | "message": "Вікно"
88 | },
89 | "p_quotaBitesPerItem_error": {
90 | "message": "Забагато табів для запису в сховище. Закрийте декілька та спробуйте знову."
91 | },
92 | "p_quotaBites_error": {
93 | "message": "Сховище заповнене. Видаліть групи, які не використовуються."
94 | },
95 | "p_syncStorageDefault_error": {
96 | "message": "Помилка під час роботи з сховищем. Спробуйте пізніше."
97 | },
98 | "p_currentSession": {
99 | "message": "Поточна"
100 | },
101 |
102 |
103 | "o_tabs_help": {
104 | "message": "Допомога",
105 | "description": "Підказка для кнопки. Префікс 'o_' вказує, що текст відноситься до options.html"
106 | },
107 | "o_tabs_backup": {
108 | "message": "Резерв / Відновлення",
109 | "description": "Ім'я вкладки"
110 | },
111 | "o_tabs_main": {
112 | "message": "Налаштування",
113 | "description": "Ім'я вкладки"
114 | },
115 | "o_tabs_about": {
116 | "message": "Про розширення",
117 | "description": "Ім'я вкладки"
118 | },
119 | "o_error": {
120 | "message": "Помилка під час збереження налаштувань",
121 | "description": "Повідомлення про помилку під час збереження налаштувань в storage"
122 | },
123 | "o_saved": {
124 | "message": "Зміни збережені",
125 | "description": "Повідомлення про успішне збереження настройок"
126 | },
127 | "o_savedTabs_legend": {
128 | "message": "Фільтр для вкладок що зберігаються (На сесії не поширюється)",
129 | "description": "Legend tag"
130 | },
131 | "o_savedTabs_pinned": {
132 | "message": "Додавати закріплені вкладки (Pinned tabs) в набір",
133 | "description": ""
134 | },
135 | "o_savedTabs_cur_win": {
136 | "message": "Зберігати в набір тільки вкладки з поточного вікна",
137 | "description": ""
138 | },
139 | "o_common_legend": {
140 | "message": "Загальні",
141 | "description": ""
142 | },
143 | "o_dateFormat_text": {
144 | "message": "Формат дати",
145 | "description": ""
146 | },
147 | "o_dateFormat_eu": {
148 | "message": "ДД-MM-РРРР",
149 | "description": "EU date format"
150 | },
151 | "o_dateFormat_us": {
152 | "message": "РРРР-MM-ДД",
153 | "description": "US date format"
154 | },
155 | "o_help_syncStorage": {
156 | "message": "Групи табів зберігаються у сховище браузера. Це сховище синхронізується. Всі збережені вкладки будуть доступні, наприклад, дома і на роботі. За умови, якщо вхід у браузер виконаний з вашим аккаунтом.",
157 | "description": ""
158 | },
159 | "o_help_syncStorage_0": {
160 | "message": "Сесії не синхронізуються.",
161 | "description": ""
162 | },
163 | "o_help_syncStorage_1": {
164 | "message": "Сховище для синхронізації має обмежений розмір. Відсоток його використання, можна побачити біля вкладки (№1 на скріншоті). В більшості випадків цього розміру буде достатньо. Якщо сховище буде переповнене, то можна видалити групи, що не використовуються.",
165 | "description": ""
166 | },
167 | "o_help_syncStorage_2": {
168 | "message": "Також є обмеження на розмір даних що записуються за один раз. Це приблизно 17 - 24 вкладок (табів).",
169 | "description": ""
170 | },
171 | "o_help_tipsLegend": {
172 | "message": "Поради та підказки",
173 | "description": ""
174 | },
175 | "o_help_quickAdd": {
176 | "message": "Щоб швидко зберегти всі відкриті таби в групу, відкрий вікно розширення та натисни клавішу 'ENTER'. Нова група буде мати ім'я з поточних дати та часу. (Пізніше можна буде відредагувати)",
177 | "description": ""
178 | },
179 | "o_help_showLinks": {
180 | "message": "Щоб продивитись збережені в групі таби клацни на сірий трикутник біля ім'я групи (№2 на скріншоті)",
181 | "description": ""
182 | },
183 | "o_help_inNewWindow": {
184 | "message": "'Ctrl+клік' на імені збереженої групи (або сесії) відкриє її в новому вікні браузера. Або клік на іконці біля ім'я групи (№3 на скріншоті)",
185 | "description": ""
186 | },
187 | "o_help_editGroup": {
188 | "message": "Збережені групи можна редагувати, видаляти, змінювати їх позицію.(№4 на скріншоті)",
189 | "description": ""
190 | },
191 | "o_help_overFavicon": {
192 | "message": "Щоб побачити url вкладки, наведіть курсор на його favicon",
193 | "description": ""
194 | },
195 | "o_help_settings": {
196 | "message": "В налаштуваннях можна вибрати формат дати та змінити фільтри для збереження табів",
197 | "description": ""
198 | },
199 | "o_sessionWatcher_toggle": {
200 | "message": "Слідкувати за сесіями. Зніміть прапорець, щоб вимкнути автоматичне збереження сесій. Розширення буде автоматично перезавантажене!!!",
201 | "description": ""
202 | },
203 |
204 | "o_backup_backupLegend": {
205 | "message": "Резерв",
206 | "description": ""
207 | },
208 | "o_backup_backupInstruction": {
209 | "message": "Скопіюй текст з текстового поля нижче (CTRL+A щоб вибрати весь текст і CTRL+C щоб скопіювати). Потім встав (CTRL+V) і збережи текст в будь-якому файлі. Наприклад, TabHamsterBackup.txt. Використовуй UTF-8 кодування для цього файлу.",
210 | "description": ""
211 | },
212 | "o_backup_backupInstructionMore": {
213 | "message": "З допомогою цих даних ти будеш мати можливість відновити свої збереженні групи і налаштування. (Дивись інструкцію в полі вище). Відновити сесії неможливо.",
214 | "description": ""
215 | },
216 |
217 | "o_backup_restoreLegend": {
218 | "message": "Відновлення",
219 | "description": ""
220 | },
221 | "o_backup_restoreInstruction": {
222 | "message": "Встав попередньо збережені резервні данні в текстове поле нижче. Данні мають бути кодовані в UTF-8. Потім натисни кнопку 'Відновити...' (під текстовим полем). В разі успіху розширення буде автоматично перезавантажене.",
223 | "description": ""
224 | },
225 | "o_backup_restoreInstructionMore": {
226 | "message": " Увага! Всі збережені на даний момент групи і налаштування будуть знищені. Замість них будуть завантажені дані з резерву.",
227 | "description": ""
228 | },
229 | "o_backup_restoreButton": {
230 | "message": "Відновити налаштування і збережені групи",
231 | "description": ""
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/build/build.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [".*", "readme.md", "build"],
3 | "reminder": "",
4 | "minify": {
5 | "dirs": ["../js", "../lib/jsTabs"],
6 | "exclude": ["*.min.js"]
7 | },
8 | "changelog": {
9 | "filename": "changelog.txt",
10 | "datetimeformat": "%d.%m.%Y %H:%M"
11 | }
12 | }
--------------------------------------------------------------------------------
/build/build.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python3
2 |
3 | ##########################################################################
4 | # Chrome Extensions builder
5 | # This is a part of Chrome Extensions Box
6 | # Read more on GitHub - https://github.com/onikienko/chrome-extensions-box
7 | ##########################################################################
8 |
9 | import os, json, fnmatch, zipfile, datetime, urllib.request, shutil, sys
10 | from zipfile import ZipFile
11 | from urllib.parse import urlencode
12 | from urllib.error import HTTPError, URLError
13 |
14 | CUR_PATH = os.path.abspath(os.curdir)
15 | PRJ_PATH = os.path.abspath(os.pardir)
16 | PRJ_PAR_DIR, PRJ_DIR_NAME = os.path.split(PRJ_PATH)
17 | BUILD_PATH = os.path.join(CUR_PATH, 'releases')
18 | PACKAGE_FILE = os.path.join(CUR_PATH, 'build.json')
19 | MANIFEST_FILE = os.path.join(PRJ_PATH, 'manifest.json')
20 | TMP_DIR = os.path.join(CUR_PATH, 'tmp')
21 |
22 | ARG_NO_MINIFY = '-m'
23 |
24 | exclude_dirs = []
25 | # TODO collect and print errors after creating a package
26 | # errors = []
27 |
28 |
29 | def getJSON(json_file):
30 | try:
31 | f = open(json_file, 'rt', encoding='utf-8')
32 | j = json.load(f)
33 | f.close()
34 | return j
35 | except:
36 | print('Error reading', json_file, 'file')
37 | exit()
38 |
39 |
40 | def isNameMatch(name, match_list):
41 | for pt in match_list:
42 | if fnmatch.fnmatch(name, pt):
43 | return True
44 | return False
45 |
46 |
47 | def writeToChangelogFile():
48 | if changelog['filename']:
49 | print('\nWriting to changelog file ...')
50 | changelog_path = os.path.join(BUILD_PATH, changelog['filename'])
51 |
52 | if changelog['datetimeformat']:
53 | try:
54 | timestamp = ' (' + datetime.datetime.now().strftime(changelog['datetimeformat']) + ')'
55 | except ValueError:
56 | print('Invalid changelog.datetimeformat string in package.json')
57 | timestamp = ''
58 | else:
59 | timestamp = ''
60 | try:
61 | if not os.path.isfile(changelog_path):
62 | f = open(changelog_path, 'w', encoding='utf-8')
63 | f.close()
64 |
65 | with open(changelog_path, 'r+', encoding='utf-8') as f:
66 | lines = f.readlines()
67 | f.seek(0)
68 | f.writelines([build_version + timestamp + '\n'] + lines)
69 |
70 | f.close()
71 | print('Ok\n')
72 | except Exception:
73 | print('Error reading or writing changelog file\n')
74 |
75 |
76 | def minifyJs(file_path, arcname):
77 | print('Start minifying', arcname[1:])
78 |
79 | try:
80 | f = open(file_path, encoding='utf-8')
81 | data = urllib.parse.urlencode({'js_code': f.read(), 'utf-8': 'utf-8'})
82 | data = data.encode('utf-8')
83 | req = urllib.request.Request(url='http://marijnhaverbeke.nl/uglifyjs')
84 | minified = urllib.request.urlopen(req, data).read().decode('utf-8')
85 | except (HTTPError, URLError) as error:
86 | print('HTTP or URL error:', error)
87 | print(arcname[1:], 'was not minified')
88 | return file_path
89 |
90 | try:
91 | if not os.path.isdir(TMP_DIR):
92 | os.makedirs(TMP_DIR)
93 | tmp_file_path = os.path.join(TMP_DIR, os.path.basename(file_path))
94 | tmp_file = open(tmp_file_path, 'w+', encoding='utf-8')
95 | tmp_file.write(minified)
96 | tmp_file.close()
97 | except Exception:
98 | print('Error during the creation temp file')
99 | print(arcname[1:], 'was not minified')
100 | return file_path
101 |
102 | print('Ok. File was minified')
103 | return tmp_file_path
104 |
105 | # get and check manifest version
106 | try:
107 | build_version = getJSON(MANIFEST_FILE)['version']
108 | except KeyError:
109 | print('Error. Check "manifest.json" file. "version" is not defined')
110 | exit()
111 | if os.path.exists(os.path.join(BUILD_PATH, build_version + '.zip')):
112 | print('Error. Version', build_version, 'already exists')
113 | exit()
114 |
115 | # get package info
116 | try:
117 | package = getJSON(PACKAGE_FILE)
118 | exclude, minify, reminder, changelog = package['exclude'], package['minify'], package['reminder'], package[
119 | "changelog"]
120 | if ARG_NO_MINIFY not in sys.argv:
121 | for index, dirname in enumerate(minify['dirs']):
122 | minify['dirs'][index] = os.path.abspath(dirname)
123 | else:
124 | minify['dirs'] = []
125 | except Exception:
126 | print('Error in build.json file')
127 | exit()
128 |
129 | if reminder:
130 | i = input(reminder + ' ' + ' Press "y" (then "ENTER") to continue or just "ENTER" to abort: ')
131 | if i.lower() != 'y':
132 | exit()
133 |
134 | if not os.path.isdir(BUILD_PATH):
135 | os.makedirs(BUILD_PATH)
136 |
137 | zipname = os.path.join(BUILD_PATH, build_version + '.zip')
138 | z = ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
139 | print('\nStart building package ', os.path.abspath(zipname), '...\n')
140 |
141 | for root, dirs, files in os.walk(PRJ_PATH):
142 | is_exclude_dir = root.startswith(tuple(exclude_dirs))
143 | if (isNameMatch(os.path.basename(root), exclude) or is_exclude_dir) and root != PRJ_PATH:
144 | if not is_exclude_dir:
145 | exclude_dirs.append(root)
146 | continue
147 | else:
148 | for name in files:
149 | if not isNameMatch(name, exclude):
150 | fullname = os.path.join(root, name)
151 | arcname = fullname[len(PRJ_PAR_DIR):]
152 | if len(minify['dirs']) > 0 and root.startswith(tuple(minify['dirs'])) and fnmatch.fnmatch(name,
153 | '*.js') and not isNameMatch(
154 | name,
155 | minify[
156 | 'exclude']):
157 | fullname = minifyJs(fullname, arcname)
158 | z.write(fullname, arcname)
159 | print(arcname[1:], 'was added to the package\n')
160 | z.close()
161 | print('Package was created')
162 |
163 | if os.path.exists(TMP_DIR):
164 | print('\nDeleting temporary files...')
165 | shutil.rmtree(TMP_DIR, ignore_errors=False, onerror=None)
166 | print('Ok')
167 |
168 | writeToChangelogFile()
169 |
170 | print('Done!')
171 | input('\nPress "Enter" to exit')
--------------------------------------------------------------------------------
/build/releases/0.1.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.1.1.zip
--------------------------------------------------------------------------------
/build/releases/0.1.3.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.1.3.zip
--------------------------------------------------------------------------------
/build/releases/0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.1.zip
--------------------------------------------------------------------------------
/build/releases/0.2.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.1.zip
--------------------------------------------------------------------------------
/build/releases/0.2.10.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.10.zip
--------------------------------------------------------------------------------
/build/releases/0.2.2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.2.zip
--------------------------------------------------------------------------------
/build/releases/0.2.3.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.3.zip
--------------------------------------------------------------------------------
/build/releases/0.2.4.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.4.zip
--------------------------------------------------------------------------------
/build/releases/0.2.5.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.5.zip
--------------------------------------------------------------------------------
/build/releases/0.2.6.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.6.zip
--------------------------------------------------------------------------------
/build/releases/0.2.7.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.7.zip
--------------------------------------------------------------------------------
/build/releases/0.2.8.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.8.1.zip
--------------------------------------------------------------------------------
/build/releases/0.2.8.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.8.zip
--------------------------------------------------------------------------------
/build/releases/0.2.9.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.9.zip
--------------------------------------------------------------------------------
/build/releases/0.2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.2.zip
--------------------------------------------------------------------------------
/build/releases/0.3.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases/0.3.0.zip
--------------------------------------------------------------------------------
/build/releases/changelog.txt:
--------------------------------------------------------------------------------
1 | 0.3.0 (12.04.2024 16:01)
2 | Manifest v3 support
3 | Fix Session Watcher behavior on the first run
4 | Fix favicon appearance if browser uses dark theme
5 | Add 32px icon
6 | Remove "ru" locale
7 | Remove Opera support
8 | 0.2.10 (08.12.2015 12:32)
9 | Add Serbian translation (thanks to Sijera)
10 | Fix Opera manifest to persistent background page
11 | Open tabs from background page (not from popup)
12 | 0.2.9 (23.09.2015 13:49)
13 | Manual Backup/Restore for saved groups and options (see Options Page).
14 | 0.2.8.1 (16.04.2015 09:29)
15 | Fix error with Group Saving when option 'Current window only' unchecked
16 | 0.2.8 (15.04.2015 10:04)
17 | Save with the same name dialogue
18 | Fix styles
19 | 0.2.7 (10.04.2015 10:54)
20 | Ability to turn off Session Watcher (see Options page)
21 | Fix locales
22 | 0.2.6 (26.11.2014 12:59)
23 | Add French translation (thanks to Aurélien Léger aka Dexyne)
24 | Changed monospaced font in popup to Tahoma, san-serif
25 | 0.2.5 (12.11.2014 17:37)
26 | Fix problem with group and session expanding (thanks to Ricardo J. Barberis aka richarson)
27 | Fix Open In New Window bug
28 | 0.2.4 (02.06.2014 18:39)
29 | Hungarian translation (thanks to Zoltan Mihics aka Med1on)
30 | 0.2.3 (12.02.2014 08:54)
31 | Add Ukrainian translation
32 | Pinned tabs are shown as Pinned in Saved Groups
33 | Pinned tabs open as Pinned
34 | 0.2.2 (31.01.2014 16:51)
35 | Fix error messages
36 | Minimize tab title to 49 chars
37 | 0.2.1 (15.10.2013 19:31)
38 | Fix error with edit link which starts with 'file://' or 'chrome-devtools://'
39 | 0.2 (14.10.2013 16:02)
40 | Spanish translation. Thanks to Martin Irigaray.
41 | Fix error with edit file:// link
42 | 0.1.3 (25.09.2013 18:43)
43 | 0.1.2 (25.09.2013 18:40)
44 | Fix error with pinned tabs
45 | Icon for pinned tabs
46 | 0.1.1 (23.09.2013 22:19)
47 | Upd ru/messages.json
48 | 0.1 (23.09.2013 22:07)
49 | Initial release
50 |
--------------------------------------------------------------------------------
/build/releases_opera/0.1.1.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.1.1.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.1.2.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.1.2.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.1.3.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.1.3.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.1.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.1.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.10.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.10.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.2.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.2.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.3.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.3.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.4.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.4.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.5.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.5.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.6.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.6.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.7.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.7.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.8.1.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.8.1.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.8.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.8.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.9.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.9.nex
--------------------------------------------------------------------------------
/build/releases_opera/0.2.nex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/build/releases_opera/0.2.nex
--------------------------------------------------------------------------------
/css/options.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #efefef;
3 | font-family: Arial, sans-serif;
4 | font-size: 15px;
5 | color: #393939;
6 | }
7 |
8 | a {
9 | color: #0099CC;
10 | }
11 |
12 | hr {
13 | border: none;
14 | background-color: #e5e5e5;
15 | height: 1px;
16 | margin: 15px 0;
17 | }
18 |
19 | #message_place {
20 | position: fixed;
21 | width: 100%;
22 | top: 0;
23 | text-align: center;
24 | font-size: 17px;
25 | font-weight: 500;
26 | margin-top: 10px;
27 | color: #ffffff;
28 | }
29 |
30 | #message_place span {
31 | padding: 12px 48px;
32 | border-bottom-left-radius: 4px;
33 | border-bottom-right-radius: 4px;
34 | box-shadow: 0 2px 10px #a9a9a9;
35 | display: none;
36 | }
37 |
38 | #error {
39 | background-color: #f0a9a5;
40 | }
41 |
42 | #success {
43 | background-color: #7bd743;
44 | }
45 |
46 | #page {
47 | margin: 20px auto;
48 | width: 800px;
49 | background-color: #ffffff;
50 | border: 1px solid #dddddd;
51 | border-radius: 3px;
52 | box-shadow: 0 3px 12px #a9a9a9;
53 | }
54 |
55 | header {
56 | padding: 20px;
57 | font-size: 18px;
58 | }
59 |
60 | header img {
61 | margin-right: 12px;
62 | vertical-align: middle;
63 | }
64 |
65 | footer {
66 | border-top: 1px solid #dddddd;
67 | padding: 0 20px;
68 | }
69 |
70 | .copyright {
71 | text-align: right;
72 | font-size: 11px;
73 | color: #a9a9a9;
74 | }
75 |
76 | dl dt {
77 | float: left;
78 | font-weight: bold;
79 | margin-right: 10px;
80 | padding: 5px;
81 | width: 100px;
82 | }
83 |
84 | dl dd {
85 | margin: 2px 0;
86 | padding: 5px 0;
87 | }
88 |
89 | fieldset {
90 | border: 1px solid #dddddd;
91 | border-radius: 3px;
92 | margin-bottom: 20px;
93 | }
94 |
95 | #help li {
96 | margin-bottom: 10px;
97 | }
98 |
99 | .help_screenshot {
100 | text-align: center;
101 | margin-bottom: 20px;
102 | }
103 |
104 | #ua_flag {
105 | height: 20px;
106 | background: url("../img/ua_1px.png") 0 50% repeat-x;
107 | }
108 |
109 | textarea {
110 | width: 100%;
111 | /*margin: 0 auto;*/
112 | }
113 |
114 | #restore_btn {
115 | margin: 15px auto;
116 | padding: 10px;
117 | text-align: center;
118 | font-weight: bold;
119 | }
120 |
--------------------------------------------------------------------------------
/css/popup.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 450px;
3 | overflow-x: hidden;
4 | font-family: Tahoma, sans-serif;
5 | font-size: 13px;
6 | color: #4a4a4a;
7 | margin-top: 50px;
8 | }
9 |
10 | header {
11 | font-size: 15px;
12 | background-color: #33B5E5;
13 | color: #ffffff;
14 | padding: 8px 16px;
15 | position: fixed;
16 | top: 0;
17 | left: 0;
18 | right: 0;
19 | }
20 |
21 | header input {
22 | outline: none;
23 | padding-left: 7px;
24 | width: 220px;
25 | }
26 |
27 | #window_actions {
28 | float: right;
29 | }
30 |
31 | header a {
32 | margin: 0 0 0 6px;
33 | text-decoration: none;
34 | color: #ffffff;
35 | font-size: 18px;
36 | }
37 |
38 | /* override jsTabs styles */
39 | ul.tabs_nav {
40 | margin-left: 10px;
41 | }
42 |
43 | .tabs_nav a {
44 | letter-spacing: normal;
45 | font-weight: normal;
46 | }
47 |
48 | .tabs_content {
49 | padding: 8px;
50 | }
51 |
52 | #error_msg {
53 | display: none;
54 | background-color: #FF4444;
55 | color: #ffffff;
56 | padding: 12px;
57 | margin: 5px 0;
58 | }
59 |
60 | #sync_storage_usage {
61 | float: right;
62 | padding: 0;
63 | margin: 5px 0 0 0;
64 | }
65 |
66 | .group {
67 | font-size: 14px;
68 | padding: 8px 3px;
69 | border-bottom: 1px solid #E2F4FB;
70 |
71 | }
72 |
73 | .open_group {
74 | margin-left: 8px;
75 | cursor: pointer;
76 | color: #0099CC;
77 | }
78 |
79 | .group_action {
80 | float: right;
81 | }
82 |
83 | .spoiler, .group_action > span, .link_action > span, .open_in_new_window {
84 | margin-left: 5px;
85 | cursor: pointer;
86 | color: lightgrey;
87 | }
88 |
89 | .spoiler:hover, .group_action > span:hover, .link_action > span:hover, .up:hover, .down:hover, .open_in_new_window:hover {
90 | color: #4a4a4a;
91 | }
92 |
93 | .link {
94 | font-size: 13px;
95 | margin-top: 8px;
96 | }
97 |
98 | .link a {
99 | color: #4a4a4a;
100 | text-decoration: none;
101 | }
102 |
103 | .link a:hover {
104 | color: #4a4a4a;
105 | text-decoration: underline;
106 | }
107 |
108 | .numbers_of_windows, .numbers_of_tabs {
109 | font-size: 13px;
110 | margin-left: 8px;
111 | color: lightgrey;
112 | }
113 |
114 | .win {
115 | padding: 8px;
116 | margin: 0 0 0 15px;
117 | }
118 |
119 | .win_title {
120 | font-size: 14px;
121 | color: #0099CC;
122 | border-bottom: 1px solid #E2F4FB;
123 | cursor: pointer;
124 | }
125 |
126 | .favi {
127 | display: inline-flex;
128 | margin: 0 8px;
129 | border-radius: 8px;
130 | background-color: lightgray;
131 | height: 16px;
132 | width: 16px;
133 | vertical-align: text-bottom;
134 | }
135 |
136 | .pinned_icon {
137 | width: 12px;
138 | height: 12px;
139 | margin-right: 3px;
140 | vertical-align: middle;
141 | }
142 |
143 | /* popup form */
144 | .popup__overlay {
145 | display: none;
146 | position: fixed;
147 | left: 0;
148 | top: 0;
149 | width: 100%;
150 | height: 100%;
151 | text-align: center;
152 | background-color: rgba(0, 0, 0, 0.5);
153 | }
154 |
155 | .popup__overlay:after {
156 | display: inline-block;
157 | height: 100%;
158 | width: 0;
159 | vertical-align: middle;
160 | content: '';
161 | }
162 |
163 | .popup {
164 | display: inline-block;
165 | position: relative;
166 | max-width: 85%;
167 | padding: 15px;
168 | border: 1px solid #606060;
169 | border-radius: 4px;
170 | background: #fdfdfd;
171 | vertical-align: middle;
172 | }
173 |
174 | .popup-form__row {
175 | margin: 1em 0;
176 | }
177 |
178 | label {
179 | display: inline-block;
180 | width: 120px;
181 | text-align: left;
182 | }
183 |
184 | input[type="text"] {
185 | width: 220px;
186 | margin: 0;
187 | padding: 2px;
188 | border: 1px solid;
189 | border-color: #999 #ccc #ccc;
190 | border-radius: 2px;
191 | }
192 |
193 | input[type="button"] {
194 | padding: 6px 16px;
195 | border: 0;
196 | border-radius: 2px;
197 | cursor: pointer;
198 | background: #33B5E5;
199 | color: #fff;
200 | }
201 |
202 | input[type="button"].cancel {
203 | background: #cccccc;
204 | }
205 |
206 | .popup__close:hover {
207 | color: red;
208 | background: #ddd;
209 | }
210 |
211 | /* popup form */
212 |
--------------------------------------------------------------------------------
/img/ext_icons/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/ext_icons/128.png
--------------------------------------------------------------------------------
/img/ext_icons/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/ext_icons/16.png
--------------------------------------------------------------------------------
/img/ext_icons/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/ext_icons/32.png
--------------------------------------------------------------------------------
/img/ext_icons/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/ext_icons/48.png
--------------------------------------------------------------------------------
/img/help_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/help_screen.png
--------------------------------------------------------------------------------
/img/pin-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/pin-26.png
--------------------------------------------------------------------------------
/img/ua_1px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onikienko/TabHamster/958b81de20f39d9765d68fddc54c554d87c0f194/img/ua_1px.png
--------------------------------------------------------------------------------
/js/background.js:
--------------------------------------------------------------------------------
1 | // it's copied from ./storage.js file
2 | // had to copy/past here to this code works with mv3 service worker
3 | var storage = {
4 | area: chrome.storage.sync,
5 | default_options: {
6 | date_format: 'eu', // or 'us'
7 | pinned: 0, //do not add pinned tabs
8 | cur_win: 1, // save tabs from current window only
9 | active_tab: '#saved', //or '#sessions'
10 | session_watcher: 1, //extension will watch sessions
11 | },
12 | };
13 |
14 | const session_config = {
15 | session_numbers: 30,
16 | prefix: 'tg_',
17 | };
18 |
19 | chrome.runtime.onInstalled.addListener(function (details) {
20 |
21 | // good place to set default options
22 | function setDefaults(callback) {
23 | storage.area.get(function (stored_options) {
24 | var default_options = storage.default_options,
25 | option,
26 | new_options = {};
27 |
28 | for (option in default_options) {
29 | if (!stored_options.hasOwnProperty(option)) {
30 | new_options[option] = default_options[option];
31 | }
32 | }
33 | if (Object.keys(new_options).length !== 0) {
34 | // save to area if new default options is appeared
35 | storage.area.set(new_options, function () {
36 | if (typeof callback === 'function') {
37 | callback();
38 | }
39 | });
40 | } else {
41 | if (typeof callback === 'function') {
42 | callback();
43 | }
44 | }
45 | });
46 | }
47 |
48 | switch (details.reason) {
49 | case 'install': // if ext is first installed
50 | setDefaults(function () {
51 | // show options page
52 | chrome.tabs.create({'url': 'options.html#help'});
53 | });
54 | break;
55 | case 'update':
56 | setDefaults();
57 | break;
58 | default:
59 | break;
60 | }
61 | });
62 |
63 | chrome.runtime.onUpdateAvailable.addListener(function (details) {
64 | chrome.runtime.reload();
65 | });
66 |
67 | chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
68 | switch (msg.msg) {
69 | case 'openLinksInNewWindow':
70 | chrome.windows.create({'type': 'normal', 'focused': true}, function (win) {
71 | msg.links.forEach(function (el) {
72 | chrome.tabs.create({'windowId': win.id, 'url': el.url, 'pinned': el.pinned});
73 | });
74 | chrome.tabs.remove(win.tabs[0].id); // remove default New Tab Page
75 | //sendResponse({msg: 'OK'});
76 | //window.close();
77 | });
78 | break;
79 | case 'openLinksInCurrentWindow':
80 | msg.links.forEach(function (el) {
81 | chrome.tabs.create({'url': el.url, 'pinned': el.pinned});
82 | });
83 | break;
84 | case 'openWindowLinksInCurrentWindow':
85 | msg.links.forEach(function (el) {
86 | if (el.windowId === parseInt(msg.window_id, 10)) {
87 | chrome.tabs.create({'url': el.url, 'pinned': el.pinned});
88 | }
89 | });
90 | break;
91 | }
92 | });
93 |
94 | /*SESSION WATCHER*/
95 |
96 | // remove old sessions from chrome.storage.local (if sessions numbers >= config.session_numbers)
97 | function cleanupSessions() {
98 | chrome.storage.local.get(function (items) {
99 | var key = (function () {
100 | var item,
101 | id;
102 | for (item in items) {
103 | if (item.indexOf(session_config.prefix) === 0) {
104 | id = item;
105 | break;
106 | }
107 | }
108 | return id ? id.slice(session_config.prefix.length) : id;
109 | }()),
110 | items_keys = Object.keys(items);
111 |
112 | if (key && items_keys.length >= session_config.session_numbers) {
113 | items_keys.forEach(function (el) {
114 | var el_id = el.slice(session_config.prefix.length);
115 | key = el_id < key ? el_id : key;
116 | });
117 | chrome.storage.local.remove(session_config.prefix + key);
118 | }
119 | });
120 | }
121 |
122 | async function updateCurrentSession() {
123 | const session_storage = await chrome.storage.session.get('session_id');
124 | let session_id = session_storage.session_id;
125 | if (!session_id) {
126 | session_id = session_config.prefix + (new Date()).getTime();
127 | await chrome.storage.session.set({session_id});
128 |
129 | cleanupSessions();
130 | }
131 | chrome.tabs.query({}, function (tabs) {
132 | const session = {
133 | name: '',
134 | tabs: [],
135 | };
136 | const obj = {};
137 | tabs.forEach(function (tab) {
138 | const obj = {};
139 | if (!tab.url.startsWith('chrome-devtools://')) {
140 | obj.pinned = tab.pinned;
141 | obj.title = tab.title;
142 | obj.url = tab.url;
143 | obj.windowId = tab.windowId;
144 | session.tabs.push(obj);
145 | }
146 | });
147 |
148 | obj[session_id] = session;
149 | chrome.storage.local.set(obj);
150 | });
151 | }
152 |
153 | function onUpdated(tab_id, change_info) {
154 | if (change_info.url) {
155 | updateCurrentSession();
156 | }
157 | }
158 |
159 | chrome.tabs.onUpdated.addListener(onUpdated);
160 | chrome.tabs.onMoved.addListener(updateCurrentSession);
161 | chrome.tabs.onAttached.addListener(updateCurrentSession);
162 | chrome.tabs.onRemoved.addListener(updateCurrentSession);
163 | chrome.tabs.onReplaced.addListener(updateCurrentSession);
164 |
165 | storage.area.get({session_watcher: storage.default_options.session_watcher}, async function (from_storage) {
166 | if (!from_storage.session_watcher) {
167 | chrome.tabs.onUpdated.hasListener(onUpdated) && chrome.tabs.onUpdated.removeListener(onUpdated);
168 | chrome.tabs.onMoved.hasListener(updateCurrentSession) && chrome.tabs.onMoved.removeListener(updateCurrentSession);
169 | chrome.tabs.onAttached.hasListener(updateCurrentSession) && chrome.tabs.onAttached.removeListener(updateCurrentSession);
170 | chrome.tabs.onRemoved.hasListener(updateCurrentSession) && chrome.tabs.onRemoved.removeListener(updateCurrentSession);
171 | chrome.tabs.onReplaced.hasListener(updateCurrentSession) && chrome.tabs.onReplaced.removeListener(updateCurrentSession);
172 | } else {
173 | const session_storage = await chrome.storage.session.get('session_id');
174 | if (!session_storage.session_id) {
175 | updateCurrentSession();
176 | }
177 | }
178 | });
179 |
--------------------------------------------------------------------------------
/js/backup.js:
--------------------------------------------------------------------------------
1 | var backup = {
2 | storage_area: chrome.storage.sync,
3 |
4 | getStoredKeys: function (callback) {
5 | var stored_keys = [];
6 |
7 | this.storage_area.get(function (storage_items) {
8 | var storage_key_name;
9 |
10 | // collect saved groups and extension options keys
11 | for (storage_key_name in storage_items) {
12 | if (storage_key_name.indexOf('tg_') === 0 || storage.default_options.hasOwnProperty(storage_key_name)) {
13 | stored_keys.push(storage_key_name);
14 | }
15 | }
16 | callback(stored_keys);
17 | });
18 | },
19 |
20 | getBackupData: function (callback) {
21 | var self = this;
22 |
23 | this.getStoredKeys(function (stored_keys) {
24 | self.storage_area.get(stored_keys, function (storage_items) {
25 | callback(JSON.stringify(storage_items));
26 | });
27 | });
28 | },
29 |
30 | cleanupBeforeReplace: function (callback) {
31 | var self = this;
32 |
33 | this.getStoredKeys(function (stored_keys) {
34 | self.storage_area.remove(stored_keys, function () {
35 | callback(chrome.runtime.lastError);
36 | });
37 | });
38 | },
39 |
40 | replaceFromBackup: function (backup_data, callback) {
41 | var backup,
42 | storage_area = this.storage_area;
43 |
44 | try {
45 | backup = JSON.parse(backup_data);
46 | } catch(e) {
47 | callback('error');
48 | }
49 |
50 | if (backup && typeof backup === 'object') {
51 | this.cleanupBeforeReplace(function (cleanup_error) {
52 | if (!cleanup_error) {
53 | storage_area.set(backup, function () {
54 | callback(chrome.runtime.lastError);
55 | });
56 | } else {
57 | callback(cleanup_error);
58 | }
59 | });
60 | }
61 | }
62 | };
--------------------------------------------------------------------------------
/js/helpers/localizer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a part of Chrome Extensions Box
3 | * Read more on GitHub - https://github.com/onikienko/chrome-extensions-box
4 | *
5 | * Parse html and replace all {{property name from message.json}} from text nodes, title, alt, value and placeholder attrs
6 | * with chrome.i18n.getMessage http://developer.chrome.com/extensions/i18n.html
7 | */
8 | window.addEventListener('load', function () {
9 | function translator(html) {
10 | var i,
11 | length,
12 | attrs_to_check = ['title', 'alt', 'placeholder', 'value'];
13 |
14 | function replacer(text) {
15 | return text.replace(/\{\{([\s\S]*?)\}\}/gm, function (str, g1) {
16 | return chrome.i18n.getMessage(g1.trim()) || str;
17 | });
18 | }
19 |
20 | if (html.attributes) {
21 | attrs_to_check.forEach(function (el) {
22 | if (html.attributes[el]) {
23 | html.attributes[el].value = replacer(html.attributes[el].value);
24 | }
25 | });
26 | }
27 |
28 | if (html.nodeType === 3) { //text node
29 | html.data = replacer(html.data);
30 | } else {
31 | for (i = 0, length = html.childNodes.length; i < length; i++) {
32 | translator(html.childNodes[i]);
33 | }
34 | }
35 | }
36 |
37 | translator(document.body);
38 | translator(document.head);
39 | });
--------------------------------------------------------------------------------
/js/helpers/quick_options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a part of Chrome Extensions Box
3 | * Read more on GitHub - https://github.com/onikienko/chrome-extensions-box
4 | */
5 |
6 | /*
7 | Modified Chrome Extensions Box
8 | Make showMessage global
9 | */
10 | var quick_options = {
11 | showMessage: function (msg) {
12 | var el = document.getElementById(msg === 'error' ? 'error' : 'success');
13 | el.style.display = 'inline';
14 | setTimeout(function () {
15 | el.style.display = 'none';
16 | }, 2501);
17 | }
18 | };
19 | /*
20 | end of modifications
21 | */
22 |
23 | window.addEventListener('load', function () {
24 | /*This event will dispatch as soon as options page will be ready*/
25 | var event = new CustomEvent('optionsPageReady');
26 |
27 | function saveToStorage(val) {
28 | storage.area.set(val, function () {
29 | quick_options.showMessage(chrome.runtime.lastError ? quick_options.showMessage('error') : quick_options.showMessage('success'));
30 | // dispatch custom event
31 | document.dispatchEvent(new CustomEvent('optionSaved', {
32 | detail: {
33 | success: chrome.runtime.lastError ? false : true,
34 | val: val
35 | }
36 | }));
37 | });
38 | }
39 |
40 | storage.area.get(storage.default_options, function (items) {
41 | var inputs = document.querySelectorAll('input'),
42 | selects = document.querySelectorAll('select'),
43 | textareas = document.querySelectorAll('textarea');
44 |
45 | [].forEach.call(inputs, function (el) {
46 | var storage_name = el.getAttribute('data-storage'),
47 | text_el;
48 | if (storage_name && items.hasOwnProperty(storage_name)) {
49 | switch (el.type) {
50 | case 'checkbox':
51 | if (parseInt(items[storage_name], 10) === 1) {
52 | el.checked = true;
53 | }
54 | el.addEventListener('change', function () {
55 | var val = {};
56 | items[storage_name] = el.checked ? 1 : 0;
57 | val[storage_name] = items[storage_name];
58 | saveToStorage(val);
59 | }, false);
60 | break;
61 |
62 | case 'radio':
63 | if (el.value === items[storage_name].toString()) {
64 | el.checked = 'checked';
65 | }
66 | el.addEventListener('change', function () {
67 | var val = {};
68 | if (el.checked) {
69 | items[storage_name] = el.value;
70 | val[storage_name] = items[storage_name];
71 | saveToStorage(val);
72 | }
73 | }, false);
74 | break;
75 |
76 | case 'range':
77 | case 'color':
78 | case 'date':
79 | case 'time':
80 | case 'month':
81 | case 'week':
82 | el.value = items[storage_name];
83 | el.addEventListener('change', function () {
84 | var val = {};
85 | items[storage_name] = el.value;
86 | val[storage_name] = items[storage_name];
87 | saveToStorage(val);
88 | }, false);
89 | break;
90 |
91 | case 'text':
92 | case 'password':
93 | case 'email':
94 | case 'tel':
95 | case 'number':
96 | el.value = items[storage_name];
97 | break;
98 |
99 | case 'submit':
100 | case 'button':
101 | [].forEach.call(document.querySelectorAll('[data-storage="' + storage_name + '"]'), function (elem) {
102 | if (elem.type !== 'submit' && elem.type !== 'button') {
103 | text_el = elem;
104 | }
105 | });
106 | if (text_el) {
107 | el.addEventListener('click', function () {
108 | var val = {};
109 | items[storage_name] = text_el.value;
110 | val[storage_name] = items[storage_name];
111 | saveToStorage(val);
112 | }, false);
113 | }
114 | break;
115 |
116 | }
117 | }
118 | });
119 |
120 | [].forEach.call(textareas, function (el) {
121 | var storage_name = el.getAttribute('data-storage');
122 | if (storage_name && items.hasOwnProperty(storage_name)) {
123 | el.value = items[storage_name];
124 | }
125 | });
126 |
127 | [].forEach.call(selects, function (el) {
128 | var storage_name = el.getAttribute('data-storage'),
129 | options,
130 | i;
131 | if (storage_name && items.hasOwnProperty(storage_name)) {
132 | if ((el.multiple && Array.isArray(items[storage_name])) || !el.multiple) {
133 | options = el.options;
134 | for (i = options.length; i--;) {
135 | if (el.multiple) {
136 | if (items[storage_name].indexOf(options[i].value) !== -1) {
137 | options[i].selected = 'selected';
138 | }
139 | } else {
140 | if (options[i].value === items[storage_name]) {
141 | options[i].selected = 'selected';
142 | break;
143 | }
144 | }
145 | }
146 | el.addEventListener('change', function () {
147 | var val = {},
148 | array_of_selected = [],
149 | i;
150 | if (el.multiple) {
151 | for (i = options.length; i--;) {
152 | if (options[i].selected) {
153 | array_of_selected.push(options[i].value);
154 | }
155 | }
156 | items[storage_name] = array_of_selected;
157 | } else {
158 | items[storage_name] = options[options.selectedIndex].value;
159 | }
160 | val[storage_name] = items[storage_name];
161 | saveToStorage(val);
162 | }, false);
163 | }
164 | }
165 | });
166 |
167 | /* Options page is ready. Dispatch event */
168 | document.dispatchEvent(event);
169 |
170 | });
171 | }, false);
172 |
--------------------------------------------------------------------------------
/js/options.js:
--------------------------------------------------------------------------------
1 | var main_tabs = new Tabs('#main_tabs');
2 |
3 | document.addEventListener('optionSaved', function (event) {
4 | // reload extension after toggle session watcher option
5 | if (event.detail.val.hasOwnProperty('session_watcher') && event.detail.success) {
6 | chrome.runtime.reload();
7 | }
8 | }, false);
9 |
10 | document.addEventListener('optionsPageReady', function () {
11 | var restore_btn = document.getElementById('restore_btn');
12 |
13 | backup.getBackupData(function (data) {
14 | document.getElementById('backup_text').value = data;
15 | });
16 |
17 | restore_btn.onclick = function () {
18 | backup.replaceFromBackup(document.getElementById('restore_text').value, function (error) {
19 | if (error) {
20 | quick_options.showMessage('error');
21 | } else {
22 | chrome.runtime.reload();
23 | }
24 | })
25 | }
26 | }, false);
--------------------------------------------------------------------------------
/js/popup.js:
--------------------------------------------------------------------------------
1 | function faviconURL(u) {
2 | const url = new URL(chrome.runtime.getURL('/_favicon/'));
3 | url.searchParams.set('pageUrl', u);
4 | url.searchParams.set('size', '32');
5 | return url.toString();
6 | }
7 |
8 | chrome.storage.session.get(function (session_storage_items) {
9 | chrome.storage.local.get(function (session_items) {
10 | chrome.storage.sync.get(function (storage_items) {
11 | /**
12 | * Popup window for edit operation
13 | * @param popup_id
14 | * @param callback {function} fired on submit.
15 | */
16 | function Popup(popup_id, callback) {
17 | this.popup_container = document.querySelector('#' + popup_id);
18 | this.callback = callback;
19 | this.go();
20 | }
21 |
22 | Popup.prototype = {
23 | go: function () {
24 | var self = this,
25 | data_container = self.popup_container.querySelectorAll('.data'),
26 | submit_btn = this.popup_container.querySelector('.submit_btn');
27 |
28 | function submitData() {
29 | var data = {};
30 | [].forEach.call(data_container, function (el) {
31 | data[el.id] = el.value;
32 | });
33 | self.popup_container.style.display = 'none';
34 | self.callback(data);
35 | }
36 |
37 | this.popup_container.style.display = 'block';
38 | if (data_container.length) {
39 | data_container[0].focus();
40 | data_container[0].select();
41 | // submit on 'enter' for last text input
42 | if (data_container[data_container.length - 1].getAttribute('type') === 'text') {
43 | data_container[data_container.length - 1].onkeyup = function (e) {
44 | if (e.keyCode === 13) {
45 | submitData();
46 | }
47 | };
48 | }
49 | } else {
50 | submit_btn.focus();
51 | this.popup_container.onkeyup = function (e) {
52 | if (e.keyCode === 13) {
53 | submitData();
54 | }
55 | };
56 | }
57 |
58 | submit_btn.onclick = function (e) {
59 | submitData();
60 | };
61 |
62 | this.popup_container.querySelector('.cancel').onclick = function (e) {
63 | self.popup_container.style.display = 'none';
64 | };
65 | },
66 | };
67 |
68 |
69 | function GroupModel(area) {
70 | this.storageArea = (function () {
71 | return area === 'session' ? chrome.storage.local : chrome.storage.sync;
72 | }());
73 | // all saved groups as object
74 | this.data_local_copy = (function () {
75 | var item,
76 | groups_data = (function () {
77 | return area === 'session' ? session_items : storage_items;
78 | }()),
79 | obj = {};
80 |
81 | for (item in groups_data) {
82 | if (item.indexOf('tg_') === 0) {
83 | obj[item] = groups_data[item];
84 | }
85 | }
86 | return obj;
87 | }());
88 | }
89 |
90 | GroupModel.prototype = {
91 | getGroups: function () {
92 | return this.data_local_copy;
93 | },
94 |
95 | getStorageNameByName: function (name) {
96 | var storage_name;
97 |
98 | for (storage_name in this.data_local_copy) {
99 | if (this.data_local_copy[storage_name].name === name) {
100 | return storage_name;
101 | }
102 | }
103 | return false;
104 | },
105 |
106 | nextIndex: function () {
107 | var last_index = 0,
108 | group;
109 |
110 | for (group in this.data_local_copy) {
111 | if (this.data_local_copy[group].index > last_index) {
112 | last_index = this.data_local_copy[group].index;
113 | }
114 | }
115 | return last_index + 1;
116 | },
117 |
118 | del: function (storage_name, callback) {
119 | var self = this;
120 |
121 | this.storageArea.remove(storage_name, function () {
122 | if (!chrome.runtime.lastError) {
123 | delete self.data_local_copy[storage_name];
124 | callback({err: 0});
125 | } else {
126 | callback({err: 1, msg: chrome.runtime.lastError.message});
127 | }
128 | });
129 | },
130 |
131 | add: function (name, group, callback) {
132 | var now = new Date(),
133 | new_group = {},
134 | storage_name = 'tg_' + now.getTime(),
135 | self = this;
136 |
137 | new_group[storage_name] = {
138 | name: name === undefined || name.length === 0 ? '' : name,
139 | index: this.nextIndex(),
140 | color: '', // TODO personal color for each group
141 | tabs: group,
142 | };
143 | this.storageArea.set(new_group, function () {
144 | if (!chrome.runtime.lastError) {
145 | self.data_local_copy[storage_name] = new_group[storage_name];
146 | callback({err: 0, storage_name: storage_name, new_group: new_group[storage_name]});
147 | } else {
148 | callback({err: 1, msg: chrome.runtime.lastError.message});
149 | }
150 | });
151 | },
152 |
153 | upd: function (storage_name, value, callback) {
154 | var obj = {},
155 | self = this;
156 |
157 | obj[storage_name] = value;
158 | this.storageArea.set(obj, function () {
159 | if (!chrome.runtime.lastError) {
160 | self.data_local_copy[storage_name] = value;
161 | callback({err: 0, storage_name: storage_name, new_group: obj[storage_name]});
162 | } else {
163 | callback({err: 1, msg: chrome.runtime.lastError.message});
164 | }
165 | });
166 | },
167 |
168 | move: function (storage_name, sibling_storage_name, callback) {
169 | var self = this,
170 | obj = {},
171 | sibling_obj = {},
172 | i = this.data_local_copy[storage_name].index;
173 |
174 | obj[storage_name] = this.data_local_copy[storage_name];
175 | obj[storage_name].index = this.data_local_copy[sibling_storage_name].index;
176 | sibling_obj[sibling_storage_name] = this.data_local_copy[sibling_storage_name];
177 | sibling_obj[sibling_storage_name].index = i;
178 | this.storageArea.set(obj, function () {
179 | if (!chrome.runtime.lastError) {
180 | self.storageArea.set(sibling_obj, function () {
181 | if (!chrome.runtime.lastError) {
182 | self.data_local_copy[storage_name] = obj[storage_name];
183 | self.data_local_copy[sibling_storage_name] = sibling_obj[sibling_storage_name];
184 | callback({err: 0});
185 | } else {
186 | callback({err: 1, msg: chrome.runtime.lastError.message});
187 | }
188 | });
189 | } else {
190 | callback({err: 1, msg: chrome.runtime.lastError.message});
191 | }
192 | });
193 | },
194 | };
195 |
196 |
197 | function LinkModel(area) {
198 | this.storage = (function () {
199 | return area === 'session' ? chrome.storage.local : chrome.storage.sync;
200 | }());
201 | this.model = area === 'session' ? sessionModel : groupModel;
202 | }
203 |
204 | LinkModel.prototype = {
205 | getLocalLinkIndexById: function (storage_name, link_id) {
206 | var i,
207 | tabs = (this.model.getGroups())[storage_name].tabs,
208 | max = tabs.length;
209 |
210 | for (i = 0; i < max; i++) {
211 | if (tabs[i].id === parseInt(link_id, 10)) {
212 | return i;
213 | }
214 | }
215 | },
216 |
217 | groupLinksByWindowId: function (storage_name) {
218 | var tabs = (this.model.getGroups())[storage_name].tabs,
219 | windows = [], // arr with unique windowId
220 | grouped_by_windows = []; // links grouped by windowId [[{tab1}, {tab2}],[{tab3}]]...
221 |
222 | tabs.forEach(function (el) {
223 | var window_index = windows.indexOf(el.windowId);
224 | if (window_index === -1) {
225 | windows.push(el.windowId);
226 | grouped_by_windows.push([]);
227 | grouped_by_windows[windows.length - 1].push(el);
228 | } else {
229 | grouped_by_windows[window_index].push(el);
230 | }
231 | });
232 | return {grouped_by_windowId: grouped_by_windows, windowId_arr: windows};
233 | },
234 |
235 | nextIndex: function (storage_name) {
236 | var tabs = (this.model.getGroups())[storage_name].tabs,
237 | last_index = 0;
238 |
239 | tabs.forEach(function (el) {
240 | if (el.id > last_index) {
241 | last_index = el.id;
242 | }
243 | });
244 | return last_index + 1;
245 | },
246 |
247 | del: function (storage_name, link_id, callback) {
248 | var group = (this.model.getGroups())[storage_name];
249 | group.tabs.splice(this.getLocalLinkIndexById(storage_name, link_id), 1);
250 |
251 | this.model.upd(storage_name, group, function (answ) {
252 | callback(answ);
253 | });
254 | },
255 |
256 | add: function (storage_name, link_data, callback) {
257 | var group = (this.model.getGroups())[storage_name];
258 |
259 | group.tabs.push(link_data);
260 | this.model.upd(storage_name, group, function (answ) {
261 | callback(answ);
262 | });
263 | },
264 |
265 | upd: function (storage_name, link_index, link_data, callback) {
266 | var group = (this.model.getGroups())[storage_name];
267 |
268 | group.tabs[link_index] = link_data;
269 | this.model.upd(storage_name, group, function (answ) {
270 | callback(answ);
271 | });
272 | },
273 | };
274 |
275 | var groupModel = new GroupModel('saved'),
276 | linkModel = new LinkModel('saved'),
277 | sessionModel = new GroupModel('session'),
278 | sessionLinkModel = new LinkModel('session'),
279 | TAB_TITLE_LENGTH = 48,
280 | ui_msg = {
281 | title_del_group: chrome.i18n.getMessage('p_delGroup_btn_title'),
282 | title_edit_group_name: chrome.i18n.getMessage('p_editGroupName_btn_title'),
283 | title_add_link_to_group: chrome.i18n.getMessage('p_addLinkToGroup_btn_title'),
284 | title_group_in_new_window: chrome.i18n.getMessage('p_groupInNewWindow_btn_title'),
285 | title_edit_link: chrome.i18n.getMessage('p_editLink_btn_title'),
286 | title_del_link: chrome.i18n.getMessage('p_delLinkFromGroup_btn_title'),
287 | title_up_link: chrome.i18n.getMessage('p_upGroup_btn_title'),
288 | title_down_link: chrome.i18n.getMessage('p_downGroup_btn_title'),
289 |
290 | windows_numbers: chrome.i18n.getMessage('p_windowsNumbers_text'),
291 | tabs_numbers: chrome.i18n.getMessage('p_tabsNumbers_text'),
292 |
293 | window_text: chrome.i18n.getMessage('p_window_text'),
294 |
295 | quota_bytes_item: chrome.i18n.getMessage('p_quotaBites_error'),
296 | quota_default_item: chrome.i18n.getMessage('p_syncStorageDefault_error'),
297 | quota_bytes_per_item: chrome.i18n.getMessage('p_quotaBitesPerItem_error'),
298 |
299 | current_session: chrome.i18n.getMessage('p_currentSession'),
300 | },
301 |
302 | tabsGrabber = {
303 | tabsFilter: function () {
304 | var obj = {};
305 |
306 | if (storage_items.cur_win) {
307 | obj.currentWindow = true;
308 | }
309 | return obj;
310 | },
311 |
312 | collectTabs: function (callback) {
313 | chrome.tabs.query(this.tabsFilter(), function (tabs) {
314 | var links = [];
315 |
316 | tabs.forEach(function (tab, index) {
317 | var link = {};
318 |
319 | if (!tab.pinned || storage_items.pinned === 1) {
320 | link.url = tab.url;
321 | link.title = tab.title.length > TAB_TITLE_LENGTH ? tab.title.slice(0, TAB_TITLE_LENGTH + 1) : tab.title;
322 | link.id = index;
323 | if (tab.pinned) {
324 | link.pinned = tab.pinned;
325 | }
326 | links.push(link);
327 | }
328 | });
329 | callback(links);
330 | });
331 | },
332 | },
333 |
334 | openLinksInNewWindow = function (links) {
335 | chrome.runtime.sendMessage({
336 | msg: 'openLinksInNewWindow',
337 | links: links,
338 | });
339 | window.close();
340 | },
341 |
342 | openLinksInCurrentWindow = function (links) {
343 | chrome.runtime.sendMessage({
344 | msg: 'openLinksInCurrentWindow',
345 | links: links,
346 | });
347 | },
348 |
349 | openWindowLinksInCurrentWindow = function (links, window_id) {
350 | chrome.runtime.sendMessage({
351 | msg: 'openWindowLinksInCurrentWindow',
352 | links: links,
353 | window_id: window_id,
354 | });
355 | },
356 |
357 | savedUI = {
358 | groupHtmlElement: function (storage_name, group) {
359 | var title = group.name;
360 |
361 | if (group.name === '') {
362 | title = utils.formatDate(new Date(parseInt(storage_name.slice('tg_'.length), 10)), storage_items.date_format);
363 | }
364 | return '
' +
365 | ' ►' +
366 | '' + title + '' +
367 | '❐ ' +
368 | '' +
369 | '✚' +
370 | '▲' +
371 | '▼' +
372 | '☰' +
373 | '✖' +
374 | '' +
375 | '
';
376 | },
377 |
378 | linkHtmlElement: function (storage_name, link) {
379 | var a_text = link.title,
380 | a_title = '',
381 | pin_icon = link.pinned ? '
' : '',
382 | favicon_src = faviconURL(link.url);
383 |
384 | if (link.title.length >= TAB_TITLE_LENGTH) {
385 | a_text = link.title.slice(0, TAB_TITLE_LENGTH) + '...';
386 | a_title = 'title="' + link.title + '"';
387 | }
388 | return '';
396 | },
397 |
398 | openGroup: function (storage_name, mouse_button) {
399 | // mouse_button = 0 - cur window, 1 - new
400 | var tabs = (groupModel.getGroups())[storage_name].tabs;
401 |
402 | if (mouse_button === 1) {
403 | openLinksInNewWindow(tabs);
404 | } else {
405 | openLinksInCurrentWindow(tabs);
406 | //tabs.forEach(function (el) {
407 | // chrome.tabs.create({'url': el.url, 'pinned': el.pinned});
408 | //});
409 | }
410 | },
411 |
412 | showGroups: function () {
413 | var html = '',
414 | self = this,
415 | groups = groupModel.getGroups(),
416 | tag = document.getElementById('saved'),
417 | sorted_group_list = Object.keys(groups).sort(function (a, b) {
418 | return groups[b].index - groups[a].index;
419 | });
420 | sorted_group_list.forEach(function (el) {
421 | html += self.groupHtmlElement(el, groups[el]);
422 | });
423 | tag.innerHTML = html;
424 | this.showSyncStorageUsage();
425 | },
426 |
427 | addGroup: function () {
428 | var input_field = document.getElementById('new_group_name'),
429 | name = input_field.value,
430 | popup,
431 | storage_name = groupModel.getStorageNameByName(name),
432 | stored_group,
433 | self = this;
434 |
435 | tabsGrabber.collectTabs(function (tabs) {
436 | // check if group name already exist
437 | if (storage_name !== false && name !== '') {
438 | // group with the same name already exist
439 | // show Popup with dialog
440 | popup = new Popup('overwrite_group', function (data) {
441 | input_field.value = '';
442 | stored_group = groupModel.getGroups()[storage_name];
443 | stored_group.tabs = tabs;
444 | groupModel.upd(storage_name, stored_group, function (answ) {
445 | if (answ.err === 0) {
446 | self.showSyncStorageUsage();
447 | } else {
448 | self.showErrorMsg(answ.msg);
449 | }
450 | });
451 | });
452 | } else {
453 | // new group name
454 | input_field.value = '';
455 | groupModel.add(name, tabs, function (answ) {
456 | var el,
457 | groups_el;
458 |
459 | if (answ.err === 0) {
460 | self.showSyncStorageUsage();
461 | el = document.createElement('div');
462 | groups_el = document.getElementById('saved');
463 | el.innerHTML = self.groupHtmlElement(answ.storage_name, answ.new_group);
464 | groups_el.insertBefore(el, groups_el.getElementsByTagName('div')[0]);
465 | groups_el.innerHTML = groups_el.innerHTML.replace(/()*|(<\/div>)*/g, '');
466 | } else {
467 | // storage Error
468 | self.showErrorMsg(answ.msg);
469 | }
470 | });
471 | }
472 | });
473 | },
474 |
475 | editGroupName: function (storage_name, el) {
476 | var group = (groupModel.getGroups())[storage_name],
477 | new_name,
478 | popup,
479 | self = this;
480 |
481 | document.getElementById('popup-new_group_name').value = group.name;
482 | popup = new Popup('edit_group_name', function (data) {
483 | new_name = data['popup-new_group_name'];
484 | if (new_name !== undefined || new_name !== '') {
485 | group.name = new_name;
486 | groupModel.upd(storage_name, group, function (answ) {
487 | if (answ.err === 0) {
488 | el.getElementsByClassName('open_group')[0].innerText = new_name;
489 | self.showSyncStorageUsage();
490 | } else {
491 | self.showErrorMsg(answ.msg);
492 | }
493 | });
494 | }
495 |
496 | });
497 | },
498 |
499 | delGroup: function (storage_name, el) {
500 | var self = this;
501 |
502 | groupModel.del(storage_name, function (answ) {
503 | if (answ.err === 0) {
504 | el.remove();
505 | self.showSyncStorageUsage();
506 | } else {
507 | self.showErrorMsg(answ.msg);
508 | }
509 | });
510 | },
511 |
512 | moveGroup: function (storage_name, sibling_storage_name) {
513 | var self = this;
514 |
515 | groupModel.move(storage_name, sibling_storage_name, function (answ) {
516 | if (answ.err === 0) {
517 | self.showGroups();
518 | } else {
519 | self.showErrorMsg(answ.msg);
520 | }
521 | });
522 | },
523 |
524 | showGroupLinks: function (storage_name, el) {
525 | var link_list = document.createElement('div'),
526 | spoiler = el.getElementsByClassName('spoiler')[0],
527 | html = '',
528 | self = this;
529 |
530 | (groupModel.getGroups())[storage_name].tabs.forEach(function (el) {
531 | html += self.linkHtmlElement(storage_name, el);
532 | });
533 | link_list.classList.add('links');
534 | link_list.innerHTML = html;
535 | el.insertBefore(link_list, null);
536 | spoiler.setAttribute('name', 'opened');
537 | spoiler.innerHTML = ' ▼';
538 | },
539 |
540 | hideGroupLinks: function (el) {
541 | var spoiler = el.getElementsByClassName('spoiler')[0];
542 |
543 | spoiler.setAttribute('name', 'closed');
544 | spoiler.innerHTML = ' ►';
545 | el.getElementsByClassName('links')[0].remove();
546 | },
547 |
548 | delLink: function (storage_name, link_id, el) {
549 | linkModel.del(storage_name, link_id, function (answ) {
550 | if (answ.err === 0) {
551 | el.remove();
552 | }
553 | });
554 | },
555 |
556 | addLink: function (storage_name, el) {
557 | var self = this,
558 | popup = new Popup('add_link', function (data) {
559 | var obj = {
560 | id: linkModel.nextIndex(storage_name),
561 | title: data['popup-add_link_name'],
562 | url: utils.correctUrl(data['popup-add_link_link']),
563 | };
564 | linkModel.add(storage_name, obj, function (answ) {
565 | if (answ.err === 0) {
566 | var links_div = el.getElementsByClassName('links');
567 | if (links_div.length) {
568 | self.hideGroupLinks(el);
569 | }
570 | self.showGroupLinks(storage_name, el);
571 | self.showSyncStorageUsage();
572 | } else {
573 | self.showErrorMsg(answ.msg);
574 | }
575 | });
576 | });
577 | },
578 |
579 | editLink: function (storage_name, link_id, el) {
580 | var group_node = el.parentNode.parentNode,
581 | tabs = (groupModel.getGroups())[storage_name].tabs,
582 | link_index = linkModel.getLocalLinkIndexById(storage_name, link_id),
583 | popup,
584 | self = this;
585 |
586 | document.getElementById('popup-edit_link_name').value = tabs[link_index].title;
587 | document.getElementById('popup-edit_link_url').value = tabs[link_index].url;
588 | popup = new Popup('edit_link', function (data) {
589 | var link_data = tabs[link_index];
590 | link_data.title = data['popup-edit_link_name'];
591 | link_data.url = utils.correctUrl(data['popup-edit_link_url']);
592 | linkModel.upd(storage_name, link_index, link_data, function (answ) {
593 | if (answ.err === 0) {
594 | self.hideGroupLinks(group_node);
595 | self.showGroupLinks(storage_name, group_node);
596 | self.showSyncStorageUsage();
597 | } else {
598 | self.showErrorMsg(answ.msg);
599 | }
600 | });
601 | });
602 | },
603 |
604 | showErrorMsg: function (msg) {
605 | var el = document.getElementById('error_msg'),
606 | text = ui_msg.quota_default_item;
607 |
608 | el.style.display = 'block';
609 | switch (msg) {
610 | case 'QUOTA_BYTES_PER_ITEM quota exceeded':
611 | text = ui_msg.quota_bytes_per_item;
612 | break;
613 | case 'QUOTA_BYTES quota exceeded':
614 | text = ui_msg.quota_bytes_item;
615 | break;
616 | }
617 | el.innerText = text;
618 | setTimeout(function () {
619 | el.style.display = 'none';
620 | }, 4581);
621 | },
622 |
623 | showSyncStorageUsage: function () {
624 | chrome.storage.sync.getBytesInUse(null, function (bytesInUse) {
625 | var percent_in_use = (bytesInUse * 100 / chrome.storage.sync.QUOTA_BYTES).toFixed(2),
626 | el = document.querySelector('ul.tabs_nav>li a'),
627 | text = el.innerText;
628 |
629 | el.innerText = text.slice(0, text.indexOf('|')) + '| ' + percent_in_use + '%';
630 | });
631 | },
632 |
633 | setHandlers: function () {
634 | var self = this,
635 | saved = document.getElementById('saved');
636 |
637 | document.getElementById('new_group').addEventListener('click', function (e) {
638 | e.preventDefault();
639 | self.addGroup();
640 | }, false);
641 |
642 | document.getElementById('new_group_name').addEventListener('keyup', function (e) {
643 | if (e.keyCode === 13) {
644 | self.addGroup();
645 | }
646 | }, false);
647 |
648 | saved.addEventListener('click', function (e) {
649 | var el = e.target,
650 | group_node,
651 | link_node,
652 | btn,
653 | sibling,
654 | link;
655 |
656 | function linkInfo(link_node_id) {
657 | return {
658 | storage_name: link_node_id.slice(0, link_node_id.lastIndexOf('_')),
659 | id: link_node_id.slice(link_node_id.lastIndexOf('_') + 1),
660 | };
661 | }
662 |
663 | e.stopPropagation();
664 | switch (el.className) {
665 | case 'open_group':
666 | // e.button - 0 - left mouse click (cur win), 1 - mouse wheel click (new win)
667 | // mouse wheel works only with no scroll
668 | btn = e.button;
669 | if (btn === 0 && e.ctrlKey === true) {
670 | self.openGroup(el.parentNode.id, 1);
671 | } else {
672 | self.openGroup(el.parentNode.id, e.button);
673 | }
674 | break;
675 | case 'open_in_new_window':
676 | // e.button - 0 - left mouse click (cur win), 1 - mouse wheel click (new win)
677 | self.openGroup(el.parentNode.id, 1);
678 | break;
679 | case 'up':
680 | group_node = el.parentNode.parentNode;
681 | sibling = group_node.previousSibling;
682 | if (sibling && sibling.className === 'group') {
683 | self.moveGroup(group_node.id, sibling.id);
684 | }
685 | break;
686 | case 'down':
687 | group_node = el.parentNode.parentNode;
688 | sibling = group_node.nextSibling;
689 | if (sibling && sibling.className === 'group') {
690 | self.moveGroup(group_node.id, sibling.id);
691 | }
692 | break;
693 | case 'del_group':
694 | group_node = el.parentNode.parentNode;
695 | self.delGroup(group_node.id, group_node);
696 | break;
697 | case 'edit_group':
698 | group_node = el.parentNode.parentNode;
699 | self.editGroupName(group_node.id, group_node);
700 | break;
701 | case 'add_link':
702 | group_node = el.parentNode.parentNode;
703 | self.addLink(group_node.id, group_node);
704 | break;
705 | case 'spoiler':
706 | group_node = el.parentNode;
707 | if (el.getAttribute('name') === 'closed') {
708 | self.showGroupLinks(group_node.id, group_node);
709 | } else {
710 | self.hideGroupLinks(group_node);
711 | }
712 | break;
713 | case 'del_link':
714 | link_node = el.parentNode.parentNode;
715 | link = linkInfo(link_node.id);
716 | self.delLink(link.storage_name, link.id, link_node);
717 | break;
718 | case 'edit_link':
719 | link_node = el.parentNode.parentNode;
720 | link = linkInfo(link_node.id);
721 | self.editLink(link.storage_name, link.id, link_node);
722 | break;
723 | }
724 |
725 | }, false);
726 |
727 | },
728 |
729 | go: function () {
730 | this.showGroups();
731 | this.setHandlers();
732 | },
733 | },
734 |
735 |
736 | sessionsUI = {
737 | groupHtmlElement: function (storage_name, group) {
738 | var title,
739 | date = utils.formatDate(new Date(parseInt(storage_name.slice('tg_'.length), 10)), storage_items.date_format),
740 | group_info = (function () {
741 | return {
742 | numbers_of_windows: sessionLinkModel.groupLinksByWindowId(storage_name).windowId_arr.length,
743 | numbers_of_tabs: (sessionModel.getGroups())[storage_name].tabs.length,
744 | };
745 | }());
746 |
747 | if (group.name === '') {
748 | title = date;
749 | }
750 | if (storage_name === session_storage_items.session_id) {
751 | title = ui_msg.current_session + ' (' + date + ')';
752 | }
753 | return '
' +
754 | ' ►' +
755 | '' + title + '' +
756 | '❐ ' +
757 | '' + ui_msg.windows_numbers + ' ' + group_info.numbers_of_windows + '' +
758 | '' + ui_msg.tabs_numbers + ' ' + group_info.numbers_of_tabs + '' +
759 | '' +
760 | '✖' +
761 | '' +
762 | '
';
763 | },
764 |
765 | linkHtmlElement: function (storage_name, link) {
766 | var a_text = link.title,
767 | a_title = '',
768 | text_length = 44,
769 | pin_icon = link.pinned === true ? '

' : '',
770 | favicon_src = faviconURL(link.url);
771 |
772 | if (link.title.length > text_length) {
773 | a_text = link.title.slice(0, text_length) + '...';
774 | a_title = 'title="' + link.title + '"';
775 | }
776 | return '
';
783 | },
784 |
785 | openGroup: function (storage_name, mouse_button) {
786 | // mouse_button = 0 - cur window, 1 - new
787 | var grouped,
788 | links = [];
789 |
790 | if (mouse_button === 1) {
791 | grouped = sessionLinkModel.groupLinksByWindowId(storage_name);
792 | grouped.windowId_arr.forEach(function (el, index) {
793 | links = [];
794 | grouped.grouped_by_windowId[index].forEach(function (link) {
795 | links.push(link);
796 | });
797 | openLinksInNewWindow(links);
798 | });
799 | } else {
800 | openLinksInCurrentWindow((sessionModel.getGroups())[storage_name].tabs);
801 | //(sessionModel.getGroups())[storage_name].tabs.forEach(function (el) {
802 | // chrome.tabs.create({'url': el.url, 'pinned': el.pinned});
803 | //});
804 | }
805 | },
806 |
807 | openWindow: function (storage_name, window_id, mouse_button) {
808 | // mouse_button = 0 - cur window, 1 - new
809 | var tabs = (sessionModel.getGroups())[storage_name].tabs,
810 | links = [];
811 |
812 | if (mouse_button === 1) {
813 | tabs.forEach(function (el) {
814 | if (el.windowId === parseInt(window_id, 10)) {
815 | links.push(el);
816 | }
817 | });
818 | openLinksInNewWindow(links);
819 | } else {
820 | openWindowLinksInCurrentWindow(tabs, window_id);
821 | //tabs.forEach(function (el) {
822 | // if (el.windowId === parseInt(window_id, 10)) {
823 | // chrome.tabs.create({'url': el.url, 'pinned': el.pinned});
824 | // }
825 | //});
826 | }
827 | },
828 |
829 | showGroups: function () {
830 | var html = '
',
831 | groups = sessionModel.getGroups(),
832 | self = this,
833 | tag = document.getElementById('sessions'),
834 | sorted_names_list = Object.keys(groups).sort(function (a, b) {
835 | return parseInt(b.slice('tg_'.length), 10) - parseInt(a.slice('tg_'.length), 10);
836 | });
837 | sorted_names_list.forEach(function (el) {
838 | html += self.groupHtmlElement(el, groups[el]);
839 | });
840 | tag.innerHTML = html;
841 | },
842 |
843 | delGroup: function (storage_name, el) {
844 | var self = this;
845 |
846 | sessionModel.del(storage_name, function (answ) {
847 | if (answ.err === 0) {
848 | el.remove();
849 | } else {
850 | self.showErrorMsg(answ.msg);
851 | }
852 | });
853 | },
854 |
855 | showGroupLinks: function (storage_name, el) {
856 | var link_list = document.createElement('div'),
857 | spoiler = el.getElementsByClassName('spoiler')[0],
858 | html = '',
859 | self = this,
860 | grouped_links = sessionLinkModel.groupLinksByWindowId(storage_name);
861 |
862 | grouped_links.grouped_by_windowId.forEach(function (link_arr, ind) {
863 | var win_html = '
' +
864 | '
▪ ' + ui_msg.window_text + ' ' + (ind + 1) + '
';
865 |
866 | link_arr.forEach(function (link) {
867 | win_html += self.linkHtmlElement(storage_name, link);
868 | });
869 | win_html += '
';
870 | html += win_html;
871 | });
872 | link_list.classList.add('links');
873 | link_list.innerHTML = html;
874 | el.insertBefore(link_list, null);
875 | spoiler.setAttribute('name', 'opened');
876 | spoiler.innerHTML = ' ▼';
877 | },
878 |
879 | hideGroupLinks: function (el) {
880 | var spoiler = el.getElementsByClassName('spoiler')[0];
881 |
882 | spoiler.setAttribute('name', 'closed');
883 | spoiler.innerHTML = ' ►';
884 | el.getElementsByClassName('links')[0].remove();
885 | },
886 | setHandlers: function () {
887 | var self = this;
888 |
889 | document.getElementById('sessions').addEventListener('click', function (e) {
890 | var el = e.target,
891 | group_node,
892 | btn,
893 | win_info;
894 |
895 | e.stopPropagation();
896 | switch (el.className) {
897 | case 'del_group':
898 | group_node = el.parentNode.parentNode;
899 | self.delGroup(group_node.id, group_node);
900 | break;
901 | case 'spoiler':
902 | group_node = el.parentNode;
903 | if (el.getAttribute('name') === 'closed') {
904 | self.showGroupLinks(group_node.id, group_node);
905 | } else {
906 | self.hideGroupLinks(group_node);
907 | }
908 | break;
909 | case 'open_group':
910 | // e.button - 0 - left mouse click (cur win), 1 - mouse wheel click (new win)
911 | // mouse wheel works only with no scroll
912 | btn = e.button;
913 | if (btn === 0 && e.ctrlKey === true) {
914 | self.openGroup(el.parentNode.id, 1);
915 | } else {
916 | self.openGroup(el.parentNode.id, e.button);
917 | }
918 | break;
919 | case 'open_in_new_window':
920 | // e.button - 0 - left mouse click (cur win), 1 - mouse wheel click (new win)
921 | self.openGroup(el.parentNode.id, 1);
922 | break;
923 | case 'win_title':
924 | // e.button - 0 - left mouse click (cur win), 1 - mouse wheel click (new win)
925 | // mouse wheel works only with no scroll
926 | btn = e.button;
927 | win_info = (function () {
928 | var win_id_start = el.id.lastIndexOf('_');
929 | return {
930 | storage_name: el.id.slice(0, win_id_start),
931 | window_id: el.id.slice(win_id_start + 1),
932 | };
933 | }());
934 | if (btn === 0 && e.ctrlKey === true) {
935 | self.openWindow(win_info.storage_name, win_info.window_id, 1);
936 | } else {
937 | self.openWindow(win_info.storage_name, win_info.window_id, 0);
938 | }
939 | break;
940 | }
941 |
942 | }, false);
943 | },
944 |
945 | go: function () {
946 | this.showGroups();
947 | this.setHandlers();
948 | },
949 | },
950 |
951 | // mainUI = {
952 | // setHandlers: function () {
953 | // document.body.onkeyup = function (e) {
954 | // if (e.keyCode === 78 && e.target.tagName !== 'input') {
955 | // alert(e.target.tagName);
956 | // }
957 | // };
958 | // }
959 | // },
960 |
961 | navigation;
962 |
963 | if (!chrome.runtime.lastError) {
964 | /* window tab navigation */
965 | navigation = new Tabs('#main_tabs');
966 | navigation.toggle(storage_items.active_tab);
967 | navigation.onToggle(function (tab_name) {
968 | chrome.storage.sync.set({active_tab: tab_name});
969 | });
970 | /* popup tab END */
971 |
972 | if (storage_items.session_watcher) {
973 | sessionsUI.go();
974 | } else {
975 | navigation.toggle('#saved');
976 | // hide session tab
977 | document.querySelector('.tabs_nav').querySelector('[href="#sessions"]').parentNode.style.display = 'none';
978 | }
979 | savedUI.go();
980 | // mainUI.setHandlers();
981 |
982 | } else {
983 | // show sync storage error
984 | document.getElementById('error_msg').style.display = 'block';
985 | document.getElementById('error_msg').innerText = ui_msg.quota_default_item;
986 | }
987 | });
988 | });
989 | });
990 | // TODO blink group el after create, move
991 | // TODO hоt keys
992 |
--------------------------------------------------------------------------------
/js/storage.js:
--------------------------------------------------------------------------------
1 | var storage = {
2 | area: chrome.storage.sync,
3 | default_options: {
4 | date_format: 'eu', // or 'us'
5 | pinned: 0, //do not add pinned tabs
6 | cur_win: 1, // save tabs from current window only
7 | active_tab: '#saved', //or '#sessions'
8 | session_watcher: 1 //extension will watch sessions
9 | }
10 | };
--------------------------------------------------------------------------------
/js/utils.js:
--------------------------------------------------------------------------------
1 | var utils = {
2 | formatDate: function (date, date_format) {
3 | var res,
4 | day = plusZero(date.getDate()),
5 | month = plusZero(date.getMonth() + 1),
6 | year = date.getFullYear(),
7 | hours = plusZero(date.getHours()),
8 | minutes = plusZero(date.getMinutes());
9 |
10 | function plusZero(d) {
11 | return (d.toString()).length === 2 ? d : '0' + d;
12 | }
13 |
14 | switch (date_format) {
15 | case 'eu':
16 | res = day + '-' + month + '-' + year + ' ' + hours + ':' + minutes;
17 | break;
18 | case 'us':
19 | res = year + '-' + month + '-' + day + ' ' + hours + ':' + minutes;
20 | break;
21 | }
22 | return res;
23 | },
24 |
25 | correctUrl: function (url) {
26 | var start_with = ['http://', 'https://', 'ftp://', 'opera://', 'chrome://', 'chrome-extension://', 'chrome-devtools://', 'file://'];
27 | return start_with.some(function (el) {
28 | return (url.indexOf(el) === 0);
29 | }) ? url : 'https://' + url;
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/lib/jsTabs/tabs.css:
--------------------------------------------------------------------------------
1 | ul.tabs_nav {
2 | list-style: none;
3 | margin: 0 0 0 20px;
4 | padding: 0;
5 | }
6 | .tabs_nav li {
7 | display: inline;
8 | float: left;
9 | border: 1px solid #dddddd;
10 | background-color: #ededed;
11 | padding: 5px;
12 | margin-right: 4px;
13 | margin-bottom: -1px;
14 | border-top-right-radius: 3px;
15 | border-top-left-radius: 3px;
16 | cursor: pointer;
17 | }
18 | .tabs_nav li.active {
19 | border-bottom: 1px solid #ffffff;
20 | background-color: #ffffff;
21 | }
22 | .tabs_nav a {
23 | text-decoration: none;
24 | font-weight: bold;
25 | letter-spacing: -1px;
26 | color: #402e16;
27 | }
28 | .tabs_content {
29 | clear: both;
30 | border-top: 1px solid #dddddd;
31 | padding: 20px;
32 | }
--------------------------------------------------------------------------------
/lib/jsTabs/tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jsTabs. No jQuery. IE 10+
3 | * https://github.com/onikienko/jsTabs
4 | *
5 | * @param {string} tabs_id ID of Tabs container with #
6 | * @param {function|object} [handlers] onToggle handlers
7 | * @constructor
8 | */
9 | function Tabs(tabs_id, handlers) {
10 | this.html = document.querySelector(tabs_id);
11 | this.nav_links = this.html.querySelectorAll('.tabs_nav li a');
12 | this.ontoggle_handlers_list = {};
13 |
14 | var self = this;
15 | this.nav_links_array = (function () {
16 | var arr = [];
17 | [].forEach.call(self.nav_links, function (el) {
18 | arr.push(el.hash);
19 | });
20 | return arr;
21 | }());
22 | if (handlers) {
23 | this.onToggle(handlers);
24 | }
25 | this.go_();
26 | }
27 |
28 | Tabs.prototype = {
29 | fireOnToggle_: function (tab_name) {
30 | if (this.ontoggle_handlers_list.hasOwnProperty(tab_name) && this.ontoggle_handlers_list[tab_name].length > 0) {
31 | this.ontoggle_handlers_list[tab_name].forEach(function (handler) {
32 | handler(tab_name);
33 | });
34 | }
35 | },
36 |
37 | toggle: function (tab_name) {
38 | if (tab_name && this.nav_links_array.indexOf(tab_name) !== -1) {
39 | [].forEach.call(this.html.querySelectorAll('.tabs_content>div'), function (el) {
40 | el.style.display = ('#' + el.id === tab_name) ? 'block' : 'none';
41 | });
42 | [].forEach.call(this.nav_links, function (el) {
43 | if (el.hash === tab_name) {
44 | el.parentNode.classList.add('active');
45 | } else {
46 | el.parentNode.classList.remove('active');
47 | }
48 | });
49 | this.fireOnToggle_(tab_name);
50 | }
51 | },
52 |
53 | /**
54 | *
55 | * @param {function|object} handlers {'#tab_name': handler_function} or a single function to bind it on every toggle
56 | */
57 | onToggle: function (handlers) {
58 | var tab,
59 | self = this;
60 |
61 | function addHandler(tab_name, handler) {
62 | if (!self.ontoggle_handlers_list[tab_name]) {
63 | self.ontoggle_handlers_list[tab_name] = [];
64 | }
65 | self.ontoggle_handlers_list[tab_name].push(handler);
66 | }
67 |
68 | if (typeof handlers === 'function') {
69 | this.nav_links_array.forEach(function (el) {
70 | addHandler(el, handlers);
71 | });
72 | } else if (typeof handlers === 'object') {
73 | for (tab in handlers) {
74 | if (this.nav_links_array.indexOf(tab) !== -1) {
75 | addHandler(tab, handlers[tab]);
76 | }
77 | }
78 | }
79 | },
80 |
81 | onNavClick_: function () {
82 | var self = this;
83 | self.html.querySelector('.tabs_nav').addEventListener('click', function (e) {
84 | var hash = e.target.hash,
85 | li;
86 | if (!hash) {
87 | if (e.target.tagName === 'LI') {
88 | li = e.target.querySelector('a');
89 | hash = li.hash;
90 | }
91 | }
92 | if (hash && self.nav_links_array.indexOf(hash) !== -1 && !e.target.parentElement.classList.contains('active')) {
93 | self.toggle(hash);
94 | }
95 | e.preventDefault();
96 | e.stopPropagation();
97 | }, false);
98 | },
99 |
100 | go_: function () {
101 | if (this.nav_links_array.length > 0) {
102 | var hash = window.location.hash;
103 | if (!hash || this.nav_links_array.indexOf(hash) === -1) {
104 | hash = this.nav_links[0].hash;
105 | }
106 | this.toggle(hash);
107 | this.onNavClick_();
108 | }
109 | }
110 | };
111 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "__MSG_extName__",
3 | "description": "__MSG_extDescr__",
4 | "version": "0.3.0",
5 | "manifest_version": 3,
6 | "author": "Mykhailo Onikiienko",
7 |
8 | "default_locale": "en",
9 | "icons": {
10 | "16": "img/ext_icons/16.png",
11 | "32": "img/ext_icons/32.png",
12 | "48": "img/ext_icons/48.png",
13 | "128": "img/ext_icons/128.png"
14 | },
15 |
16 | "action": {
17 | "default_icon": {
18 | "16": "img/ext_icons/16.png",
19 | "32": "img/ext_icons/32.png",
20 | "48": "img/ext_icons/48.png"
21 | },
22 | "default_popup": "popup.html"
23 | },
24 |
25 | "background": {
26 | "service_worker": "js/background.js"
27 | },
28 | "options_page": "options.html",
29 |
30 | "permissions": [
31 | "storage",
32 | "tabs",
33 | "favicon"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{extName}}
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{o_error}}
13 | {{o_saved}}
14 |
15 |
16 |
17 |
18 |
19 | {{extName}}
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
52 |
53 |
54 |
61 |
67 |
68 |
69 |
70 |
{{ o_help_syncStorage }}
71 |
{{ o_help_syncStorage_0 }}
72 |
{{ o_help_syncStorage_1 }}
73 |
{{ o_help_syncStorage_2 }}
74 |
75 |
76 |
87 |
88 |
89 |
107 |
108 |
109 |
110 |
111 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
19 |
20 |
30 |
44 |
58 |
67 |
68 |
69 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | TabHamster Chrome Extension
2 | ===========================
3 |
4 | Session manager and tabs saver. With sync.
5 | ------------------------------------------
6 |
7 | 
8 |
9 | Install for your browser:
10 |
11 | **[Chrome Web Store](https://chrome.google.com/webstore/detail/tabhamster/mkfjjmjmnplabnplceaekkjcmdddokee)**
12 |
13 | ~~**[Opera Market](https://addons.opera.com/extensions/details/tabhamster/)**~~ (not available for Opera anymore)
14 |
15 | ------------------------
16 |
17 | ### Glory to Ukraine. 🇺🇦
18 |
--------------------------------------------------------------------------------