Linkificator, a Firefox Add-on to convert text links into clickable links.
18 |
19 |
20 | This add-on parse text parts of HTML pages to match hypertext patterns not correctly encoded (i.e. not part of an anchor) and add an anchor to enable a standard mouse click to access the target as specified by the hypertext.
21 |
22 |
23 |
24 | For testing purposes, I have compiled a fairly exhaustive list of test cases. Linkificator should recognize each of these, unless otherwise marked.
25 |
23 | Compatibility: The extension requires at least Firefox 55. Because the extension makes use of modern web technologies and the latest WebExtension APIs, support for older versions of Firefox is not possible for technical reasons.
25 |
26 | The current version is an hybrid version (i.e. displayed as LEGACY in Firefox) to enable to propagate current settings to the new environment. Next version will be a pure WebExtension add-on.
27 |
28 | Icons by: FastIcon.com
29 | Localization infrastructure: Crowdin.com
30 | ]]>
31 |
32 |
33 | Support E-mail on Add-on page).
35 |
36 | Help is welcomed to manage various localization. For that purpose, send-me an e-mail (see Support E-mail on Add-on page) to request translation materials.
37 | ]]>
38 |
39 |
40 |
41 |
A tooltip shows which URL will be used
42 |
The widget gives some information about treatment
43 |
Widget menu
44 |
The widget gives some information about treatment
45 |
Linkificator settings
46 |
Advanced settings, links management
47 |
Advanced settings, custom rules management
48 |
Advanced settings, configuration management, general settings
49 |
Advanced settings, configuration management, top level domains
50 |
51 |
52 |
53 |
56 |
57 |
Short term features
58 |
59 |
Add support of more locales (locales currently supported: en-US, fr and ru)
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/l10n/linkificator-description.fr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | NOTE IMPORTANTE POUR LES UTILISATEURS DES PRECEDENTES VERSIONS - A LIRE
13 |
14 | Linkificator a été entièrement re-développé en s'appuyant sur la technologie WebExtension pour permettre la compatibilité avec Firefox 57+. Certaines fonctionnalités, disponibles dans les versions précédentes, ne peuvent plus être offertes dans le cadre de WebExtension. Celles-ci seront de nouveau intégrées à Linkificator dés que Mozilla offre de nouvelles possibilités à WebExtension.
15 | Les fonctionnalités suivantes ne sont donc plus disponibles:
16 | ]]>
17 |
18 |
Raccourcis clavier
19 |
Les diverses configurations des clics sur le Widget
20 |
support du protocole about:
21 |
Importation/Exportation des réglages
22 |
23 | Compatibilité: Ce module nécessite, au minimum, la version 55 de Firefox due à la mise en œuvre de technologies nouvelles (WebExtension). Le support des versions plus anciennes n'est donc pas possible pour des raisons techniques.
25 |
26 | Cette version est une version hybride (i.e. affiché comme OBSOLÈTE dans Firefox) pour permettre la transmission des réglages actuels vers le nouvel environnement. La prochaine version sera un module exclusivement basé sur l'API WebExtension.
27 |
28 | Icones fournies par FastIcon.com
29 | Infrastructure de localisation : Crowdin.com
30 | ]]>
31 |
32 |
33 | Courriel d'assistance sur la page du module) plutôt que de poster un avis.
35 |
36 | Toute aide pour la localisation est la bienvenue. Pour cela, il suffit de m'envoyer un courriel (voir Courriel d'assistance sur la page du module) pour demander les éléments nécessaires à la localisation.
37 | ]]>
38 |
39 |
40 |
41 |
Une info-bulle montre quel URL sera utilisé
42 |
Le widget fournit des informations concernant le traitement
43 |
Menu du widget
44 |
Le widget fournit des informations concernant le traitement
45 |
Réglages généraux
46 |
Réglages avancés, gestion des liens
47 |
Réglages avancés, gestion des règles utilisateur
48 |
Réglages avancés, gestion de la configuration, paramètres généraux
49 |
Réglages avancés, gestion de la configuration, listes des domaines de premier niveau
50 |
51 |
52 |
53 |
56 |
57 |
Fonctionnalités court terme
58 |
59 |
Ajout de nouvelles localisations (locales actuellement supportées : en-US, fr et ru)
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/l10n/linkificator-description.ru.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | ВАЖНОЕ ЗАМЕЧАНИЕ ДЛЯ ПОЛЬЗОВАТЕЛЕЙ ПРЕДЫДУЩИХ ВЕРСИЙ - ПОЖАЛУЙСТА, ПРОЧИТАЙТЕ
13 |
14 | Linkificator был разработан с нуля, как так называемый WebExtension. Это делает Linkificator совместимым с Firefox 57 и более поздними версиями. Не все опции предыдущей версии в настоящее время доступны для WebExtension. Как только Mozilla реализует поддержку отсутствующих функциональных возможностей в Firefox, они будут интегрированы в будущие версии Linkificator.
15 | В результате некоторые функции больше недоступны:
16 | ]]>
17 |
18 |
Горячие клавиши
19 |
Различные настройки виджета
20 |
Поддержка about: протокола
21 |
Импорт/Экспорт настроек
22 |
23 | Совместимость: Для расширения требуется Firefox 55 или более поздняя версия. Поскольку расширение использует современные веб-технологии и последние API-интерфейсы WebExtension, поддержка старых версий Firefox невозможна по техническим причинам.
25 |
26 | Текущая версия представляет собой гибридную версию (т.е. отображаемую как УСТАРЕВШЕЕ в Firefox), чтобы включить применение текущих настроек в новой среде. Следующая версия будет чистым дополнением WebExtension.
27 |
28 | Значки: FastIcon.com
29 | Система локализации: Crowdin.com
30 | ]]>
31 |
32 |
33 | Адрес эл. почты поддержки на странице дополнения).
36 |
37 | Приветствуется помощь по созданию различных локализаций. Для этого отправьте разработчику письмо по электронной почте (см. Адрес эл. почты поддержки на странице дополнения) для запроса локализуемых материалов.
38 | ]]>
39 |
40 |
41 |
42 |
Подсказка показывает, какой URL будет использоваться
43 |
Виджет предоставляет некоторую информацию об обработке
44 |
Меню виджета
45 |
Виджет предоставляет некоторую информацию об обработке
46 |
Настройки Linkificator
47 |
Расширенные настройки, управление ссылками
48 |
Расширенные настройки, управление пользовательскими правилами
49 |
Расширенные настройки, управления конфигурацией, общие настройки
50 |
Расширенные настройки, управление конфигурацией, домены верхнего уровня
51 |
52 |
53 |
54 |
58 |
59 |
Функции в краткосрочной перспективе
60 |
61 |
Добавить поддержку нескольких языков (в настоящее время поддерживаются языки: en-US, fr и ru)
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/legacy/chrome.manifest:
--------------------------------------------------------------------------------
1 |
2 | # This Source Code Form is subject to the terms of the Mozilla Public
3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 |
6 | # chrome manifest - Linkificator's module
7 | # author: MarkaPola
8 |
9 | content linkificator chrome/content/
10 | skin linkificator classic/1.0 chrome/skin/
11 |
12 | locale linkificator en-US chrome/locale/en-US/
13 | locale linkificator fr chrome/locale/fr/
14 | locale linkificator ru chrome/locale/ru/
15 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/en-US/advanced-options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/en-US/global.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # en-US translation - Advanced Settings module
8 | # author: MarkaPola
9 |
10 | advanced-settings.rules-validation.title=Custom Rule Validation
11 | advanced-settings.rules-validation.empty-field=Fields must not be empty
12 | advanced-settings.rules-validation.invalid-re=Errors on regular expressions
13 | advanced-settings.rules-validation.invalid-pattern=Pattern is not a valid regular expression
14 | advanced-settings.rules-validation.invalid-url=URL is not a valid regular expression
15 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/en-US/options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/fr/advanced-options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/fr/global.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # fr translation - Advanced Settings module
8 | # author: MarkaPola
9 |
10 | advanced-settings.rules-validation.title=Validation r\u00e8gle utilisateur
11 | advanced-settings.rules-validation.empty-field=Les champs ne doivent pas \u00eatre vides
12 | advanced-settings.rules-validation.invalid-re=Erreurs dans les espressions r\u00e9guli\u00e8res
13 | advanced-settings.rules-validation.invalid-pattern=L'expression r\u00e9guli\u00e8re pour le mod\u00e8le est invalide
14 | advanced-settings.rules-validation.invalid-url=L'expression r\u00e9guli\u00e8re pour l'URL est invalide
15 |
16 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/fr/options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/gl/advanced-options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/gl/global.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # gl translation - Advanced Settings module
8 | # author:
9 |
10 | advanced-settings.rules-validation.title=Personalizar regra de validaci\u00f3n
11 | advanced-settings.rules-validation.empty-field=Os campos non deben estar baleiros
12 | advanced-settings.rules-validation.invalid-re=Erros nas expresi\u00f3ns regulares
13 | advanced-settings.rules-validation.invalid-pattern=Pattern is not a valid regular expression
14 | advanced-settings.rules-validation.invalid-url=URL is not a valid regular expression
15 |
16 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/gl/options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/ru/advanced-options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/ru/global.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # ru translation - Advanced Settings module
8 | # author: Капелька Яда
9 |
10 | advanced-settings.rules-validation.title=\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430
11 | advanced-settings.rules-validation.empty-field=\u041f\u043e\u043b\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c\u0438
12 | advanced-settings.rules-validation.invalid-re=\u041e\u0448\u0438\u0431\u043a\u0438 \u0432 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u0445
13 | advanced-settings.rules-validation.invalid-pattern=\u0428\u0430\u0431\u043b\u043e\u043d \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c
14 | advanced-settings.rules-validation.invalid-url=URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c
15 |
16 |
--------------------------------------------------------------------------------
/legacy/chrome/locale/ru/options.dtd:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/legacy/chrome/skin/advanced-options.css:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * This Source Code is subject to the terms of the Mozilla Public License
4 | * version 2.0 (the "License"). You can obtain a copy of the License at
5 | * http://mozilla.org/MPL/2.0/.
6 | */
7 |
8 | @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
9 | @namespace html url("http://www.w3.org/1999/xhtml");
10 |
11 | #advanced-settings {
12 | -moz-appearance: window !important;
13 | }
14 |
15 | #advanced-settings\.tabbox {
16 | width: 500px;
17 | }
18 |
19 | .advanced-settings\.custom-rules\.edit-button {
20 | border-style: none !important;
21 | -moz-user-focus: normal;
22 | list-style-image: url("edit.png");
23 | -moz-appearance: none;
24 | -moz-image-region: rect(0px, 14px, 14px, 0px);
25 | }
26 |
27 | .advanced-settings\.custom-rules\.edit-button:hover {
28 | -moz-image-region: rect(0px, 28px, 14px, 14px);
29 | }
30 |
31 | .advanced-settings\.custom-rules\.delete-button {
32 | border-style: none !important;
33 | -moz-user-focus: normal;
34 | list-style-image: url("close.png");
35 | -moz-appearance: none;
36 | -moz-image-region: rect(0px, 14px, 14px, 0px);
37 | }
38 |
39 | .advanced-settings\.custom-rules\.delete-button:hover {
40 | -moz-image-region: rect(0px, 28px, 14px, 14px);
41 | }
42 |
43 | #advanced-settings\.custom-rules\.panel {
44 | width: 450px;
45 | }
46 | .advanced-settings\.custom-rules\.panel\.textbox {
47 | width: 320px;
48 | }
49 |
50 | .advanced-settings\.configuration\.textbox {
51 | width: 330px;
52 | }
53 |
--------------------------------------------------------------------------------
/legacy/chrome/skin/arrow-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/arrow-circle.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/close.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/edit.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/empty.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/export.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/extension.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/extension.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/favicon.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/icon128.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/icon256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/icon256.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/icon48.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/icon64.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/import.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link-exclude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link-exclude.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link-include.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link-include.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link-update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link-update.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link16-excluded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link16-excluded.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link16-manual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link16-manual.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link16-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link16-off.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link16-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link16-on.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link32-excluded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link32-excluded.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link32-manual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link32-manual.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link32-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link32-off.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/link32-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/link32-on.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/release-notes.css:
--------------------------------------------------------------------------------
1 |
2 | .large-text {
3 | font-size: 120%;
4 | }
5 |
6 | .version-changes {
7 | position: relative;
8 | left: 5%;
9 | }
10 |
11 | .feature-list-title {
12 | font-size: 115%;
13 | font-weight: bold;
14 | color: rgb(30, 100, 255);
15 | }
16 |
17 | html {
18 | background: linear-gradient(to bottom, rgb(220, 245, 255), rgb(255, 255, 255));
19 | }
20 |
21 | #linkificator-icon {
22 | vertical-align: middle;
23 | float: left;
24 | margin-top: 10pt;
25 | margin-right: 30pt;
26 | margin-bottom: 10pt;
27 | margin-left: 10pt;
28 | }
29 |
--------------------------------------------------------------------------------
/legacy/chrome/skin/utilities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/legacy/chrome/skin/utilities.png
--------------------------------------------------------------------------------
/legacy/chrome/skin/widget.css:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | *
6 | * widget style-sheet - Linkificator's module
7 | * author: MarkaPola
8 | */
9 |
10 | @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
11 |
12 | @-moz-document url("chrome://browser/content/browser.xul") {
13 | .linkificator-on {
14 | list-style-image: url("chrome://linkificator/skin/link16-on.png");
15 | }
16 | /* Added for Australis support */
17 | .linkificator-on[cui-areatype="menu-panel"],
18 | toolbarpaletteitem[place="palette"] > .linkificator-on {
19 | list-style-image: url("chrome://linkificator/skin/link32-on.png");
20 | }
21 |
22 | .linkificator-manual {
23 | list-style-image: url("chrome://linkificator/skin/link16-manual.png");
24 | }
25 | /* Added for Australis support */
26 | .linkificator-manual[cui-areatype="menu-panel"],
27 | toolbarpaletteitem[place="palette"] > .linkificator-manual {
28 | list-style-image: url("chrome://linkificator/skin/link32-manual.png");
29 | }
30 |
31 | .linkificator-off {
32 | list-style-image: url("chrome://linkificator/skin/link16-off.png");
33 | }
34 | /* Added for Australis support */
35 | .linkificator-off[cui-areatype="menu-panel"],
36 | toolbarpaletteitem[place="palette"] > .linkificator-off {
37 | list-style-image: url("chrome://linkificator/skin/link32-off.png");
38 | }
39 |
40 | .linkificator-excluded {
41 | list-style-image: url("chrome://linkificator/skin/link16-excluded.png");
42 | }
43 | /* Added for Australis support */
44 | .linkificator-excluded[cui-areatype="menu-panel"],
45 | toolbarpaletteitem[place="palette"] > .linkificator-excluded {
46 | list-style-image: url("chrome://linkificator/skin/link32-excluded.png");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/legacy/data/history.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* tab history handling - Linkificator's module
7 | * author: MarkaPola */
8 |
9 | // catch backward/forward button events to handle widget update
10 | function toPage (event) {
11 | if (event.persisted) {
12 | self.port.emit('pageshow');
13 | }
14 | }
15 |
16 | self.port.on('attach', function () {
17 | window.top.addEventListener('pageshow', toPage, false);
18 | });
19 |
--------------------------------------------------------------------------------
/legacy/data/menu/menu.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* This Source Code Form is subject to the terms of the Mozilla Public
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 |
7 | /* panel state and mouse clicks handling - Linkificator's module
8 | * author: MarkaPola */
9 |
10 | self.on("context", function (node) {
11 | var state = State(document);
12 |
13 | return state.isConfigured() || state.isComplete();
14 | });
15 |
16 | self.on('click', function (node, data) {
17 | self.postMessage(data);
18 | });
19 |
--------------------------------------------------------------------------------
/legacy/data/state.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* state parsing management - Linkificator's module
7 | * author: MarkaPola */
8 |
9 | function State (document) {
10 | "use strict";
11 |
12 | const statusLabel = "linkificator-status";
13 |
14 | var body = document.body;
15 |
16 | // if (action == 'parse' && body.hasAttribute(statusLabel) && body.getAttribute(statusLabel) != 'configured') {
17 | // // parsing is already in process or done
18 | // return null;
19 | // }
20 | // if (action == 're-parse' && (!body.hasAttribute(statusLabel) || (body.getAttribute(statusLabel) != "complete" && body.getAttribute(statusLabel) != "configured"))) {
21 | // // parsing is not yet started or is in process
22 | // return null;
23 | // }
24 |
25 | // if (action == 'undo' && !body.hasAttribute(statusLabel)) {
26 | // // parsing is not yet started
27 | // return null;
28 | // }
29 |
30 | // if (action == 'reset') {
31 | // if (body.hasAttribute(statusLabel)) {
32 | // body.removeAttribute(statusLabel);
33 | // }
34 | // return null;
35 | // }
36 |
37 | //body.setAttribute(statusLabel, action == 'undo' ? "in-undo" : "in-process");
38 |
39 | return {
40 | isValid: function (action) {
41 | if (action == 'parse' && body.hasAttribute(statusLabel) && body.getAttribute(statusLabel) != 'configured') {
42 | // parsing is already in process or done
43 | return false;
44 | }
45 | if (action == 're-parse' && (!body.hasAttribute(statusLabel) || (body.getAttribute(statusLabel) != "complete" && body.getAttribute(statusLabel) != "configured"))) {
46 | // parsing is not yet started or is in process
47 | return false;
48 | }
49 |
50 | if (action == 'undo' && !body.hasAttribute(statusLabel)) {
51 | // parsing is not yet started
52 | return false;
53 | }
54 |
55 | return true;
56 | },
57 |
58 | process: function () {
59 | body.setAttribute(statusLabel, "in-process");
60 | },
61 | inProcess: function () {
62 | return body.hasAttribute(statusLabel)
63 | && body.getAttribute(statusLabel) == "in-process";
64 | },
65 |
66 | configured: function () {
67 | body.setAttribute(statusLabel, "configured");
68 | },
69 | isConfigured: function () {
70 | return body.hasAttribute(statusLabel)
71 | && body.getAttribute(statusLabel) == "configured";
72 | },
73 |
74 | complete: function () {
75 | body.setAttribute(statusLabel, "complete");
76 | },
77 | isComplete: function() {
78 | return body.hasAttribute(statusLabel)
79 | && body.getAttribute(statusLabel) == "complete";
80 | },
81 |
82 | undo: function () {
83 | body.setAttribute(statusLabel, "in-undo");
84 | },
85 |
86 | reset: function () {
87 | if (body.hasAttribute(statusLabel)) {
88 | body.removeAttribute(statusLabel);
89 | }
90 | }
91 | };
92 | }
93 |
--------------------------------------------------------------------------------
/legacy/data/statistics.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* Statistics management - Linkificator's module
7 | * author: MarkaPola */
8 |
9 |
10 | function Statistics (document, action) {
11 | "use strict";
12 |
13 | const countLabel = "linkificator-count";
14 | const timeLabel = "linkificator-time";
15 |
16 | var body = document.body;
17 |
18 | function getInt (value) {
19 | let v = parseInt(value, 10);
20 | return isNaN(v) ? 0 : v;
21 | }
22 |
23 | function getStats (count, time) {
24 | return {links: getInt(count), time: getInt(time)};
25 | }
26 |
27 | if (action == 'undo') {
28 | if (body.hasAttribute(countLabel)) {
29 | body.removeAttribute(countLabel);
30 | body.removeAttribute(timeLabel);
31 | }
32 | return null;
33 | }
34 |
35 | if (action == 'get-statistics') {
36 | return {
37 | get: function () {
38 | if (body.hasAttribute(countLabel)) {
39 | return getStats(body.getAttribute(countLabel),
40 | body.getAttribute(timeLabel));
41 | } else {
42 | return getStats(0, 0);
43 | }
44 | }
45 | };
46 | }
47 |
48 | var elapse = 0;
49 | var startTime = Date.now();
50 |
51 | if (action === 'parse') {
52 | body.setAttribute(countLabel, 0);
53 | body.setAttribute(timeLabel, 0);
54 | } else if (action == 're-parse') {
55 | elapse = getInt(body.getAttribute(timeLabel));
56 | }
57 |
58 | return {
59 | store: function (count) {
60 | let links = getInt(body.getAttribute(countLabel));
61 |
62 | body.setAttribute(countLabel, links+count);
63 | body.setAttribute(timeLabel, elapse+(Date.now()-startTime));
64 | },
65 |
66 | get: function () {
67 | if (body.hasAttribute(countLabel)) {
68 | return getStats(body.getAttribute(countLabel),
69 | body.getAttribute(timeLabel));
70 | } else {
71 | return getStats(0, 0);
72 | }
73 | }
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/legacy/data/utilities/document.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // dom.js - Linkificator's module
7 | // author: MarkaPola
8 | //
9 |
10 | //
11 | // DOcument tools
12 | //
13 |
14 | /*
15 | * Retrieve MIME type from DOM Document
16 | *
17 | * @param [object] document: DOM Document object
18 | */
19 |
20 | function Document (doc) {
21 | "use strict";
22 |
23 | var document = doc;
24 |
25 | return {
26 | get contentType () {
27 | let contentType = null;
28 |
29 | if (document) {
30 | // specify multiple CSS Selectors because 'i' flag is not supported before FF47
31 | let ct = document.querySelector('meta[http-equiv="content-type"], meta[http-equiv="Content-Type"], meta[http-equiv="Content-type"], meta[http-equiv="content-Type"]');
32 | if (ct) {
33 | let content = ct.getAttribute('content');
34 | if (content) {
35 | contentType = content.trim().split(/;|\s/)[0];
36 | }
37 | }
38 |
39 | if (! contentType) {
40 | // Check possible plain text page
41 | if (document.querySelector('link[href="resource://gre-resources/plaintext.css"]')) {
42 | contentType = 'text/plain';
43 | }
44 | }
45 | }
46 |
47 | if (! contentType) {
48 | contentType = 'text/html';
49 | }
50 |
51 | return contentType;
52 | }
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/legacy/data/utilities/thread.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* Thread helper - Linkificator's module
7 | * author: MarkaPola */
8 |
9 | // javascript does not support parallel executions
10 | // to avoid any CPU total comsumption and UI freezes, this utility help execute tasks in the background
11 |
12 | // arguments to Thread function are:
13 | // action: object which MUST expose, at least, the following functions:
14 | // * execute: will execute a part of the work and must return true if work is completed
15 | // * finish: will execute all remaining tasks to complete the work
16 | // * abort: thread was aborted, remaining work must not be done
17 | // * complete: this function will be called when all work is done
18 | // interval: waiting time (in ms) between each execution. If not specified, 10ms will be used.
19 | // all functions are argumentless except abort and complete which pass thread reference.
20 | //
21 | // Thread functions are:
22 | // start: launch execution of action object in the background.
23 | // terminate: request to complete action in the foreground.
24 | // kill: stop background execution. action will not terminate.
25 | //
26 | // Here is a small example: each part of the work will be done at 100ms interval.
27 | //
28 | // var test = {
29 | // index: 0,
30 | // total: 100,
31 | //
32 | // execute: function () {
33 | // var part = Math.min((this.index + 3), this.total);
34 | // while (this.index < part) {
35 | // console.log (this.index);
36 | // this.index++;
37 | // }
38 | // console.log ("chunk done");
39 | //
40 | // return this.index == this.total;
41 | // },
42 | // finish: function () {
43 | // while (this.index++ < this.total)
44 | // console.log (this.index);
45 | // console.log ("complete done");
46 | // },
47 | // abort: function (thread) {
48 | // console.log ("aborted at " + this.index);
49 | // },
50 | // complete: function (thread) {
51 | // console.log ("completed");
52 | // }
53 | // };
54 | //
55 | // var thread = Thread (test, 100);
56 | // thread.start ();
57 | //
58 |
59 |
60 | function Thread (action, interval) {
61 | "use strict";
62 |
63 | var thread = {
64 | ref: null,
65 | interval: 10,
66 | action: null,
67 | worker: null,
68 | completed: false
69 | };
70 | if (interval)
71 | thread.interval = interval;
72 | thread.action = action;
73 |
74 | function execute () {
75 | if (thread.completed)
76 | return;
77 |
78 | if (thread.action.execute()) {
79 | thread.completed = true;
80 | thread.action.complete(thread.ref);
81 | } else {
82 | thread.worker = setTimeout(function(){execute();}, interval);
83 | }
84 | }
85 |
86 | function finish (timeout) {
87 | if (thread.completed)
88 | return;
89 |
90 | clearTimeout(thread.worker);
91 |
92 | let terminate = function() {
93 | thread.completed = true;
94 | thread.action.finish();
95 | thread.action.complete(thread.ref);
96 | };
97 |
98 | if (timeout) {
99 | thread.worker = setTimeout(terminate, timeout);
100 | } else {
101 | terminate();
102 | }
103 | }
104 |
105 | function abort () {
106 | if (thread.completed)
107 | return;
108 |
109 | clearTimeout(thread.worker);
110 |
111 | thread.completed = true;
112 | thread.action.abort(thread.ref);
113 | }
114 |
115 | thread.ref = {
116 | start: function () {
117 | execute();
118 | },
119 |
120 | terminate: function (timeout) {
121 | finish();
122 | },
123 |
124 | kill: function () {
125 | abort();
126 | }
127 | };
128 |
129 | return thread.ref;
130 | }
131 |
--------------------------------------------------------------------------------
/legacy/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | linkificator@markapola
8 | 2.3.2
9 | 2
10 | true
11 | false
12 | true
13 |
14 | chrome://linkificator/skin/icon48.png
15 |
16 |
17 |
18 |
19 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
20 | 38.0
21 | 47.*
22 |
23 |
24 |
25 |
26 | Linkificator
27 |
28 | 2
29 | Converts text links into clickable links...
30 | MarkaPola
31 | Icons by: FastIcon.com (http://www.fasticon.com)
32 | Localization infrastructure: Crowdin.com (http://crowdin.com)
33 | MarkaPola
34 |
35 |
36 |
37 | en-US
38 | MarkaPola
39 | Icons from www.fasticon.com
40 | Localization infrastructure: Crowdin.com (http://crowdin.com)
41 | MarkaPola
42 |
43 | Linkificator
44 | Converts text links into clickable links...
45 |
46 |
47 |
48 |
49 | fr
50 | MarkaPola
51 | Icones fournies par www.fasticon.com
52 | Infrastructure de localisation : Crowdin.com (http://crowdin.com)
53 | MarkaPola
54 |
55 | Linkificator
56 | Transforme les liens textuels en liens hypertexte cliquables...
57 |
58 |
59 |
60 |
61 | ru
62 | MarkaPola
63 | Icons from www.fasticon.com
64 | Localization infrastructure: Crowdin.com (http://crowdin.com)
65 | Капелька Яда
66 |
67 | Linkificator
68 | Преобразует текстовые ссылки в кликабельные.
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/legacy/lib/main.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // The main module of the Linkificator Add-on.
7 | // author: MarkaPola
8 |
9 | var Context = {};
10 |
11 | // workers management
12 | var workers = [];
13 | workers.apply = function (tab, action) {
14 | this.forEach (function (worker, index, array) {
15 | if (worker.tab === tab) {
16 | try {
17 | action (worker, index, array);
18 | } catch (e) {
19 | // exception could be raised if history is used because
20 | // some workers are attached to a hidden page, so ERR_FROZEN is raised
21 | }
22 | }
23 | });
24 | };
25 | workers.detach = function (worker) {
26 | var index = this.indexOf(worker);
27 | if(index != -1) {
28 | this.splice(index, 1);
29 | }
30 | };
31 |
32 |
33 | exports.main = function (options) {
34 | "use strict";
35 |
36 | var prefs = require('sdk/preferences/service');
37 | var pageMod = require('sdk/page-mod');
38 | var tabs = require('sdk/tabs');
39 |
40 | var data = require('sdk/self').data;
41 |
42 | // for new installation or upgrade, show an informational page
43 | if (options.loadReason == 'install' || options.loadReason == 'upgrade') {
44 | require('sdk/timers').setTimeout(function() {
45 | tabs.open("chrome://linkificator/content/release-notes.xhtml");
46 | }, 2000);
47 | }
48 |
49 | var configurator = require('./configurator').Configurator();
50 | var controler = require('./controler').Controler(configurator);
51 | Context = {configurator: configurator, controler: controler};
52 |
53 | function Statistics () {
54 | this.links = 0;
55 | this.time = 0;
56 | }
57 |
58 | // process specified tab
59 | function process (action, tab) {
60 | workers.apply(tab, function (worker) {
61 | let properties = configurator.properties;
62 | if (tab.contentType) {
63 | properties.document = {contentType: tab.contentType};
64 | } else {
65 | properties.document = {contentType: null};
66 | }
67 |
68 | worker.port.emit(action, properties);
69 | });
70 | }
71 |
72 | tabs.on('activate', function (tab) {
73 | let processTab = function(tab) {
74 | tab.removeListener('ready', processTab);
75 |
76 | controler.setStatus(tab);
77 |
78 | // re-launch valid action
79 | if (controler.isValidDocument(tab) && controler.linkifyURL(tab)) {
80 | process(controler.isActive() ? 'parse' : 'undo', tab);
81 | }
82 | };
83 |
84 | let worker = tab.attach({contentScript: "self.port.emit ('readyState', document.readyState && (document.readyState == 'interactive' || document.readyState == 'complete'));"});
85 |
86 | worker.port.on('readyState', function(ready) {
87 | if (ready) {
88 | processTab(tab);
89 | } else {
90 | tab.on('ready', processTab);
91 | }
92 | });
93 | });
94 | tabs.on('ready', function (tab) {
95 | controler.setStatus(tab);
96 | });
97 |
98 | // to handle page history browsing
99 | pageMod.PageMod({
100 | include: ["*", "file://*"],
101 | attachTo: ["existing", "top"],
102 | contentScriptWhen: 'start',
103 | contentScriptFile: data.url("history.js"),
104 |
105 | onAttach: function (worker) {
106 | let tab = worker.tab;
107 |
108 | worker.port.on('pageshow', function () {
109 | controler.setStatus(tab);
110 |
111 | if (controler.isValidDocument(tab) && controler.linkifyURL(tab)) {
112 | process(controler.isActive() ? 'parse' : 'undo', tab);
113 | }
114 | });
115 |
116 | worker.port.emit('attach');
117 | }
118 | });
119 |
120 | // parsing of all frames
121 | pageMod.PageMod({
122 | include: ["*", "file://*"],
123 | attachTo: ["existing", "top", "frame"],
124 | contentScriptWhen: 'end',
125 | contentScriptFile: [data.url("utilities/thread.js"), data.url("utilities/document.js"),
126 | data.url("statistics.js"), data.url("state.js"),
127 | data.url("linkificator.js")],
128 |
129 | onAttach: function (worker) {
130 | let tab = worker.tab;
131 |
132 | if (!tab) {
133 | // not content script attached to worker
134 | return;
135 | }
136 |
137 | let properties = configurator.properties;
138 |
139 | if (tab.contentType) {
140 | properties.document = {contentType: tab.contentType};
141 | tab.linkificator = {contentType: tab.contentType, statistics: null};
142 | } else {
143 | properties.document = {contentType: null};
144 | tab.linkificator = {contentType: 'text/html', statistics: null};
145 | }
146 |
147 | controler.setStatus(tab);
148 |
149 | if (!controler.isValidDocument(tab)) {
150 | return;
151 | }
152 |
153 | // store worker to enable to retrieve statistics and re-parsing on add-on re-activation
154 | workers.push(worker);
155 | worker.on('detach', function () {
156 | workers.detach(this);
157 | });
158 |
159 | worker.port.on('content-type', function (data) {
160 | tab.linkificator.contentType = data;
161 | properties.document.contentType = data;
162 |
163 | controler.setStatus(tab);
164 | });
165 |
166 | worker.port.on('document-changed', function () {
167 | if (workers.indexOf(worker) !== -1) {
168 | controler.setStatus(tab);
169 | worker.port.emit(controler.linkifyURL(tab) ? 're-parse' : 'undo', properties);
170 | }
171 | });
172 |
173 | worker.port.on('statistics', function (data) {
174 | let statistics = tab.linkificator.statistics;
175 |
176 | statistics.links += data.links;
177 | statistics.time = Math.max(statistics.time, data.time);
178 | controler.setStatistics(tab, statistics);
179 | });
180 |
181 | worker.port.on('open-url', function (action) {
182 | if (action.button === 'left') {
183 | tabs.activeTab.url = action.url;
184 | } else {
185 | tabs.open({
186 | url: action.url,
187 | inBackground: prefs.get('browser.tabs.loadInBackground', false)
188 | });
189 | }
190 | });
191 |
192 | if (controler.isActive() && controler.linkifyURL(tab)) {
193 | worker.port.emit('initial-parse', properties);
194 | }
195 | }
196 | });
197 |
198 | // compute statistics
199 | controler.on('statistics', function (tab) {
200 | if (controler.isValidDocument(tab) && controler.linkifyURL(tab)) {
201 | tab.linkificator.statistics = new Statistics;
202 |
203 | workers.apply(tab, function (worker) {
204 | worker.port.emit('get-statistics');
205 | });
206 | }
207 | });
208 |
209 | // to re-parse current tab on user's request
210 | controler.on('re-parse', function (tab) {
211 | process('re-parse', tab);
212 | });
213 | // to ensure linkification on add-on reactivation through widget or keyboard shortcut
214 | // or inclusion of previously excluded url
215 | controler.on('activate', function (tab) {
216 | process('parse', tab);
217 | });
218 | // to revert all linkificator changes
219 | controler.on('undo', function (tab) {
220 | process('undo', tab);
221 | });
222 | };
223 |
224 | exports.onUnload = function (reason) {
225 | "use strict";
226 |
227 | if (reason == 'disable' || reason == 'uninstall') {
228 | // reload tabs to get unmodified content
229 | let tabs = [];
230 |
231 | for (let worker of workers) {
232 | if (tabs.indexOf(worker.tab) == -1)
233 | tabs.push(worker.tab);
234 | }
235 |
236 | for (let tab of tabs) {
237 | tab.reload();
238 | }
239 | }
240 | };
241 |
--------------------------------------------------------------------------------
/legacy/lib/ui/australis/menu.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // australis/menu.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage Australis UI elements
12 | //
13 |
14 |
15 | "use strict";
16 |
17 | const {Cu} = require('chrome');
18 | const {ShortcutUtils} = Cu.import('resource://gre/modules/ShortcutUtils.jsm');
19 |
20 | const { toJSON: jsonify } = require("sdk/keyboard/utils");
21 |
22 | const dom = require('../../util/dom');
23 | const panels = require('./panel');
24 |
25 |
26 | function MenuEvent (nodes)
27 | {
28 | let menu = {};
29 |
30 | for (let id in nodes) {
31 | let entry = nodes[id];
32 | // add management interface for each menu entry
33 | menu[id] = {
34 | get node () {
35 | return entry;
36 | },
37 |
38 | set label (data) {
39 | entry.setAttribute('label', data);
40 | },
41 | set image (data) {
42 | entry.setAttribute('image', data);
43 | },
44 | set checked (data) {
45 | if (data)
46 | entry.setAttribute('checked', 'true');
47 | },
48 | set shortcut (data) {
49 | let hotkey = jsonify(data);
50 | let keyNode = entry.ownerDocument.createElementNS(dom.NAMESPACES.xul, 'key');
51 | keyNode.setAttribute('modifiers', hotkey.modifiers.join(' '));
52 | keyNode.setAttribute('key', hotkey.key);
53 | entry.setAttribute('shortcut', ShortcutUtils.prettifyShortcut(keyNode));
54 | },
55 | get disabled () {
56 | let data = entry.getAttribute('disabled');
57 | return (data && data === 'true');
58 | },
59 | set disabled (data) {
60 | if (data)
61 | entry.setAttribute('disabled', 'true');
62 | else
63 | entry.removeAttribute('disabled');
64 | }
65 | };
66 | }
67 |
68 | return menu;
69 | }
70 |
71 | const MenuTrait = function (options) {
72 | if (!options) return;
73 |
74 | this.menu = null;
75 |
76 | this.handlers = {
77 | show: options.onShow,
78 | hide: options.onHide
79 | };
80 |
81 | this.onShow = function (event, nodes) {
82 | if (this.handlers.show)
83 | this.handlers.show(MenuEvent(nodes));
84 | }.bind(this);
85 | this.onHide = function (event, nodes) {
86 | if (this.handlers.hide)
87 | this.handlers.hide(MenuEvent(nodes));
88 | }.bind(this);
89 |
90 | if (options.onShow)
91 | options.onShow = this.onShow;
92 | if (options.onHide)
93 | options.onHide = this.onHide;
94 |
95 | this.panel = panels.Panel(options);
96 | };
97 |
98 | exports.Menu = function (options) {
99 | var mt = new MenuTrait(options);
100 |
101 | return {
102 | get panel () {
103 | return mt.panel;
104 | },
105 |
106 | on: function (event, action) {
107 | if (event === 'show') {
108 | mt.handlers.show = action;
109 | } else if (event === 'hide') {
110 | mt.handlers.hide = action;
111 | }
112 | },
113 |
114 | destroy: function () {
115 | mt.panel.destroy();
116 | }
117 | };
118 | };
119 |
--------------------------------------------------------------------------------
/legacy/lib/ui/australis/panel.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // australis/panel.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage Australis UI elements
12 | //
13 |
14 |
15 | "use strict";
16 |
17 | const unload = require('../../util/unload').unload;
18 | const watchWindows = require('../../util/windows').watchWindows;
19 | const DOMGenerator = require('../../util/dom');
20 |
21 | const PanelTrait = function (options) {
22 | if (!options) return;
23 |
24 | this.options = options;
25 |
26 | this.handlers = {
27 | show: options.onShow,
28 | hide: options.onHide
29 | };
30 |
31 | this.onShow = function (event, nodes) {
32 | if (this.handlers.show)
33 | this.handlers.show(event, nodes);
34 | }.bind(this);
35 | this.onHide = function (event, nodes) {
36 | if (this.handlers.hide)
37 | this.handlers.hide(event, nodes);
38 | }.bind(this);
39 |
40 | this.views = new Set();
41 | };
42 |
43 | function createView (trait, document) {
44 | function onShow (event) {
45 | trait.onShow(event, nodes);
46 | }
47 | function onHide (event) {
48 | trait.onHide(event, nodes);
49 | }
50 |
51 | var nodes = {};
52 | var view = DOMGenerator.fromJSON(trait.options.content, document, nodes);
53 | trait.views.add(view);
54 |
55 | view.setAttribute("id", trait.options.id);
56 |
57 | document.getElementById("PanelUI-multiView").appendChild(view);
58 | unload(function () {
59 | document.getElementById("PanelUI-multiView").removeChild(view);
60 | trait.views.delete(view);
61 | }, document);
62 |
63 | view.addEventListener('ViewShowing', onShow, false);
64 | unload(function () { view.removeEventListener('ViewShowing', onShow, false); }, view);
65 |
66 | view.addEventListener('ViewHiding', onHide, false);
67 | unload(function () { view.removeEventListener('ViewHiding', onHide, false); }, view);
68 | }
69 |
70 | exports.Panel = function (options) {
71 | // check validity of properties
72 | if (!options.id) {
73 | throw new Error("id option is required");
74 | }
75 | if (!options.content) {
76 | throw new Error("content option is required");
77 | }
78 |
79 | let pt = new PanelTrait(options);
80 |
81 | function watcher (window) {
82 | createView(pt, window.document);
83 | }
84 | watchWindows(watcher);
85 |
86 | return {
87 | id: options.id,
88 |
89 | on: function (event, action) {
90 | if (event === 'show') {
91 | pt.handlers.show = action;
92 | } else if (event === 'hide') {
93 | pt.handlers.hide = action;
94 | }
95 | },
96 |
97 | destroy: function () {
98 | pt.views.forEach(function (view) {
99 | let document = view.ownerDocument;
100 | document.getElementById("PanelUI-multiView").removeChild(view);
101 | });
102 | pt.views.clear();
103 | }
104 | };
105 | };
106 |
107 |
--------------------------------------------------------------------------------
/legacy/lib/ui/australis/widget.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // australis/widget.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage Australis UI elements
12 | //
13 |
14 | "use strict";
15 |
16 | const { Cu, Cc, Ci } = require('chrome');
17 | const { CustomizableUI } = Cu.import("resource:///modules/CustomizableUI.jsm");
18 |
19 | const windows = require("sdk/windows");
20 | const window_utils = require('sdk/window/utils');
21 | const stylesheets = require('sdk/stylesheet/utils');
22 |
23 | const unload = require('../../util/unload').unload;
24 | const watchWindows = require('../../util/windows').watchWindows;
25 |
26 | var { versionCompare } = require('../../util/system');
27 |
28 | var windowManager = (function () {
29 | const view = require('sdk/view/core');
30 | const model = require('sdk/model/core');
31 |
32 | return {
33 | viewFor: function (window) {
34 | return view.viewFor(window);
35 | },
36 | modelFor: function (window) {
37 | return model.modelFor(window);
38 | }
39 | };
40 | })();
41 |
42 |
43 | const WidgetTrait = function (options) {
44 | if (!options) return;
45 |
46 | function loadStyleSheet (window) {
47 | stylesheets.loadSheet(window, options.stylesheet, 'user');
48 |
49 | unload((function () { stylesheets.removeSheet(window, options.stylesheet, 'user'); }).bind(this),
50 | window);
51 | }
52 |
53 | this.widget = null;
54 | this.widgetNodes = new Map();
55 |
56 | this.handlers = {
57 | mouseover: options.onMouseover,
58 | click: options.onClick,
59 | middleclick: options.onMiddleclick,
60 | rightclick: options.onRightclick
61 | };
62 |
63 | this.onMouseover = function (event) {
64 | if (this.handlers.mouseover)
65 | this.handlers.mouseover(windowManager.modelFor(window_utils.getOwnerBrowserWindow(event.target)), event);
66 | }.bind(this);
67 |
68 | this.woptions = {
69 | onCreated: (function (node) {
70 | if (options.icon) {
71 | node.classList.add(options.icon);
72 | this.widgetNodes.set(node, {icon: options.icon});
73 | }
74 | else
75 | this.widgetNodes.set(node, {icon: ''});
76 |
77 | node.addEventListener('mouseover', this.onMouseover);
78 | unload((function () {node.removeEventListener('mouseover', this.onMouseover);}).bind(this), node);
79 | }).bind(this),
80 |
81 | onClick: (function (event) {
82 | if (this.handlers.click) {
83 | this.handlers.click(windowManager.modelFor(window_utils.getOwnerBrowserWindow(event.target)), event);
84 | event.stopPropagation();
85 | event.preventDefault();
86 |
87 | return;
88 | }
89 |
90 | // middle-click
91 | if (event.button == 1 || (event.button == 0 && event.altKey == true)) {
92 | if (this.handlers.middleclick) {
93 | this.handlers.middleclick(windowManager.modelFor(window_utils.getOwnerBrowserWindow(event.target)), event);
94 | event.stopPropagation();
95 | event.preventDefault();
96 | }
97 | }
98 | // right-click
99 | if (event.button == 2 || (event.button == 0 && event.shiftKey == true)) {
100 | if (this.handlers.rightclick) {
101 | this.handlers.rightclick(windowManager.modelFor(window_utils.getOwnerBrowserWindow(event.target)), event);
102 | event.stopPropagation();
103 | event.preventDefault();
104 | }
105 | }
106 | }).bind(this)
107 | };
108 |
109 | if (options.panel) {
110 | this.woptions.viewId = options.panel.id;
111 | }
112 | if (options.tooltip) {
113 | this.woptions.tooltiptext = options.tooltip;
114 | }
115 | if (options.stylesheet) {
116 | watchWindows(loadStyleSheet);
117 | }
118 | this.woptions.defaultArea = options.defaultArea ? options.defaultArea : CustomizableUI.AREA_NAVBAR;
119 | // populate woptions from options for other options
120 | var validOptions = ['id', 'type', 'label', 'removable', 'overflows', 'shortcutId', 'showInPrivateBrowsing'];
121 | validOptions.forEach((function (option) {
122 | if (options[option])
123 | this.woptions[option] = options[option];
124 | }).bind(this));
125 |
126 | // connect some listeners
127 | let listener = {
128 | onWidgetInstanceRemoved: (function (id, document) {
129 | if (id !== options.id) {
130 | return;
131 | }
132 |
133 | this.widgetNodes.delete(document.getElementById(id));
134 | }).bind(this)
135 | };
136 | CustomizableUI.addListener(listener);
137 |
138 | this.destroyWidget = function () {
139 | this.widgetNodes.clear();
140 | CustomizableUI.removeListener(listener);
141 | CustomizableUI.destroyWidget(this.woptions.id);
142 | };
143 | unload((function () { this.destroyWidget(); }).bind(this));
144 |
145 | this.widget = CustomizableUI.createWidget(this.woptions);
146 | };
147 |
148 | exports.Widget = function (options) {
149 | // check validity of properties
150 | if (options.type === 'view') {
151 | if (!options.panel) {
152 | throw new Error("panel option required for view type");
153 | }
154 | } else {
155 | if (options.panel) {
156 | throw new Error("panel option only valid for view type");
157 | }
158 | }
159 |
160 | let wt = new WidgetTrait(options);
161 |
162 | return {
163 | on: function (event, action) {
164 | if (event === 'mouseover') {
165 | wt.handlers.mouseover = action;
166 | } else if (event === 'click') {
167 | wt.handlers.click = action;
168 | } else if (event === 'middleclick') {
169 | wt.handlers.middleclick = action;
170 | } else if (event === 'rightclick') {
171 | wt.handlers.rightclick = action;
172 | }
173 | },
174 |
175 | getView: function (window) {
176 | let win = windowManager.viewFor(window);
177 | let instance = null;
178 | try {
179 | instance = win ? wt.widget.forWindow(win) : null;
180 | } catch (e) {
181 | // can be called too early: no document attached to window
182 | }
183 | if (!instance) return null;
184 |
185 | return {
186 | set icon (value) {
187 | if (instance) {
188 | let config = wt.widgetNodes.get(instance.node);
189 | if (value !== config.icon) {
190 | instance.node.classList.add(value);
191 | instance.node.classList.remove(config.icon);
192 |
193 | config.icon = value;
194 | }
195 | }
196 | },
197 |
198 | set tooltip (value) {
199 | if (instance) {
200 | instance.node.tooltipText = value;
201 | }
202 | }
203 | };
204 | },
205 |
206 | set icon (value) {
207 | wt.widget.instances.forEach(function (instance) {
208 | let config = wt.widgetNodes.get(instance.node);
209 |
210 | if (value !== config.icon) {
211 | instance.node.classList.add(value);
212 | instance.node.classList.remove(config.icon);
213 |
214 | config.icon = value;
215 | }
216 | });
217 | },
218 | set tooltip (value) {
219 | wt.widget.instances.forEach(function (instance) {
220 | instance.node.tooltipText = value;
221 | });
222 | },
223 |
224 | destroy: function () {
225 | wt.destroyWidget();
226 | }
227 | };
228 | };
229 |
230 | // exports some useful constonts from CustomizableUI
231 | exports.AREA_PANEL = CustomizableUI.AREA_PANEL;
232 | exports.AREA_NAVBAR = CustomizableUI.AREA_NAVBAR;
233 | exports.AREA_MENUBAR = CustomizableUI.AREA_MENUBAR;
234 | exports.AREA_TABSTRIP = CustomizableUI.AREA_TABSTRIP;
235 | exports.AREA_BOOKMARKS = CustomizableUI.AREA_BOOKMARKS;
236 |
--------------------------------------------------------------------------------
/legacy/lib/ui/file-picker.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // file-picker.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage file picker UI
12 | //
13 |
14 | "use strict";
15 |
16 | const {Cu, Cc, Ci} = require('chrome');
17 | const {Services} = Cu.import('resource://gre/modules/Services.jsm');
18 | const {FileUtils} = Cu.import('resource://gre/modules/FileUtils.jsm');
19 | const nsIFilePicker = Ci.nsIFilePicker;
20 |
21 |
22 | exports.show = function (window, mode, callback, properties) {
23 | var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
24 |
25 | function fpCallback (result) {
26 | if (result != nsIFilePicker.returnCancel)
27 | callback(fp.file.path);
28 | }
29 |
30 | var fpMode = mode == "open" ? nsIFilePicker.modeOpen : nsIFilePicker.modeSave;
31 |
32 | if (properties.directory) {
33 | if (properties.directory == "") {
34 | fp.displayDirectory = Services.dirsvc.get("Home", Ci.nsIFile);
35 | } else {
36 | fp.displayDirectory = new FileUtils.File(properties.directory);
37 | }
38 | }
39 |
40 | if (mode == "save" && properties.filename)
41 | fp.defaultString = properties.filename;
42 |
43 | if (properties.extension) {
44 | fp.defaultExtension = properties.extension;
45 | fp.appendFilter(properties.extension+" Files", "*."+properties.extension);
46 | }
47 | fp.appendFilters(fp.filterText+fp.filterAll);
48 |
49 | fp.init(window, properties.title, fpMode);
50 |
51 | fp.open(fpCallback);
52 | };
53 |
--------------------------------------------------------------------------------
/legacy/lib/ui/menu.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // menu.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage High Level UI elements
12 | //
13 |
14 | "use strict";
15 |
16 | var menus = require('./australis/menu');
17 |
18 |
19 | exports.Menu = function (options) {
20 | return menus.Menu(options);
21 | };
22 |
23 |
--------------------------------------------------------------------------------
/legacy/lib/ui/panel.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // panel.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage High Level UI elements
12 | //
13 |
14 | "use strict";
15 |
16 | var panels = require('./australis/panel');
17 |
18 | exports.Panel = function (options) {
19 | return panels.Panel(options);
20 | };
21 |
22 |
--------------------------------------------------------------------------------
/legacy/lib/ui/popup.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // popup.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage Chrome UI elements
12 | //
13 |
14 | "use strict";
15 |
16 | const {Cu} = require('chrome');
17 | const {Services} = Cu.import('resource://gre/modules/Services.jsm');
18 |
19 |
20 | exports.display = function (id, xul, parameters) {
21 | let settingsWindow = Services.wm.getMostRecentWindow(id);
22 | if (settingsWindow) {
23 | settingsWindow.focus();
24 | } else {
25 | parameters.wrappedJSObject = parameters;
26 |
27 | Services.ww.openWindow(null, xul, "_blank", "chrome,centerscreen,dialog=yes,modal=yes,titlebar=yes", parameters);
28 | }
29 | };
30 |
31 | /**
32 | * Shows an alert message like window.alert() but with a custom title.
33 | *
34 | * @param {Window} parentWindow parent window of the dialog (can be null)
35 | * @param {String} title dialog title
36 | * @param {String} message message to be displayed
37 | */
38 | exports.alert = function (parentWindow, title, message) {
39 | if (!title)
40 | title = "Linkificator";
41 | Services.prompt.alert(parentWindow, title, message);
42 | };
43 |
44 | /**
45 | * Shows a dialog message with OK and Cancel buttons.
46 | *
47 | * @param {Window} parentWindow parent window of the dialog (can be null)
48 | * @param {String} title dialog title
49 | * @param {String} message message to be displayed
50 | */
51 | exports.confirm = function (parentWindow, title, message) {
52 | if (!title)
53 | title = "Linkificator";
54 | return Services.prompt.confirm(parentWindow, title, message);
55 | };
56 |
--------------------------------------------------------------------------------
/legacy/lib/ui/widget.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // widget.js - Linkificator's module
7 | // author: MarkaPola
8 |
9 |
10 | //
11 | // Manage High Level UI elements
12 | //
13 |
14 | "use strict";
15 |
16 | var widgets = require('./australis/widget');
17 |
18 | exports.Widget = function (options) {
19 | // check validity of properties
20 | if (options.panel && options.menu) {
21 | throw new Error("panel and menu options are mutually exclusive");
22 | }
23 |
24 | // handle options
25 | if (options.menu) {
26 | options.panel = options.menu.panel;
27 | }
28 |
29 | return widgets.Widget(options);
30 | };
31 |
32 |
--------------------------------------------------------------------------------
/legacy/lib/util/dom.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // dom.js - Linkificator's module
7 | // author: MarkaPola
8 | //
9 |
10 | //
11 | // DOM tools
12 | //
13 |
14 | const NAMESPACES = {
15 | html: "http://www.w3.org/1999/xhtml",
16 | xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
17 | };
18 |
19 | var defaultNamespace = NAMESPACES.xul;
20 |
21 | exports.NAMESPACES = Object.freeze(NAMESPACES);
22 | exports.defaultNamespace = exports.NAMESPACES.xul;
23 |
24 | /*
25 | * Generates a Document fragment from a JSON structure
26 | *
27 | * @param [object] json: JSON structure describing DOM to generate
28 | * @param [object] document: DOM object used to generate new elements
29 | * @output [array] nodes: list of nodes with attribute 'key' in JSON description
30 | */
31 | function fromJSON (json, document, nodes) {
32 | const unload = require('./unload').unload;
33 |
34 | function namespace(name) {
35 | var m = /^(?:(.*):)?(.*)$/.exec(name);
36 | return [NAMESPACES[m[1]], m[2]];
37 | }
38 |
39 | function tag(name, attr) {
40 | if (Array.isArray(name)) {
41 | var frag = document.createDocumentFragment();
42 | Array.forEach(arguments, function (arg) {
43 | if (!Array.isArray(arg[0]))
44 | frag.appendChild(tag.apply(null, arg));
45 | else
46 | arg.forEach(function (arg) {
47 | frag.appendChild(tag.apply(null, arg));
48 | });
49 | });
50 | return frag;
51 | }
52 |
53 | var args = Array.slice(arguments, 2);
54 | var vals = namespace(name);
55 | var elem = document.createElementNS(vals[0] || defaultNamespace,
56 | vals[1]);
57 |
58 | for (var key in attr) {
59 | var val = attr[key];
60 | if (nodes && key == "key")
61 | nodes[val] = elem;
62 |
63 | vals = namespace(key);
64 | if (typeof val == "function") {
65 | elem.addEventListener(key, val, false);
66 | unload (function () { elem.removeEventlistener(key, val, false); }, elem);
67 | } else
68 | elem.setAttributeNS(vals[0] || "", vals[1], val);
69 | }
70 | args.forEach(function(e) {
71 | elem.appendChild(typeof e == "object" ? tag.apply(null, e) :
72 | e instanceof Node ? e : document.createTextNode(e));
73 | });
74 | return elem;
75 | }
76 | return tag.apply(null, json);
77 | }
78 |
79 | exports.fromJSON = fromJSON;
80 |
--------------------------------------------------------------------------------
/legacy/lib/util/system.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | // system.js - Linkificator's module
6 | // author: MarkaPola
7 | //
8 |
9 | //
10 | // Manage configuration
11 | //
12 |
13 | var australis = true;
14 |
15 | try {
16 | require('chrome').Cu.import('resource:///modules/CustomizableUI.jsm', {});
17 | }
18 | catch (e) {
19 | australis = false;
20 | }
21 |
22 | Object.defineProperty(exports, "australis", {
23 | enumerable: true,
24 | get: (function () {
25 | return australis;
26 | }).bind(this)
27 | });
28 |
29 |
30 | function versionCompare (v1, v2) {
31 | const {Cu, Ci} = require('chrome');
32 | const {Services} = Cu.import('resource://gre/modules/Services.jsm');
33 |
34 | return Services.vc.compare(v1, v2);
35 | }
36 |
37 | exports.versionCompare = versionCompare;
38 |
--------------------------------------------------------------------------------
/legacy/lib/util/unload.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | // unload.js - Linkificator's module
6 | // author: MarkaPola
7 | //
8 | // This code is derived from work done by Mardak (see https://github.com/Mardak/restartless/blob/watchWindows/bootstrap.js)
9 | //
10 |
11 | //
12 | // Manage actions on unload
13 | //
14 |
15 | /**
16 | * Save callbacks to run when unloading. Optionally scope the callback to a
17 | * container, e.g., window. Unload will be done automatically on add-on unload.
18 | * Provide a way to run all the callbacks.
19 | *
20 | * @usage unload(): Run all callbacks and release them.
21 | *
22 | * @usage unload(callback): Add a callback to run on unload.
23 | * @param [function] callback: 0-parameter function to call on unload.
24 | * @return [function]: A 0-parameter function that undoes adding the callback.
25 | *
26 | * @usage unload(callback, container) Add a scoped callback to run on unload.
27 | * @param [function] callback: 0-parameter function to call on unload.
28 | * @param [node] container: Remove the callback when this container unloads.
29 | * @return [function]: A 0-parameter function that undoes adding the callback.
30 | */
31 | function unload (callback, container) {
32 | // Initialize the array of unloaders and global unload on the first usage
33 | let unloaders = unload.unloaders;
34 | if (unloaders == null) {
35 | unloaders = unload.unloaders = [];
36 |
37 | // execute unload on add-on unload
38 | require('sdk/system/unload').when(function(reason) { unload(); });
39 | }
40 |
41 | // Calling with no arguments runs all the unloader callbacks
42 | if (callback == null) {
43 | unloaders.slice().forEach(function(unloader) { unloader(); });
44 | unloaders.length = 0;
45 | return undefined;
46 | }
47 |
48 | // The callback is bound to the lifetime of the container if we have one
49 | if (container != null) {
50 | // Remove the unloader when the container unloads
51 | container.addEventListener("unload", removeUnloader, false);
52 |
53 | // Wrap the callback to additionally remove the unload listener
54 | let origCallback = callback;
55 | callback = function() {
56 | container.removeEventListener("unload", removeUnloader, false);
57 | origCallback();
58 | };
59 | }
60 |
61 | // Wrap the callback in a function that ignores failures
62 | function unloader() {
63 | try {
64 | callback();
65 | } catch(ex) {}
66 | }
67 | unloaders.push(unloader);
68 |
69 | // Provide a way to remove the unloader
70 | function removeUnloader() {
71 | let index = unloaders.indexOf(unloader);
72 | if (index != -1)
73 | unloaders.splice(index, 1);
74 | }
75 | return removeUnloader;
76 | }
77 |
78 | exports.unload = unload;
79 |
--------------------------------------------------------------------------------
/legacy/lib/util/windows.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // windows.js - Linkificator's module
7 | // author: MarkaPola
8 | //
9 | // This code is derived from work done by Mardak (see https://github.com/Mardak/restartless/blob/watchWindows/bootstrap.js)
10 | //
11 |
12 |
13 | //
14 | // Manage windows objects creation
15 | //
16 |
17 | /**
18 | * Apply a callback to each open and new browser windows.
19 | *
20 | * @usage watchWindows(callback): Apply a callback to each browser window.
21 | * @param [function] callback: 1-parameter function that gets a browser window.
22 | */
23 | function watchWindows (callback) {
24 | const {Cu, Ci} = require('chrome');
25 | const {Services} = Cu.import('resource://gre/modules/Services.jsm');
26 |
27 | const unload = require('./unload').unload;
28 |
29 | // Wrap the callback in a function that ignores failures
30 | function watcher (window) {
31 | try {
32 | // Now that the window has loaded, only handle browser windows
33 | let {documentElement} = window.document;
34 | if (documentElement.getAttribute("windowtype") == "navigator:browser")
35 | callback(window);
36 | } catch(ex) {}
37 | }
38 |
39 | // Wait for the window to finish loading before running the callback
40 | function runOnLoad(window) {
41 | // Listen for one load event before checking the window type
42 | window.addEventListener("DOMContentLoaded", function runOnce () {
43 | window.removeEventListener("DOMContentLoaded", runOnce, false);
44 | watcher(window);
45 | }, false);
46 | }
47 |
48 | // Add functionality to existing windows
49 | let windows = Services.wm.getEnumerator("navigator:browser");
50 | while (windows.hasMoreElements()) {
51 | // Only run the watcher immediately if the window is completely loaded
52 | let window = windows.getNext();
53 | if (window.document.readyState == "complete")
54 | watcher(window);
55 | // Wait for the window to load before continuing
56 | else
57 | runOnLoad(window);
58 | }
59 |
60 | let windowListener = {
61 | onOpenWindow: function (xulWindow) {
62 | let window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
63 | runOnLoad(window);
64 | },
65 |
66 | onCloseWindow: function (xulWindow) {},
67 | onWindowTitleChange: function (xulWindow) {}
68 | };
69 |
70 | // Watch for new browser windows opening then wait for it to load
71 | Services.wm.addListener(windowListener);
72 |
73 | // Make sure to stop watching for windows if we're unloading
74 | unload(function() { Services.wm.removeListener(windowListener); });
75 | }
76 |
77 | exports.watchWindows = watchWindows;
78 |
--------------------------------------------------------------------------------
/legacy/locale/en-US.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # en-US translation - Linkificator's module
8 | # author: MarkaPola
9 |
10 | # statistics
11 | stats.links[zero]=0 links processed
12 | stats.links[one]=1 link processsed
13 | stats.links=%d links processed
14 | stats.time=in %d ms
15 | stats.excluded=page excluded
16 | stats.filtered=page ignored
17 | stats.not_processed=page not processed
18 | stats.undetermined=undetermined
19 |
20 | # widget panel
21 | panel.options=Options...
22 | panel.manual=On demand
23 | panel.enable=Enable
24 | panel.disable=Disable
25 | panel.include=Include
26 | panel.exclude=Exclude
27 | panel.linkify=Update
28 |
29 | # context menu
30 | menu.linkify=Update links
31 |
32 | # alert message
33 | alert.title=Linkificator degraded mode
34 | alert.message=Due to some Firefox SDK limitations, Linkificator context menu is not supported on this version. It is strongly recommanded to upgrade Firefox to version 21 or above.
35 |
36 | # settings management
37 | reset.title=Linkificator - Restore Default Settings
38 | reset.message=You are about to reset settings to default values.\u000aAre you sure you want to continue?
39 |
40 | export.title=Linkificator - Export Settings
41 | import.title=Linkificator - Import Settings
42 |
--------------------------------------------------------------------------------
/legacy/locale/fr.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # fr translation - Linkificator's module
8 | # author: MarkaPola
9 |
10 | # statistics
11 | stats.links[zero]=0 liens trait\u00e9s
12 | stats.links[one]=1 lien trait\u00e9
13 | stats.links=%d liens trait\u00e9s
14 | stats.time=en %d ms
15 | stats.excluded=page exclue
16 | stats.filtered=page ignor\u00e9e
17 | stats.not_processed=page non trait\u00e9e
18 | stats.undetermined=ind\u00e9termin\u00e9
19 |
20 | # widget panel
21 | panel.options=Options...
22 | panel.manual=Sur demande
23 | panel.enable=Activer
24 | panel.disable=D\u00e9sactiver
25 | panel.include=Inclure
26 | panel.exclude=Exclure
27 | panel.linkify=Actualiser
28 |
29 | # context menu
30 | menu.linkify=Actualiser les liens
31 |
32 | # alert message
33 | alert.title=Linkificator en mode d\u00e9grad\u00e9
34 | alert.message=Du fait de certaines limitations du SDK de Firefox, le menu contextuel de Linkificator n'est pas support\u00e9 sur cette version. Il est fortement recommand\u00e9 de mettre \u00e0 jour Firefox vers la version 21 ou suivantes.
35 |
36 | # settings management
37 | reset.title=Linkificator - R\u00e9initialisation des r\u00e9glages
38 | reset.message=Vous \u00eates sur le point de r\u00e9initialiser les r\u00e9glages par d\u00e9faut.\u000aEtes-vous s\u00fbr de vouloir continuer?
39 |
40 | export.title=Linkificator - Exporter les r\u00e9glages
41 | import.title=Linkificator - Importer les r\u00e9glages
42 |
--------------------------------------------------------------------------------
/legacy/locale/gl.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # gl translation - Linkificator's module
8 | # author:
9 |
10 | # statistics
11 | stats.links[zero]=0 ligaz\u00f3ns procesadas
12 | stats.links[one]=1 ligaz\u00f3n procesada
13 | stats.links=%d ligaz\u00f3ns procesadas
14 | stats.time=en %d m
15 | stats.excluded=p\u00e1xina exclu\u00edda
16 | stats.filtered=p\u00e1xina ignorada
17 | stats.not_processed=p\u00e1xina non procesada
18 | stats.undetermined=sen determinar
19 |
20 | # widget panel
21 | panel.options=Opci\u00f3ns...
22 | panel.manual=On Demand
23 | panel.enable=Activar
24 | panel.disable=Desactivar
25 | panel.include=Inclu\u00edr
26 | panel.exclude=Exclu\u00edr
27 | panel.linkify=Actualizar
28 |
29 | # context menu
30 | menu.linkify=Actualizar ligaz\u00f3ns
31 |
32 | # alert message
33 | alert.title=Linkificator degraded mode
34 | alert.message=Due to some Firefox SDK limitations, Linkificator context menu is not supported on this version. It is strongly recommanded to upgrade Firefox to version 21 or above.
35 |
36 | # settings management
37 | reset.title=Linkificator - Restore Default Settings
38 | reset.message=You are about to reset settings to default values.\u000aAre you sure you want to continue?
39 |
40 | export.title=Linkificator - Export Settings
41 | import.title=Linkificator - Import Settings
42 |
--------------------------------------------------------------------------------
/legacy/locale/ru.properties:
--------------------------------------------------------------------------------
1 | #X-Generator: crowdin.com
2 |
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 |
7 | # ru translation - Linkificator's module
8 | # author: Капелька Яда
9 |
10 | # statistics
11 | stats.links[zero]=0 \u0441\u0441\u044b\u043b\u043e\u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e
12 | stats.links[one]=1 \u0441\u0441\u044b\u043b\u043a\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430
13 | stats.links=%d \u0441\u0441\u044b\u043b\u043e\u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e
14 | stats.time=\u0437\u0430 %d \u043c\u0441\u0435\u043a
15 | stats.excluded=\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0430
16 | stats.filtered=\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430
17 | stats.not_processed=\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430
18 | stats.undetermined=\u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e
19 |
20 | # widget panel
21 | panel.options=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438...
22 | panel.manual=\u041f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443
23 | panel.enable=\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c
24 | panel.disable=\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c
25 | panel.include=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c
26 | panel.exclude=\u0418\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c
27 | panel.linkify=\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c
28 |
29 | # context menu
30 | menu.linkify=\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0438
31 |
32 | # alert message
33 | alert.title=\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c Linkificator
34 | alert.message=\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 Firefox SDK, \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e Linkificator \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438. \u041d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c Firefox \u0434\u043e \u0432\u0435\u0440\u0441\u0438\u0438 21 \u0438\u043b\u0438 \u0432\u044b\u0448\u0435.
35 |
36 | # settings management
37 | reset.title=Linkificator - \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e
38 | reset.message=\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e?
39 |
40 | export.title=Linkificator - \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a
41 | import.title=Linkificator - \u0418\u043c\u043f\u043e\u0440\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a
42 |
--------------------------------------------------------------------------------
/legacy/options.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | &settings.separator-domains;
62 | &settings.separator-domains;
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/legacy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Linkificator",
3 | "name": "linkificator",
4 | "version": "2.3.2",
5 | "license": "MPL 2.0",
6 | "author": "MarkaPola",
7 | "id": "linkificator@markapola",
8 | "description": "Converts text links into clickable links...",
9 | "contributors": ["Icons from www.fasticon.com"],
10 | "keywords": [
11 | "jetpack"
12 | ],
13 | "engines": {
14 | "firefox": ">=38.0a1"
15 | },
16 | "permissions": {
17 | "private-browsing": true,
18 | "multiprocess": true
19 | },
20 | "main": "lib/main.js"
21 | }
22 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // The main module of the Linkificator Add-on.
7 | // author: MarkaPola
8 |
9 | const webext = require("sdk/webextension");
10 |
11 | function sendPreferences (port) {
12 | function splitProtocols (data) {
13 | function Element (data) {
14 | let parts1 = data.split('~');
15 | if (parts1.length != 2) {
16 | return null;
17 | }
18 |
19 | let parts2 = parts1[1].split('#');
20 | let count = 2;
21 |
22 | if (parts2.length == 2) {
23 | count = parseInt(parts2[1], 10);
24 | }
25 |
26 | let trailer = ":";
27 | for (let index = 0; index < count; ++index) {
28 | trailer += "/";
29 | }
30 |
31 | return {
32 | pattern: parts1[0]+trailer,
33 | term: parts2[0]+trailer
34 | };
35 | }
36 |
37 | let result = [];
38 | for (let element of data.trim().split(';')) {
39 | let protocol = Element(element);
40 | if (protocol)
41 | result.push (protocol);
42 | }
43 |
44 | return result;
45 | }
46 | function splitSubdomains (data) {
47 | function Element (data) {
48 | let parts = data.split('~');
49 | if (parts.length == 3) {
50 | return {
51 | filter: parts[0],
52 | pattern: parts[1],
53 | term: parts[2]
54 | };
55 | }
56 | else {
57 | return null;
58 | }
59 | }
60 |
61 | let result = [];
62 | for (let element of data.trim().split(';')) {
63 | let subdomain = Element(element);
64 | if (subdomain)
65 | result.push (subdomain);
66 | }
67 |
68 | return result;
69 | }
70 | function splitExcludedElements (data) {
71 | let elements = data.trim().split(';');
72 |
73 | for (let index = 0; index < elements.length; ++index) {
74 | let element = elements[index];
75 | if (element.charAt(0) == '@')
76 | elements[index] = '*[' + element + ']';
77 | }
78 |
79 | return elements;
80 | }
81 | function splitTopLevelDomains (data) {
82 | return data.trim().split(';').sort().filter((value, index, self) => self.indexOf(value) === index);
83 | }
84 |
85 |
86 | const prefs = require('sdk/simple-prefs').prefs;
87 |
88 | let settings = {};
89 |
90 | // convert legacy preferences to webextension format
91 | settings.manual = prefs.manual;
92 | settings.contextMenuIntegration = prefs.contextMenuIntegration;
93 |
94 | settings.hotkeys = {
95 | toggle: prefs.hotkeyToggle,
96 | manual: prefs.hotkeyManual,
97 | manage: prefs.hotkeyManage,
98 | parse: prefs.hotkeyParse
99 | };
100 |
101 | settings.domains = {
102 | useRegExp: prefs.useRegExp,
103 | type: prefs.filterMode,
104 | list: {
105 | white: prefs.whitelist.trim().split(/\s+/),
106 | black: prefs.blacklist.trim().split(/\s+/).filter(item => item !== '^about:')
107 | }
108 | };
109 |
110 | settings.style = {
111 | text: {
112 | override: prefs.overrideTextColor,
113 | color: prefs.linkColor
114 | },
115 | background: {
116 | override: prefs.overrideBackgroundColor,
117 | color: prefs.backgroundColor
118 | }
119 | };
120 |
121 | settings.predefinedRules = {
122 | support: {
123 | email: {
124 | active: prefs.supportEmail,
125 | useTLD: prefs.emailUseTLD
126 | },
127 | standard: {
128 | active: prefs.supportStandardURLs,
129 | useSubdomains: prefs.standardURLUseSubdomains,
130 | useTLD: prefs.standardURLUseTLD,
131 | linkifyAuthority: prefs.standardURLlinkifyAuthority
132 | }
133 | },
134 | protocols: splitProtocols(prefs.protocols),
135 | subdomains: splitSubdomains(prefs.subdomains),
136 | excludedElements: splitExcludedElements(prefs.excludedElements)
137 | };
138 |
139 | settings.tldGenerics = {
140 | active: prefs.useGTLDs,
141 | domains: splitTopLevelDomains(prefs.gTLDs)
142 | };
143 | settings.tldCountryCodes = {
144 | active: prefs.useCcTLDs,
145 | domains: splitTopLevelDomains(prefs.ccTLDs)
146 | };
147 | settings.tldGgeographics = {
148 | active: prefs.useGeoTLDs,
149 | domains: splitTopLevelDomains(prefs.geoTLDs)
150 | };
151 | settings.tldCommunities = {
152 | active: prefs.useCommunityTLDs,
153 | domains: splitTopLevelDomains(prefs.communityTLDs)
154 | };
155 | settings.tldBrands = {
156 | active: prefs.useBrandTLDs,
157 | domains: splitTopLevelDomains(prefs.brandTLDs)
158 | };
159 |
160 | settings.customRules = {
161 | support: {
162 | before: prefs.supportCustomRulesBefore,
163 | after: prefs.supportCustomRulesAfter
164 | },
165 | rules: JSON.parse(prefs.customRules)
166 | };
167 |
168 | settings.extraFeatures = {
169 | support: {
170 | inlineElements: prefs.supportInlineElements,
171 | autoLinkification: prefs.automaticLinkification
172 | },
173 | inlineElements: prefs.inlineElements.trim().split(";"),
174 | maxDataSize: prefs.maxDataSize,
175 | autoLinkification: {
176 | delay: prefs.autoLinkificationDelay,
177 | interval: {
178 | active: prefs.autoLinkificationInterval,
179 | value: prefs.autoLinkificationIntervalValue
180 | },
181 | threshold: {
182 | active: prefs.autoLinkificationThreshold,
183 | value: prefs.autoLinkificationThresholdValue
184 | }
185 | }
186 | };
187 |
188 | try {
189 | settings.processing = JSON.parse(prefs.processing);
190 | } catch (e) {
191 | // erroneous JSON, use default
192 | settings.processing = {interval: 10, iterations: 40};
193 | }
194 |
195 | port.postMessage({
196 | id: 'set-preferences',
197 | preferences: {
198 | config: {
199 | sync: prefs.sync,
200 | activated: prefs.activated
201 | },
202 | settings: settings
203 | }
204 | });
205 | }
206 |
207 |
208 | exports.main = function (options) {
209 | webext.startup().then(({browser}) => {
210 | browser.runtime.onConnect.addListener(port => {
211 | if (port.name === "legacy-channel") {
212 | // for new installation or upgrade,
213 | // show an informational page
214 | // forward current settings to new extension
215 | if (options.loadReason == 'install' || options.loadReason == 'upgrade') {
216 | port.postMessage({id: 'show-release-notes'});
217 |
218 | sendPreferences(port);
219 | }
220 | }
221 | });
222 | });
223 | };
224 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "title": "Linkificator",
4 | "name": "linkificator",
5 | "icon": "icon48.png",
6 | "version": "3.0.1",
7 | "license": "MPL 2.0",
8 | "author": "MarkaPola",
9 | "id": "linkificator@markapola",
10 | "description": "Converts text links into clickable links...",
11 | "contributors": ["Icons from www.fasticon.com (http://www.fasticon.com)",
12 | "Localization infrastructure: Crowdin.com (http://crowdin.com)"],
13 | "translators": ["MarkaPola", "Капелька Яда"],
14 | "engines": {
15 | "firefox": ">=55.0"
16 | },
17 | "permissions": {
18 | "private-browsing": true,
19 | "multiprocess": true
20 | },
21 | "hasEmbeddedWebExtension": true,
22 | "locales": {
23 | "en-US": {
24 | "description": "Converts text links into clickable links..."
25 | },
26 | "fr": {
27 | "description": "Transforme les liens textuels en liens hypertexte cliquables..."
28 | },
29 | "ru": {
30 | "description": "Преобразует текстовые ссылки в кликабельные."
31 | }
32 | },
33 | "main": "./main.js"
34 | }
35 |
--------------------------------------------------------------------------------
/webextension/content_scripts/statistics.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* Statistics management - Linkificator's module
7 | * author: MarkaPola */
8 |
9 |
10 | function Statistics (document, action) {
11 | "use strict";
12 |
13 | const countLabel = 'data-linkificator-count';
14 | const timeLabel = 'data-linkificator-time';
15 |
16 | var body = document.body;
17 |
18 | function getInt (value) {
19 | let v = parseInt(value, 10);
20 | return isNaN(v) ? 0 : v;
21 | }
22 |
23 | function getStats (count, time) {
24 | return {links: getInt(count), time: getInt(time)};
25 | }
26 |
27 | if (action == 'undo') {
28 | if (body.hasAttribute(countLabel)) {
29 | body.removeAttribute(countLabel);
30 | body.removeAttribute(timeLabel);
31 | }
32 | return {
33 | get: function () {
34 | return getStats(0, 0);
35 | }
36 | };
37 | }
38 |
39 | var elapse = 0;
40 | var startTime = Date.now();
41 |
42 | if (action === 'parse') {
43 | body.setAttribute(countLabel, 0);
44 | body.setAttribute(timeLabel, 0);
45 | } else if (action == 're-parse') {
46 | elapse = getInt(body.getAttribute(timeLabel));
47 | }
48 |
49 | return {
50 | store: function (count) {
51 | let links = getInt(body.getAttribute(countLabel));
52 |
53 | body.setAttribute(countLabel, links+count);
54 | body.setAttribute(timeLabel, elapse+(Date.now()-startTime));
55 | },
56 |
57 | get: function () {
58 | if (body.hasAttribute(countLabel)) {
59 | return getStats(body.getAttribute(countLabel),
60 | body.getAttribute(timeLabel));
61 | } else {
62 | return getStats(0, 0);
63 | }
64 | }
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/webextension/main.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // The main module of the Linkificator Add-on.
7 | // author: MarkaPola
8 |
9 |
10 | // Display release-notes on install or update
11 | function displayReleaseNotes () {
12 | function display(alarm) {
13 | if (alarm.name === 'linkificator-release-notes') {
14 | browser.alarms.clear('linkificator-release-notes');
15 | browser.alarms.onAlarm.removeListener(display);
16 | browser.tabs.create({url: "/resources/doc/release-notes.html", active: true});
17 | }
18 | }
19 |
20 | browser.alarms.onAlarm.addListener(display);
21 | browser.alarms.create('linkificator-release-notes', {when: Date.now()+2000});
22 | }
23 |
24 | browser.runtime.onInstalled.addListener(details => {
25 | if ((details.reason === 'install' || details.reason === 'update')) {
26 | displayReleaseNotes();
27 | }
28 | });
29 |
30 |
31 | Configurator().then(config => {
32 |
33 | let {configurator, properties} = config;
34 |
35 |
36 | function Statistics () {
37 | this.links = 0;
38 | this.time = 0;
39 | }
40 |
41 |
42 | class Worker {
43 | constructor (port, tab) {
44 | this._port = port;
45 | this._tab = tab;
46 | this._contentType = null;
47 | this._statistics = new Statistics();
48 | }
49 |
50 | get tab () {
51 | return this._tab;
52 | }
53 |
54 | get contentType () {
55 | return this._contentType;
56 | }
57 | set contentType (contentType) {
58 | return this._contentType = contentType;
59 | }
60 |
61 | get isValidDocument () {
62 | if (!this._contentType)
63 | return false;
64 |
65 | return this._contentType.startsWith('text/html') || this._contentType.startsWith('text/plain')
66 | || this._contentType.startsWith('application/xhtml');
67 | }
68 |
69 | sendMessage (message) {
70 | this._port.postMessage(message);
71 | }
72 |
73 | get statistics () {
74 | return this._statistics;
75 | }
76 | set statistics (stats) {
77 | this._statistics = stats;
78 | }
79 | }
80 |
81 | class Workers extends Map {
82 | constructor () {
83 | super();
84 | }
85 |
86 | *getTabs (query) {
87 | let set = new Set();
88 | if (query === undefined) query = {validOnly: false};
89 |
90 | for (const worker of this.values()) {
91 | if (!set.has(worker.tab)) {
92 | if (!query.validOnly || worker.isValidDocument) {
93 | set.add(worker.tab);
94 | yield worker.tab;
95 | }
96 | }
97 | }
98 | }
99 |
100 | *forTab (tab) {
101 | for (const worker of this.values()) {
102 | if (tab.id === worker.tab.id)
103 | yield worker;
104 | };
105 | }
106 |
107 | isValidDocument (tab) {
108 | for (const worker of this.forTab(tab)) {
109 | if (worker.isValidDocument) {
110 | return true;
111 | }
112 | }
113 |
114 | return false;
115 | }
116 | isValidTab (tab) {
117 | return controler.linkifyURL(tab) && this.isValidDocument(tab);
118 | }
119 |
120 | getStatistics (tab) {
121 | let statistics = new Statistics();
122 |
123 | for (const worker of this.forTab(tab)) {
124 | statistics.links += worker.statistics.links;
125 | statistics.time = Math.max(statistics.time, worker.statistics.time);
126 | };
127 |
128 | return statistics;
129 | }
130 | }
131 |
132 | var workers = new Workers();
133 |
134 | var controler = Controler(config);
135 |
136 |
137 | // TEMPORARY: Handle communication with legacy part of add-on
138 | let legacyChannel = browser.runtime.connect({name: 'legacy-channel'});
139 | legacyChannel.onMessage.addListener(message => {
140 | switch(message.id) {
141 | case 'show-release-notes':
142 | displayReleaseNotes();
143 | break;
144 | case 'set-preferences':
145 | configurator.updateProperties(message.preferences);
146 | break;
147 | }
148 | });
149 |
150 |
151 | // handle tabs events
152 | browser.tabs.onCreated.addListener(tab => controler.setStatus({tab: tab}));
153 |
154 | browser.tabs.onActivated.addListener(info => {
155 | browser.tabs.get(info.tabId).then(tab =>
156 | controler.contextMenu.update({enable: controler.isActive() && workers.isValidTab(tab)}));
157 | });
158 |
159 | browser.tabs.onUpdated.addListener((tabId, info, tab) => {
160 | if (info.url) {
161 | let isValid = workers.isValidTab(tab);
162 |
163 | controler.setStatus({tab: tab, isValid: isValid});
164 |
165 | // update context menu if this is one of the active tabs
166 | if (controler.isActive() && isValid) {
167 | controler.contextMenu.update({tabId: tab.id, enable: true});
168 | }
169 | }
170 | });
171 |
172 | // handle content_scripts
173 | browser.runtime.onConnect.addListener(port => {
174 | if (port.name !== 'linkificator' || port.sender.tab.id === browser.tabs.TAB_ID_NONE) {
175 | return;
176 | }
177 |
178 | workers.set(port, new Worker(port, {id: port.sender.tab.id, url: port.sender.tab.url}));
179 |
180 | port.onMessage.addListener(message => {
181 | let worker = workers.get(port);
182 | let tab = worker.tab;
183 |
184 | switch (message.id) {
185 | case 'content-type':
186 | worker.contentType = message.contentType;
187 |
188 | controler.setStatus({tab: tab, isValid: workers.isValidDocument(tab)});
189 |
190 | if (controler.isActive() && controler.linkifyURL(tab) && worker.isValidDocument) {
191 | port.postMessage ({id: 'parse'});
192 | }
193 | break;
194 | case 'configured':
195 | if (controler.isActive() && controler.isManual()) {
196 | // update context menu
197 | controler.contextMenu.update({tabId: tab.id, enable: true});
198 | }
199 | break;
200 | case 'completed':
201 | if (controler.isActive() && !controler.isManual()) {
202 | // update context menu
203 | controler.contextMenu.update({tabId: tab.id, enable: true});
204 | }
205 | break;
206 | case 'statistics':
207 | worker.statistics = message.statistics;
208 |
209 | if (controler.isActive() && controler.linkifyURL(tab)) {
210 | controler.setStatus({tab: tab,
211 | isValid: true,
212 | displayTooltip: true,
213 | displayBadge: properties.displayBadge,
214 | statistics: workers.getStatistics(tab)});
215 | }
216 | break;
217 | case 'document-changed':
218 | if (controler.isActive() && worker.isValidDocument) {
219 | port.postMessage ({id: controler.linkifyURL(tab) ? 're-parse' : 'undo'});
220 | }
221 | break;
222 | }
223 | });
224 |
225 | port.onDisconnect.addListener(port => {
226 | workers.delete(port);
227 | });
228 | });
229 |
230 |
231 | // manage communication with popup
232 | browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
233 | switch (message.id) {
234 | case 'tab-context':
235 | return browser.tabs.query({active: true, currentWindow: true}).then(tabs => {
236 | let context = {area: properties.area,
237 | activated: properties.activated,
238 | manual: properties.manual};
239 |
240 | let tab = tabs[0];
241 | context.tab = tab;
242 | context.status = controler.getStatus({tab: tab,
243 | isValid: workers.isValidDocument(tab)});
244 |
245 | return context;
246 | });
247 | break;
248 | default:
249 | return undefined;
250 | }
251 | });
252 |
253 |
254 | // attach listeners to various events
255 | controler.onBadgeChanged.addListener(info => {
256 | if (controler.isActive()) {
257 | for (const tab of workers.getTabs({validOnly: true})) {
258 | // recompute links count for every tab
259 | if (controler.linkifyURL(tab)) {
260 | controler.setStatus({tab: tab,
261 | isValid: true,
262 | displayBadge: info.displayBadge,
263 | statistics: info.displayBadge ? workers.getStatistics(tab)
264 | : null});
265 | }
266 | }
267 | }
268 | });
269 |
270 | controler.onContextMenuChanged.addListener(info => {
271 | if (info.activated) {
272 | browser.tabs.query({active: true}).then(tabs => {
273 | for (const tab of tabs) {
274 | controler.contextMenu.update({enable: workers.isValidTab(tab)});
275 | }
276 | });
277 | }
278 | });
279 |
280 | controler.onActivated.addListener(info => {
281 | // context menu configuration
282 | if (info.activated) {
283 | browser.tabs.query({active: true}).then(tabs => {
284 | for (const tab of tabs) {
285 | controler.contextMenu.update({enable: workers.isValidTab(tab)});
286 | }
287 | });
288 | }
289 |
290 | for (const tab of workers.getTabs()) {
291 | let isValid = workers.isValidTab(tab);
292 |
293 | controler.setStatus({tab: tab,
294 | isValid: isValid});
295 |
296 | if (isValid && (!controler.isManual() || !info.activated)) {
297 | for (const worker of workers.forTab(tab)) {
298 | worker.sendMessage({id: info.activated ? 'parse' : 'undo'});
299 | }
300 | }
301 | }
302 | });
303 |
304 | controler.onUpdate.addListener(info => {
305 | switch(info.action) {
306 | case 'parse':
307 | case 're-parse':
308 | case 'undo':
309 | for (const worker of workers.forTab(info.tab)) {
310 | worker.sendMessage({id: info.action});
311 | }
312 | break;
313 | }
314 | });
315 |
316 | }).catch(reason => console.error(reason));
317 |
--------------------------------------------------------------------------------
/webextension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Linkificator",
4 | "version": "3.3.3",
5 | "description": "__MSG_extensionDescription__",
6 | "icons": {
7 | "16": "resources/icons/link16-on.png",
8 | "32": "resources/icons/link32-on.png",
9 | "48": "resources/icons/icon48.png",
10 | "64": "resources/icons/icon64.png",
11 | "128": "resources/icons/icon128.png"
12 | },
13 |
14 | "developer": {
15 | "name": "MarkaPola",
16 | "url": "https://github.com/MarkaPola/Linkificator"
17 | },
18 |
19 | "default_locale": "en_US",
20 |
21 | "applications": {
22 | "gecko": {
23 | "id": "linkificator@markapola",
24 | "strict_min_version": "60.0"
25 | }
26 | },
27 |
28 | "permissions": [
29 | "tabs", "contextMenus", "history", "storage", "alarms"
30 | ],
31 |
32 | "options_ui": {
33 | "browser_style": true,
34 | "page": "options/options.html"
35 | },
36 |
37 | "browser_action": {
38 | "browser_style": true,
39 | "default_area": "navbar",
40 | "default_icon": {
41 | "16": "resources/icons/link16-on.png",
42 | "32": "resources/icons/link32-on.png"
43 | },
44 | "default_title": "Linkificator",
45 | "default_popup": "popup/popup.html"
46 | },
47 |
48 | "commands": {
49 | "Toggle": {
50 | "description": "__MSG_settings@shortcuts@toggle__ (Alt+Shift+O)"
51 | },
52 | "Manual": {
53 | "description": "__MSG_settings@shortcuts@manual__ (Alt+Shift+M)"
54 | },
55 | "Manage": {
56 | "description": "__MSG_settings@shortcuts@manage__ (Alt+Shift+X)"
57 | },
58 | "Update": {
59 | "description": "__MSG_settings@shortcuts@update__ (Alt+Shift+U)"
60 | }
61 | },
62 |
63 | "background": {
64 | "scripts" : [
65 | "options/ShortcutCustomizeUI.js",
66 | "configurator.js",
67 | "controler.js",
68 | "main.js"]
69 | },
70 | "content_scripts": [
71 | {
72 | "matches": ["*://*/*", "file://*/*"],
73 | "all_frames": true,
74 | "run_at": "document_end",
75 | "js": [
76 | "resources/js/thread.js",
77 | "resources/js/DOMutils.js",
78 | "content_scripts/statistics.js",
79 | "content_scripts/linkificator.js"]
80 | }
81 | ]
82 | }
83 |
--------------------------------------------------------------------------------
/webextension/options/advanced-options.css:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 |
6 | * author: MarkaPola
7 | */
8 |
9 | *, html {
10 | margin: 0;
11 | padding: 0;
12 | }
13 |
14 | * {box-sizing: border-box}
15 |
16 | body {
17 | font-family: "Arial";
18 | background-color: #f9f9fa;
19 | }
20 |
21 |
22 | /* Style the tab */
23 | div.tab {
24 | position: fixed;
25 | float: left;
26 | width: 230px;
27 | height: 100vh;
28 | padding-top: 70px;
29 | }
30 |
31 | /* Style the buttons inside the tab */
32 | .tablinks {
33 | display: inline-flex;
34 | background-color: inherit;
35 | color: #0c0c0d;
36 | fill: #0c0c0d;
37 | margin-left: 34px;
38 | padding-left: 16px;
39 | padding-right: 16px;
40 | padding-top: 15px;
41 | padding-bottom: 15px;
42 | width: calc(100% - 34px);
43 | border: none;
44 | border-radius: 2px;
45 | outline: none;
46 | font-size: 1rem;
47 | transition: background-color 150ms;
48 | -moz-user-select: none;
49 | }
50 |
51 | .tablinks svg {
52 | margin-right: 10px;
53 | }
54 |
55 | /* Change background color of buttons on hover */
56 | .tablinks:hover {
57 | background-color: rgba(12, 12, 13, 0.1);
58 | }
59 | .tablinks:active:hover {
60 | color: #0060df;
61 | fill: #0060df;
62 | background-color: rgba(12, 12, 13, 0.15);
63 | }
64 |
65 | /* Create an active/current "tab button" class */
66 | .tablinks.active {
67 | color: #0a84ff;
68 | fill: #0a84ff;
69 | }
70 | .tablinks.active:hover {
71 | color: #0a84ff;
72 | fill: #0a84ff;
73 | background-color: rgba(12, 12, 13, 0.20);
74 | }
75 | .tablinks.active:active:hover {
76 | color: #0060df;
77 | fill: #0060df;
78 | }
79 |
80 | /* Style the tab content */
81 | .tabcontent {
82 | margin-left: 230px;
83 | padding: 30px 12px;
84 | width: calc(100% - 230px);
85 | border-left: none;
86 | height: 100vh;
87 | }
88 |
89 | .tabcontent > fieldset {
90 | border-top: 2px solid #c1c1c1;
91 | border-bottom: none;
92 | border-left: none;
93 | border-right: none;
94 | padding-left: 2em;
95 | padding-top: 10px;
96 | padding-bottom: 25px;
97 | float:left;
98 | width: 95%;
99 | }
100 |
101 | legend {
102 | padding-left: 10px;
103 | padding-right: 10px;
104 | font-size: 1.2rem;
105 | }
106 |
107 |
108 | .settings-entry > input[type="checkbox"] {
109 | margin-top: 3px;
110 | }
111 | .settings-entry > input[type="text"] {
112 | width: calc(50% - 400px);
113 | }
114 | .settings-entry > input.long-text {
115 | width: calc(95% - 400px);
116 | }
117 |
118 | .settings-entry label {
119 | width: 400px;
120 | }
121 |
122 | .settings-entry2 {
123 | margin-top: 1em;
124 | margin-bottom: 1em;
125 | display: flex;
126 | flex-direction: row;
127 | }
128 | .settings-entry2 label {
129 | width: 200px;
130 | }
131 | .settings-entry2 > input[type="text"] {
132 | flex-grow: 1;
133 | height: 2em;
134 | }
135 | .settings-entry2 > textarea {
136 | margin-left: 6px;
137 | flex-grow: 1;
138 | height: 6em;
139 | resize: vertical;
140 | }
141 | .settings-entry2 .flex-space {
142 | flex-grow: 1;
143 | }
144 | .settings-entry2 > input[type="number"] {
145 | height: 2em;
146 | }
147 |
148 | .indent label {
149 | margin-left: 25px;
150 | margin-right: 4px;
151 | }
152 |
153 | .secondary-input {
154 | margin-left: 20px;
155 | width: 4em;
156 | }
157 |
158 | .secondary-label {
159 | margin-left: 6px !important;
160 | width: 50px !important;
161 | }
162 |
163 | .tld-checkbox {
164 | height: 1em;
165 | }
166 |
167 | .reset-button {
168 | float: right;
169 | height: 2em;
170 | margin-left: 30px;
171 | margin-right: 20px;
172 | }
173 |
174 | #max-data-size {
175 | width: 8em;
176 | }
177 |
178 |
179 | /*
180 | * Custom Rules Tab
181 | */
182 |
183 | /*** Rule editor : Modal window ***/
184 | .modal {
185 | display: none; /* Hidden by default */
186 | position: fixed; /* Stay in place */
187 | z-index: 999; /* Sit on top */
188 | padding-top: 20%; /* Location of the box */
189 | left: 0;
190 | top: 0;
191 | width: 100%; /* Full width */
192 | height: 100%; /* Full height */
193 | overflow: auto; /* Enable scroll if needed */
194 | background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
195 | }
196 | .modal-content {
197 | display: block;
198 | background-color: #fefefe;
199 | margin: auto;
200 | padding: 20px;
201 | border-style: solid;
202 | border-width: 1px;
203 | border-radius: 6px;
204 | border-color: #888;
205 | width: 700px;
206 | overflow: hidden;
207 | }
208 |
209 | .rule-entry label {
210 | width: 80px !important;
211 | }
212 | .rule-entry > input[type="text"] {
213 | width: calc(100% - 140px) !important;
214 | }
215 |
216 | #rule-editor\.error {
217 | width: 70%;
218 | height: 1em;
219 | margin-left: 9px;
220 | font-weight: bold;
221 | color: red;
222 | }
223 |
224 | .rule-editor\.buttons {
225 | float: right;
226 | margin-right: 22px;
227 | }
228 |
229 | .rule-editor\.buttons button {
230 | margin: 8px;
231 | }
232 |
233 | .modal-content button {
234 | vertical-align: top;
235 | font-size: 0.95em;
236 | }
237 |
238 | /*** special configuration for tabcontent ***/
239 | #custom-rules.tabcontent > fieldset {
240 | height: 100%;
241 | }
242 |
243 | /*** Rules List ***/
244 | .rules-container {
245 | margin-top: 0.5em;
246 | margin-left: 10%;
247 |
248 | width: 80%;
249 | height: 70%;
250 | }
251 |
252 | .rules-container > .rules-commands {
253 | margin-bottom: 20px;
254 | }
255 |
256 | div.rules-commands button {
257 | float: right;
258 | }
259 |
260 | .rules-container > .rules-list {
261 | height: 100%;
262 | }
263 |
264 | .rules-list {
265 | border-style: solid;
266 | border-width: 1px;
267 | border-radius: 2px;
268 | border-color: #c1c1c1;
269 |
270 | overflow: auto;
271 |
272 | display: flex;
273 | flex-direction: column;
274 | }
275 |
276 | .overflow {
277 | flex-grow: 1;
278 | }
279 |
280 | .rules-list > table {
281 | width: 100%;
282 | }
283 |
284 | .rules-table {
285 | table-layout: fixed;
286 | border-collapse: collapse;
287 | }
288 |
289 | .rules-table tr, .rules-table td, .rules-table div {
290 | display: inline-block;
291 | width: 100%;
292 | }
293 | .rules-table tr:hover {
294 | background-color: rgba(0, 149, 221, 0.25);
295 | }
296 |
297 | .settings-rule {
298 | width: calc(100% - 10px) !important;
299 | margin-top: 4px;
300 | margin-bottom: 4px;
301 | margin-left: 5px;
302 | -moz-user-select: none;
303 | cursor: move;
304 | border-width: 2px;
305 | border-style: dashed;
306 | border-color: rgba(0,0,0,0.0);
307 | }
308 | /* Drag n drop customization */
309 | .settings-rule.dragover {
310 | border-color: #000;
311 | }
312 |
313 | .rule-name {
314 | margin-left: 10px;
315 | width: calc(100% - 80px) !important;
316 | }
317 |
318 | .settings-rule input[type="checkbox"] {
319 | float: left;
320 | margin-top: 3px;
321 | }
322 |
323 | .settings-rule input[type="image"] {
324 | border-style: none;
325 | background-color: rgba(0,0,0,0.0);
326 | background-repeat: no-repeat;
327 | background-position: 50% 50%;
328 | width: 16px;
329 | height: 16px;
330 | margin-top: 3px;
331 | margin-right: 5px;
332 | }
333 |
334 | .rule\.buttons {
335 | float: right;
336 | width: auto !important;
337 | display: inline-block;
338 | }
339 |
340 | .edit-button {
341 | background-image: url(edit.png);
342 | }
343 | .edit-button:hover,
344 | .edit-button:active,
345 | .edit-button:focus {
346 | background-image: url(edit-hover.png);
347 | }
348 |
349 | .delete-button {
350 | background-image: url(delete.png);
351 | }
352 | .delete-button:hover,
353 | .delete-button:active,
354 | .delete-button:focus {
355 | background-image: url(delete-hover.png);
356 | }
357 |
358 |
--------------------------------------------------------------------------------
/webextension/options/delete-hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/options/delete-hover.png
--------------------------------------------------------------------------------
/webextension/options/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/options/delete.png
--------------------------------------------------------------------------------
/webextension/options/edit-hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/options/edit-hover.png
--------------------------------------------------------------------------------
/webextension/options/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/options/edit.png
--------------------------------------------------------------------------------
/webextension/options/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/options/empty.png
--------------------------------------------------------------------------------
/webextension/options/options.css:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 |
6 | * author: MarkaPola
7 | */
8 |
9 | form {
10 | color: #0c0c0d;
11 | background-color: #f9f9fa;
12 | }
13 |
14 | .settings-entry > input[type="text"] {
15 | width: calc(50% - 200px);
16 | }
17 | .settings-entry > input.long-text {
18 | width: calc(95% - 200px);
19 | }
20 | .settings-entry input[type="color"] {
21 | margin-left: 15px;
22 | }
23 |
24 | .settings-entry label {
25 | width: 200px;
26 | }
27 |
28 | form > .settings-entry {
29 | width: 100%;
30 | }
31 |
32 | form > hr {
33 | background-color: #c1c1c1;
34 | height: 1px;
35 | border: none;
36 | width: 100%;
37 | }
38 |
39 | input[type="image"] {
40 | height: 25px;
41 | }
42 |
43 | .key-combination input, .key-combination button {
44 | vertical-align: top;
45 | }
46 |
--------------------------------------------------------------------------------
/webextension/options/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/webextension/options/options.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // author: MarkaPola
7 |
8 | //
9 | // Manage the options page of the add-on
10 | //
11 |
12 |
13 | //=============== settings management =====================
14 | var properties = {};
15 |
16 | function updatePreference (id, value) {
17 | function setCheckbox (id, checked) {
18 | $(id).checked = checked;
19 | }
20 | function setColorSelector (type, config) {
21 | setCheckbox(`override-${type}-color`, config.override);
22 | let colorPicker = $(`href-${type}-color`);
23 | colorPicker.value = config.color;
24 | colorPicker.disabled = !config.override;
25 | }
26 |
27 | properties[id] = value;
28 |
29 | switch (id) {
30 | // basic settings
31 | case 'activated':
32 | setCheckbox('activated', properties.activated);
33 | break;
34 | case 'manual':
35 | setCheckbox('on-demand', properties.manual);
36 | break;
37 | case 'displayBadge':
38 | setCheckbox('display-counter', properties.displayBadge);
39 | break;
40 | case 'contextMenuIntegration':
41 | setCheckbox('context-menu', properties.contextMenuIntegration);
42 | break;
43 | // domains management
44 | case 'domains':
45 | setCheckbox('use-regexp', properties.domains.useRegExp);
46 | $('domain-filtering-mode').value = properties.domains.type;
47 | let domainList = $('domains-list');
48 | if (properties.domains.type == 'none') {
49 | domainList.disabled = true;
50 | domainList.value = '';
51 | } else {
52 | domainList.disabled = false;
53 | domainList.value = properties.domains.list[properties.domains.type].join(' ');
54 | }
55 | break;
56 | // link colors management
57 | case 'style':
58 | setColorSelector('text', properties.style.text);
59 | setColorSelector('background', properties.style.background);
60 | break;
61 | case 'sync':
62 | // settings management
63 | setCheckbox('sync-settings', properties.sync);
64 |
65 | }
66 | }
67 |
68 | function initializePreferences () {
69 | return browser.storage.local.get({sync: false, activated: true}).then(result => {
70 | properties.area = result.sync ? 'sync' : 'local';
71 | updatePreference('sync', result.sync);
72 | updatePreference('activated', result.activated);
73 |
74 | ShortcutCustomizeUI.build().then(list => {
75 | // remove any current shortcuts list
76 | let shortcuts = $('shortcuts');
77 | shortcuts.parentNode.replaceChild(shortcuts.cloneNode(false), shortcuts);
78 | // insert new list
79 | $('shortcuts').appendChild(list);
80 | list.addEventListener('ShortcutChanged', event => {
81 | properties.hotKeys[event.detail.name] = event.detail.key;
82 | browser.storage[properties.area].set({hotKeys: properties.hotKeys}).catch(reason => console.error(reason));
83 | });
84 | });
85 |
86 | return browser.storage[properties.area].get().then(result => {
87 | for (let id in result)
88 | updatePreference(id, result[id]);
89 |
90 | return true;
91 | });
92 | });
93 | }
94 |
95 | function managePreferences () {
96 | function addCheckboxManager (id, preference, area = properties.area) {
97 | let checkbox = $(id);
98 | checkbox.addEventListener('change', event => {
99 | properties[preference] = checkbox.checked;
100 | browser.storage[area].set({[preference]: properties[preference]}).catch(reason => console.error(reason));
101 | });
102 | }
103 | function addColorManager (type, area = properties.area) {
104 | // checkbox
105 | let checkbox = $(`override-${type}-color`);
106 | let colorPicker = $(`href-${type}-color`);
107 |
108 | checkbox.addEventListener('change', event => {
109 | properties.style[type].override = checkbox.checked;
110 | colorPicker.disabled = !checkbox.checked;
111 |
112 | browser.storage[properties.area].set({style: properties.style}).catch(reason => console.error(reason));
113 | });
114 | colorPicker.addEventListener('change', event => {
115 | properties.style[type].color = colorPicker.value;
116 |
117 | browser.storage[properties.area].set({style: properties.style}).catch(reason => console.error(reason));
118 | });
119 | }
120 |
121 | addCheckboxManager('activated', 'activated', 'local');
122 | addCheckboxManager('on-demand', 'manual');
123 | addCheckboxManager('display-counter', 'displayBadge');
124 | addCheckboxManager('context-menu', 'contextMenuIntegration');
125 |
126 | // domain management
127 | let useRegex = $('use-regexp');
128 | let select = $('domain-filtering-mode');
129 | let domainList = $('domains-list');
130 | useRegex.addEventListener('change', event => {
131 | properties.domains.useRegExp = useRegex.checked;
132 |
133 | browser.storage[properties.area].set({domains: properties.domains}).catch(reason => console.error(reason));
134 | });
135 | select.addEventListener('change', event => {
136 | properties.domains.type = select.value;
137 | if (select.value === 'none') {
138 | domainList.disabled = true;
139 | domainList.value = '';
140 | } else {
141 | domainList.disabled = false;
142 | domainList.value = properties.domains.list[select.value].join(' ');
143 | }
144 | browser.storage[properties.area].set({domains: properties.domains}).catch(reason => console.error(reason));
145 | });
146 | domainList.addEventListener('change', event => {
147 | properties.domains.list[properties.domains.type] = domainList.value.split(' ').filter((value, index, self) => self.indexOf(value) === index);
148 |
149 | browser.storage[properties.area].set({domains: properties.domains}).catch(reason => console.error(reason));
150 | });
151 |
152 | // link colors management
153 | addColorManager('text');
154 | addColorManager('background');
155 |
156 | // advanced settings
157 | // check if Advanced options tab is already opened
158 | function advancedOptionsOpened (url) {
159 | return browser.runtime.getBrowserInfo().then(info => {
160 | if (parseInt(info.version) < 56) {
161 | // moz-extension: schema not accepted
162 | return browser.tabs.query({}).then(tabs => tabs.find(tab => tab.url === url));
163 | } else {
164 | return browser.tabs.query({url: url}).then(tabs => tabs.length > 0 ? tabs[0] : undefined);
165 | }
166 | });
167 | }
168 | $('advanced-settings').addEventListener('click', event => {
169 | const url = browser.extension.getURL('/options/advanced-options.html');
170 |
171 | advancedOptionsOpened(url).then(tab => {
172 | if (tab) {
173 | return browser.tabs.update(tab.id, {active: true}).then(() => {
174 | return browser.history.deleteUrl({url: url});
175 | });
176 | } else {
177 | // create advanced settings tabs next to the options tab
178 | return browser.tabs.getCurrent().then(tab => {
179 | return browser.tabs.create({active: true,
180 | index: tab.index+1,
181 | url: url}).then(() => {
182 | // don't keep this url in history
183 | return browser.history.deleteUrl({url: url});
184 | });
185 | });
186 | }
187 | }).catch(reason => console.error(reason));
188 | });
189 |
190 | // settings management
191 | let syncSettings = $('sync-settings');
192 | syncSettings.addEventListener('change', event => {
193 | browser.storage.local.set({sync: syncSettings.checked}).then(() => {
194 | initializePreferences();
195 | }).catch(reason => console.error(reason));
196 | });
197 | let resetDefault = $('reset-defaults');
198 | resetDefault.addEventListener('click', event => {
199 | browser.runtime.sendMessage({id: 'reset-defaults'}).then(message => {
200 | if (message.done) initializePreferences();
201 | }).catch(reason => console.error(reason));
202 | });
203 | }
204 |
205 | // audit storage changes
206 | browser.storage.onChanged.addListener((changes, area) => {
207 | if (area === 'local') {
208 | if (changes.hasOwnProperty('sync'))
209 | updatePreference('sync', changes.sync.newValue);
210 | if (changes.hasOwnProperty('activated'))
211 | updatePreference('activated', changes.activated.newValue);
212 | }
213 |
214 | if (area === properties.area) {
215 | for (let key in changes) {
216 | updatePreference(key, changes[key].newValue);
217 | }
218 | }
219 | });
220 |
221 |
222 | document.addEventListener("DOMContentLoaded",
223 | () => {
224 | // UI tweak for windows
225 | browser.runtime.getPlatformInfo().then(platformInfo => {
226 | if (platformInfo.os === 'win') {
227 | $('linkificator-settings').style['font-family'] = 'Segoe UI';
228 | $('linkificator-settings').style['font-size'] = '1.25rem';
229 | } else if (platformInfo.os === 'mac') {
230 | $('linkificator-settings').style['font-family'] = 'Arial';
231 | $('linkificator-settings').style['font-size'] = '1.1rem';
232 | }
233 | });
234 | initializePreferences().then(() => {
235 | managePreferences();
236 | });
237 | },
238 | {
239 | capture: true,
240 | passive: true,
241 | once: true
242 | });
243 |
--------------------------------------------------------------------------------
/webextension/options/restart.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/webextension/popup/extension.svg:
--------------------------------------------------------------------------------
1 |
4 |
7 |
--------------------------------------------------------------------------------
/webextension/popup/link-exclude.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/webextension/popup/link-include.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/webextension/popup/link-update.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webextension/popup/popup.css:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 |
6 | * author: MarkaPola
7 | */
8 |
9 | .popup-content {
10 | display: flex;
11 | flex-direction: column;
12 | background-color: #fbfbfb;
13 | box-shadow: 4px 4px 2px 0px rgba(0,0,0,0.3);
14 | z-index: 1;
15 | }
16 |
17 | .popup-content > .popup-entry {
18 | color: #333;
19 | padding: 4px 10px;
20 | text-decoration: none;
21 | display: inline-block;
22 | width: calc(100%);
23 | margin-left: auto;
24 | margin-right: auto;
25 | }
26 |
27 | /* Change color of popup entries on hover */
28 | .popup-entry:hover {
29 | background-color: rgba(0, 149, 221, 0.25);
30 | }
31 |
32 | .popup-content .entry-label {
33 | vertical-align: top;
34 | text-align: left;
35 | min-width: 90px;
36 | margin-left: 10px;
37 | width: auto;
38 | float: left;
39 | }
40 |
41 | .popup-content .entry-shortcut {
42 | color: #808080;
43 | text-align: right;
44 | margin-left: 8px;
45 | float: right;
46 | }
47 |
48 | .popup-content > hr {
49 | width: 100%;
50 | }
51 | .popup-content hr {
52 | color: #fff;
53 | float: right;
54 | margin-top: 0em;
55 | margin-bottom: 0.30em;
56 | }
57 |
58 | .popup-content input {
59 | vertical-align: top;
60 | font-size: 1.25rem;
61 | float: left;
62 | }
63 | .popup-content input[type="image"] {
64 | margin-right: 6px;
65 | width: 16px;
66 | height: 16px;
67 | }
68 |
69 | /* to manage disabled menu entries */
70 | .popup-entry-disabled {
71 | color: #808080 !important;
72 | }
73 | .popup-entry-disabled:hover {
74 | background-color: #e7e7e7 !important;
75 | }
76 |
77 | .popup-entry-disabled input[type="image"], .popup-entry-disabled .entry-shortcut {
78 | visibility: hidden;
79 | }
80 |
--------------------------------------------------------------------------------
/webextension/popup/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/webextension/popup/popup.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // author: MarkaPola
7 |
8 | //
9 | // Manage the options page of the add-on
10 | //
11 |
12 | Promise.prototype.finally = function (cb) {
13 | return this.then(v => Promise.resolve(cb(v)),
14 | v => Promise.reject(cb(v)));
15 | };
16 |
17 | function managePopup (context) {
18 | $('panel-options').addEventListener('click', event => {
19 | browser.runtime.openOptionsPage().finally(() => window.close());
20 | });
21 |
22 | let manual = $('panel-manual');
23 | manual.checked = context.manual;
24 | manual.addEventListener('click', event => {
25 | browser.storage[context.area].set({manual: manual.checked}).finally(() => window.close());
26 | });
27 |
28 | let activate = $('panel-activate');
29 | let deactivate = $('panel-deactivate');
30 | if (context.activated) {
31 | $('entry-activate').setAttribute('style', 'display: none');
32 | $('panel-deactivate').addEventListener('click', event => {
33 | browser.storage.local.set({activated: false}).finally(() => window.close());
34 | });
35 | } else {
36 | $('entry-deactivate').setAttribute('style', 'display: none');
37 | $('panel-activate').addEventListener('click', event => {
38 | browser.storage.local.set({activated: true}).finally(() => window.close());
39 | });
40 | }
41 |
42 | let entry, manage;
43 | if (context.status === 'excluded' || context.status === 'filtered') {
44 | $('entry-exclude').setAttribute('style', 'display: none');
45 | entry = $('entry-include');
46 | manage = $('panel-include');
47 | } else {
48 | $('entry-include').setAttribute('style', 'display: none');
49 | entry = $('entry-exclude');
50 | manage = $('panel-exclude');
51 | }
52 | if (context.status === 'not_processed') {
53 | entry.classList.add('popup-entry-disabled');
54 | }
55 | manage.addEventListener('click', event => {
56 | browser.runtime.sendMessage({id: 'manage-url', info: context}).catch(reason => console.error(reason)).finally(() => window.close());
57 | });
58 |
59 | let linkify = $('panel-linkify');
60 | if (context.status === 'processed') {
61 | linkify.addEventListener('click', event => {
62 | browser.runtime.sendMessage({id: 're-parse', info: context}).catch(reason => console.error(reason)).finally(() => window.close());
63 | });
64 | } else {
65 | $('entry-linkify').classList.add('popup-entry-disabled');
66 | }
67 |
68 | // fill keyboard shortcuts
69 | browser.commands.getAll().then(commands => {
70 | for (let command of commands) {
71 | switch(command.name) {
72 | case 'Toggle':
73 | $('shortcut-activate').textContent = command.shortcut;
74 | $('shortcut-deactivate').textContent = command.shortcut;
75 | break;
76 | case 'Manual':
77 | $('shortcut-manual').textContent = command.shortcut;
78 | break;
79 | case 'Manage':
80 | $('shortcut-include').textContent = command.shortcut;
81 | $('shortcut-exclude').textContent = command.shortcut;
82 | break;
83 | case 'Update':
84 | $('shortcut-update').textContent = command.shortcut;
85 | break;
86 | }
87 | }
88 | });
89 | }
90 |
91 |
92 | document.addEventListener("DOMContentLoaded",
93 | () => {
94 | // query current tab status
95 | browser.runtime.sendMessage({id: 'tab-context'}).then(context => {
96 | managePopup(context);
97 | }).catch(reason => console.error(reason));
98 | },
99 | {
100 | capture: true,
101 | passive: true,
102 | once: true
103 | });
104 |
--------------------------------------------------------------------------------
/webextension/resources/css/common.css:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 |
6 | * author: MarkaPola
7 | */
8 |
9 | /*
10 | * Utilitu classes
11 | */
12 | .hidden {
13 | display: none !important;
14 | }
15 |
16 |
17 | /*
18 | * Global configuration for some elements
19 | */
20 | input, select, option, button {
21 | color: #333;
22 | font-size: inherit;
23 | }
24 | input, button {
25 | border-style: solid;
26 | border-width: 1px;
27 | border-radius: 2px;
28 | border-color: #c1c1c1;
29 | }
30 |
31 | input:focus {
32 | border-color: rgba(0, 149, 221, 0.8);
33 | }
34 | input[type="text"] {
35 | text-align: left;
36 | }
37 | input[type="number"] {
38 | text-align: right;
39 | }
40 | input[type="image"] {
41 | padding: 4px;
42 | background-color: white;
43 | cursor: pointer;
44 | }
45 | input[type="image"]:hover {
46 | background-color: #f1f1f1;
47 | }
48 | input[type="image"]:active {
49 | background-color: #dadada;
50 | }
51 | input[type="image"]:disabled {
52 | background-color: #f1f1f1;
53 | color: #666666;
54 | cursor: inherit;
55 | }
56 | input[type="image"]::-moz-focus-inner {
57 | border: 0;
58 | }
59 | select {
60 | border-style: solid;
61 | border-width: 1px;
62 | border-color: #c1c1c1;
63 | border-radius: 2px;
64 | }
65 | select:hover {
66 | background-color: #f1f1f1;
67 | }
68 | select option:hover {
69 | box-shadow: 0 0 10px 100px rgba(0, 149, 221, 0.25) inset;
70 | }
71 | select option:checked {
72 | box-shadow: 0 0 10px 100px rgb(0, 149, 221) inset;
73 | }
74 | button {
75 | padding: 6px;
76 | background-color: white;
77 | cursor: pointer;
78 | }
79 | button:hover {
80 | background-color: #f1f1f1;
81 | }
82 | button:active {
83 | background-color: #dadada;
84 | }
85 | button:disabled {
86 | background-color: #f1f1f1;
87 | color: #666666;
88 | cursor: inherit;
89 | }
90 | button::-moz-focus-inner {
91 | border: 0;
92 | }
93 |
94 |
95 | .settings-entry {
96 | display: block;
97 | margin-top: 0.5em;
98 | margin-bottom: 0.5em;
99 | margin-left: 0;
100 | margin-right: 0;
101 | }
102 |
103 | .settings-entry input, .settings-entry select, .settings-entry button {
104 | vertical-align: top;
105 | }
106 |
107 | .settings-entry label {
108 | display: inline-block;
109 | margin-left: 9px;
110 | margin-right: 20px;
111 | vertical-align: top;
112 | text-align: left;
113 | }
114 |
--------------------------------------------------------------------------------
/webextension/resources/doc/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/doc/favicon.png
--------------------------------------------------------------------------------
/webextension/resources/doc/release-notes.css:
--------------------------------------------------------------------------------
1 |
2 | .large-text {
3 | font-size: 120%;
4 | }
5 |
6 | .version-changes {
7 | position: relative;
8 | left: 5%;
9 | max-width: 90%;
10 | }
11 |
12 | .feature-list-title {
13 | font-size: 115%;
14 | font-weight: bold;
15 | color: rgb(30, 100, 255);
16 | }
17 |
18 | html {
19 | background: linear-gradient(to bottom, rgb(220, 245, 255), rgb(255, 255, 255));
20 | background-size: cover;
21 | background-repeat: no-repeat;
22 | }
23 |
24 | #linkificator-icon {
25 | vertical-align: middle;
26 | float: left;
27 | margin-top: 10pt;
28 | margin-right: 30pt;
29 | margin-bottom: 10pt;
30 | margin-left: 10pt;
31 | }
32 |
--------------------------------------------------------------------------------
/webextension/resources/doc/release-notes.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* This Source Code Form is subject to the terms of the Mozilla Public
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 |
7 | /* Release notes localization helper - Linkificator's module
8 | * author: MarkaPola */
9 |
10 |
11 | function translateElements() {
12 | const children = document.querySelectorAll('*[data-l10n-id]');
13 | for(let child of children) {
14 | if(!child.dataset.l10nNocontent) {
15 | const data = browser.i18n.getMessage(child.dataset.l10nId);
16 | if(data && data.length != 0) {
17 | let fragment = document.createRange().createContextualFragment(data);
18 | child.insertBefore(fragment, child.firstChild);
19 | }
20 | }
21 | }
22 | }
23 |
24 |
25 |
26 | document.addEventListener("DOMContentLoaded",
27 | () => translateElements(),
28 | {
29 | capture: true,
30 | passive: true,
31 | once: true
32 | });
33 |
--------------------------------------------------------------------------------
/webextension/resources/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/icon128.png
--------------------------------------------------------------------------------
/webextension/resources/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/icon48.png
--------------------------------------------------------------------------------
/webextension/resources/icons/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/icon64.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link16-excluded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link16-excluded.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link16-manual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link16-manual.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link16-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link16-off.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link16-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link16-on.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link32-excluded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link32-excluded.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link32-manual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link32-manual.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link32-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link32-off.png
--------------------------------------------------------------------------------
/webextension/resources/icons/link32-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarkaPola/Linkificator/c3a3f3347c4e5742e9102792c69e94cd3bf9e589/webextension/resources/icons/link32-on.png
--------------------------------------------------------------------------------
/webextension/resources/js/DOMutils.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | // author: MarkaPola
7 |
8 | //
9 | // DOM Utilities
10 | //
11 |
12 | function $ (id) {
13 | return document.getElementById(id);
14 | }
15 |
16 |
17 | /*
18 | * Retrieve MIME type from DOM Document
19 | *
20 | * @param [object] document: DOM Document object
21 | */
22 |
23 | function Document (doc) {
24 | "use strict";
25 |
26 | var document = doc;
27 |
28 | return {
29 | get contentType () {
30 | let contentType = null;
31 |
32 | if (document) {
33 | contentType = document.contentType;
34 |
35 | if (!contentType) {
36 | // try to get content type for head section
37 | let ct = document.querySelector('meta[http-equiv="content-type" i]');
38 | if (ct) {
39 | let content = ct.getAttribute('content');
40 | if (content) {
41 | contentType = content.trim().split(/;|\s/)[0];
42 | }
43 | }
44 | }
45 |
46 | if (! contentType) {
47 | // Check possible plain text page
48 | if (document.querySelector('link[href="resource://gre-resources/plaintext.css"]')) {
49 | contentType = 'text/plain';
50 | }
51 | }
52 | }
53 |
54 | if (! contentType) {
55 | contentType = 'text/html';
56 | }
57 |
58 | return contentType;
59 | }
60 | };
61 | }
62 |
--------------------------------------------------------------------------------
/webextension/resources/js/localization.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* Localization helper - Linkificator's module
7 | * author: MarkaPola */
8 |
9 | function translateElementAttributes(element) {
10 | const attributes = [ 'title', 'accesskey', 'alt', 'label', 'placeholder', 'abbr', 'content', 'download', 'srcdoc', 'value' ];
11 | const separator = '@';
12 |
13 | const presentAttributes = element.dataset.l10nAttrs.split(",");
14 |
15 | // Translate allowed attributes.
16 | for(let attribute of presentAttributes) {
17 | let data;
18 | if(attributes.includes(attribute)) {
19 | data = browser.i18n.getMessage(element.dataset.l10nId + separator + attribute);
20 | }
21 |
22 | if(data && data.length != 0) {
23 | element.setAttribute(attribute, data);
24 | }
25 | }
26 | }
27 |
28 | function translateElements() {
29 | const children = document.querySelectorAll('*[data-l10n-id]');
30 | for(let child of children) {
31 | if(!child.dataset.l10nNocontent) {
32 | const data = browser.i18n.getMessage(child.dataset.l10nId);
33 | if(data && data.length != 0) {
34 | child.textContent = data;
35 | }
36 | }
37 |
38 | if(child.dataset.l10nAttrs) {
39 | translateElementAttributes(child);
40 | }
41 | }
42 | }
43 |
44 |
45 |
46 | document.addEventListener("DOMContentLoaded",
47 | () => translateElements(),
48 | {
49 | capture: true,
50 | passive: true,
51 | once: true
52 | });
53 |
--------------------------------------------------------------------------------
/webextension/resources/js/thread.js:
--------------------------------------------------------------------------------
1 |
2 | /* This Source Code Form is subject to the terms of the Mozilla Public
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 |
6 | /* Thread helper - Linkificator's module
7 | * author: MarkaPola */
8 |
9 | // javascript does not support parallel executions
10 | // to avoid any CPU total comsumption and UI freezes, this utility help execute tasks in the background
11 |
12 | // arguments to Thread function are:
13 | // action: object which MUST expose, at least, the following functions:
14 | // * execute: will execute a part of the work and must return true if work is completed
15 | // * finish: will execute all remaining tasks to complete the work
16 | // * abort: thread was aborted, remaining work must not be done
17 | // * complete: this function will be called when all work is done
18 | // interval: waiting time (in ms) between each execution. If not specified, 10ms will be used.
19 | // all functions are argumentless except abort and complete which pass thread reference.
20 | //
21 | // Thread functions are:
22 | // start: launch execution of action object in the background.
23 | // terminate: request to complete action in the foreground.
24 | // kill: stop background execution. action will not terminate.
25 | //
26 | // Here is a small example: each part of the work will be done at 100ms interval.
27 | //
28 | // var test = {
29 | // index: 0,
30 | // total: 100,
31 | //
32 | // execute: function () {
33 | // var part = Math.min((this.index + 3), this.total);
34 | // while (this.index < part) {
35 | // console.log (this.index);
36 | // this.index++;
37 | // }
38 | // console.log ("chunk done");
39 | //
40 | // return this.index == this.total;
41 | // },
42 | // finish: function () {
43 | // while (this.index++ < this.total)
44 | // console.log (this.index);
45 | // console.log ("complete done");
46 | // },
47 | // abort: function (thread) {
48 | // console.log ("aborted at " + this.index);
49 | // },
50 | // complete: function (thread) {
51 | // console.log ("completed");
52 | // }
53 | // };
54 | //
55 | // var thread = Thread (test, 100);
56 | // thread.start ();
57 | //
58 |
59 |
60 | function Thread (action, interval) {
61 | "use strict";
62 |
63 | var thread = {
64 | ref: null,
65 | interval: 10,
66 | action: null,
67 | worker: null,
68 | completed: false
69 | };
70 | if (interval)
71 | thread.interval = interval;
72 | thread.action = action;
73 |
74 | function execute () {
75 | if (thread.completed)
76 | return;
77 |
78 | if (thread.action.execute()) {
79 | thread.completed = true;
80 | thread.action.complete(thread.ref);
81 | } else {
82 | thread.worker = setTimeout(function(){execute();}, interval);
83 | }
84 | }
85 |
86 | function finish (timeout) {
87 | if (thread.completed)
88 | return;
89 |
90 | clearTimeout(thread.worker);
91 |
92 | let terminate = function() {
93 | thread.completed = true;
94 | thread.action.finish();
95 | thread.action.complete(thread.ref);
96 | };
97 |
98 | if (timeout) {
99 | thread.worker = setTimeout(terminate, timeout);
100 | } else {
101 | terminate();
102 | }
103 | }
104 |
105 | function abort () {
106 | if (thread.completed)
107 | return;
108 |
109 | clearTimeout(thread.worker);
110 |
111 | thread.completed = true;
112 | thread.action.abort(thread.ref);
113 | }
114 |
115 | thread.ref = {
116 | start: function () {
117 | execute();
118 | },
119 |
120 | terminate: function (timeout) {
121 | finish();
122 | },
123 |
124 | kill: function () {
125 | abort();
126 | }
127 | };
128 |
129 | return thread.ref;
130 | }
131 |
--------------------------------------------------------------------------------
/webextension/web-ext-config.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | ignoreFiles: ['**/*~', 'web-ext-config.js'],
4 | build: {
5 | overwriteDest: true
6 | }
7 | };
8 |
9 |
--------------------------------------------------------------------------------