├── .gitignore
├── src
├── content
│ ├── options
│ │ ├── options.css
│ │ ├── options.js
│ │ └── options.html
│ ├── images
│ │ ├── editemailsubjectmx-icon-16px.png
│ │ ├── editemailsubjectmx-icon-32px.png
│ │ └── editemailsubjectmx-icon-64px.png
│ ├── editemailsubjectPopup.html
│ ├── editemailsubjectPopup.js
│ ├── scripts
│ │ ├── locales.js
│ │ └── preferences.js
│ └── editemailsubject.js
├── CONTRIBUTORS
├── _locales
│ ├── de-DE
│ │ └── messages.json
│ ├── fr-FR
│ │ └── messages.json
│ ├── hu
│ │ └── messages.json
│ └── en-US
│ │ └── messages.json
├── api
│ ├── LegacyPrefs
│ │ ├── schema.json
│ │ └── implementation.js
│ └── MessageModification
│ │ ├── schema.json
│ │ └── implementation.js
├── background.js
├── manifest.json
└── LICENSE
├── cmds.txt
├── scripts
└── dev-tools
│ └── localization
│ └── dtd-converter-py
│ ├── README.md
│ ├── checkJSON.py
│ └── migrateLocale.py
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | *.xpi
--------------------------------------------------------------------------------
/src/content/options/options.css:
--------------------------------------------------------------------------------
1 | .ees input[type="checkbox"] {
2 | vertical-align: top;
3 | float:left;
4 | margin-right:1ex;
5 | }
6 |
--------------------------------------------------------------------------------
/src/content/images/editemailsubjectmx-icon-16px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/valinet/IMAPNotes/HEAD/src/content/images/editemailsubjectmx-icon-16px.png
--------------------------------------------------------------------------------
/src/content/images/editemailsubjectmx-icon-32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/valinet/IMAPNotes/HEAD/src/content/images/editemailsubjectmx-icon-32px.png
--------------------------------------------------------------------------------
/src/content/images/editemailsubjectmx-icon-64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/valinet/IMAPNotes/HEAD/src/content/images/editemailsubjectmx-icon-64px.png
--------------------------------------------------------------------------------
/src/content/options/options.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', () => {
2 | editEmailSubjectPreferences.load(document);
3 | }, { once: true });
4 |
--------------------------------------------------------------------------------
/cmds.txt:
--------------------------------------------------------------------------------
1 | # Project utility commands
2 |
3 | # Building - I will be uploading my build script that deals with versions and file inclusions
4 |
5 | # Windows
6 | # quickie build - uses open source 7zip https://www.7-zip.org/
7 | 7z a .\xpi\editemailsubject-2.1.2b1.xpi .\src\*
8 |
9 | # *nix - zip/gzip
10 | zip ./xpi/editemailsubject-2.1.2b1.xpi ./src/*
--------------------------------------------------------------------------------
/src/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | ## Original Creator
2 | * PRIN Jean-Charles, 'jisse44'
3 |
4 |
5 | ## Contributors
6 | * Achim Czasch
7 | * Carl-Erich (c-e-github@github)
8 | * Christopher (cleidigh@github)
9 | * Dillinger
10 | * Guenter (neandr@github)
11 | * John Bieling (jobisoft@github)
12 | * Paolo Kaosmos
13 |
14 |
15 | ## Translators
16 | * Ruben Barkow (German)
17 |
18 |
--------------------------------------------------------------------------------
/src/content/options/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Options
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
__EESMSG_lang.settings__
15 | (empty)
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/_locales/de-DE/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionDescription": {
3 | "message": "Email Betreff bearbeiten"
4 | },
5 | "extensionName": {
6 | "message": "Edit email subject"
7 | },
8 | "lang.settings": {
9 | "message": "Einstellungen"
10 | },
11 | "lang.localOnly": {
12 | "message": "Änderungen nur lokal speichern (Lokale Datenbank)"
13 | },
14 | "lang.menuTitle": {
15 | "message": "&Email Betreff bearbeiten"
16 | },
17 | "lang.subject": {
18 | "message": "Betreff:"
19 | },
20 | "lang.subjectOld": {
21 | "message": "Original betreff:"
22 | },
23 | "lang.warning": {
24 | "message": "Warnung, der Betreff wurde schon bearbeitet. Eine erneute Änderung ist mit Risiken verbunden."
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/_locales/fr-FR/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionDescription": {
3 | "message": "Modifiez les lignes d'objet des messages reçus."
4 | },
5 | "extensionName": {
6 | "message": "Edit email subject"
7 | },
8 | "lang.settings": {
9 | "message": "Réglages"
10 | },
11 | "lang.localOnly": {
12 | "message": "Changements locaux uniquement (base de donnée locale de Thunderbird)"
13 | },
14 | "lang.menuTitle": {
15 | "message": "&Editer le sujet du message"
16 | },
17 | "lang.subject": {
18 | "message": "Sujet:"
19 | },
20 | "lang.subjectOld": {
21 | "message": "Sujet original:"
22 | },
23 | "lang.warning": {
24 | "message": "Attention, le sujet de ce mail a déjà été modifié. Le changer de nouveau comprend des risques."
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/_locales/hu/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionDescription": {
3 | "message": "A kapott e-mailek tárgynak szerkesztése."
4 | },
5 | "extensionName": {
6 | "message": "E-mail tárgyának szerkesztése (Edit Email Subject)"
7 | },
8 | "lang.settings": {
9 | "message": "Beállítások"
10 | },
11 | "lang.localOnly": {
12 | "message": "Csak helyi változások (csak a helyi Thunderbird index adatbázis módosul)"
13 | },
14 | "lang.menuTitle": {
15 | "message": "Tárgy s&zerkesztése"
16 | },
17 | "lang.subject": {
18 | "message": "Tárgy:"
19 | },
20 | "lang.subjectOld": {
21 | "message": "Eredeti tárgy:"
22 | },
23 | "lang.warning": {
24 | "message": "Figyelem, a téma már megváltozott. Próbálja ki újra a saját felelősségére."
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/_locales/en-US/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "extensionDescription": {
3 | "message": "Edit received e-mail subjects."
4 | },
5 | "extensionName": {
6 | "message": "Edit email subject"
7 | },
8 | "lang.settings": {
9 | "message": "Settings"
10 | },
11 | "lang.localOnly": {
12 | "message": "Only local changes (will only change local Thunderbird index database)"
13 | },
14 | "lang.menuTitle": {
15 | "message": "Edit IMAP note(s) [&z]"
16 | },
17 | "lang.subject": {
18 | "message": "Subject:"
19 | },
20 | "lang.subjectOld": {
21 | "message": "Original subject:"
22 | },
23 | "lang.warning": {
24 | "message": "Warning, subject has already been changed. Try it again at your own risk."
25 | },
26 | "lang.menuTitle2": {
27 | "message": "Duplicate IMAP note(s) [&x]"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/scripts/dev-tools/localization/dtd-converter-py/README.md:
--------------------------------------------------------------------------------
1 | # migrateLocale.py
2 |
3 | This python3 script converts legacy locale files (*.properties and *.dtd)
4 | to the new WebExt JSON format.
5 |
6 | ## Usage on Windows
7 |
8 | To install python3 on Windows, download it from here:
9 | https://www.python.org/downloads/
10 |
11 | If you are unsure which file to download, take this one:
12 | https://www.python.org/ftp/python/3.8.2/python-3.8.2-webinstall.exe
13 |
14 | After you have installed python3, open a command prompt (cmd) and execute:
15 | `py `
16 |
17 | The script will print a help screen, on how to use it.
18 |
19 | ## Usage on Linux
20 |
21 | Python3 is probably installed by your Linux distribution. If not, run
22 | your package manager to get it installed. Depending on your distribution,
23 | you can start the script by calling:
24 | `python3 `
25 |
26 | The script will print a help screen, on how to use it.
27 |
--------------------------------------------------------------------------------
/src/content/editemailsubjectPopup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/content/editemailsubjectPopup.js:
--------------------------------------------------------------------------------
1 | async function okAndInput(e) {
2 | if(e.ctrlKey && e.code === "KeyS") {
3 | msg = await messenger.runtime.sendMessage({action: "requestUpdate", newSubject: document.getElementById("editemailsubjectInput").value, msg: msg});
4 | document.title = msg.subject;
5 | //const windowId = (await messenger.windows.getCurrent()).id;
6 | //await messenger.windows.remove(windowId);
7 | }
8 | }
9 |
10 | async function cancel(e) {
11 | const windowId = (await messenger.windows.getCurrent()).id;
12 | await messenger.windows.remove(windowId);
13 | }
14 | var msg;
15 | async function load() {
16 | msg = await messenger.runtime.sendMessage({action: "requestData"});
17 |
18 | document.title = msg.subject;
19 | document.getElementById("editemailsubjectInput").value = msg.body;
20 |
21 | document.getElementById("editemailsubjectInput").addEventListener("keydown", okAndInput);
22 |
23 | document.getElementById("editemailsubjectInput").focus();
24 | }
25 |
26 | document.addEventListener('DOMContentLoaded', load, { once: true });
--------------------------------------------------------------------------------
/src/api/LegacyPrefs/schema.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "namespace": "LegacyPrefs",
4 | "functions": [
5 | {
6 | "name": "get",
7 | "type": "function",
8 | "description": "Gets a value from the legacy pref system.",
9 | "async": true,
10 | "parameters": [
11 | {
12 | "name": "aName",
13 | "type": "string",
14 | "description": "Name of the preference."
15 | },
16 | {
17 | "name": "aDefault",
18 | "description": "Default value of the preference.",
19 | "choices": [
20 | {
21 | "type": "integer"
22 | },
23 | {
24 | "type": "string"
25 | },
26 | {
27 | "type": "boolean"
28 | }
29 | ]
30 | }
31 | ]
32 | },
33 | {
34 | "name": "clear",
35 | "type": "function",
36 | "description": "Removes a value from the legacy pref system.",
37 | "async": true,
38 | "parameters": [
39 | {
40 | "name": "aName",
41 | "type": "string",
42 | "description": "Name of the preference."
43 | }
44 | ]
45 | }
46 | ]
47 | }
48 | ]
49 |
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | async function main() {
2 |
3 | // define default prefs and migrate legacy settings
4 | let defaultPrefs = {
5 | "version": "2.1.1",
6 | "localOnly": true
7 | };
8 | await editEmailSubjectPreferences.setDefaults(defaultPrefs);
9 | await editEmailSubjectPreferences.migrateFromLegacy(defaultPrefs, "extensions.editemailsubject.");
10 |
11 | messenger.menus.create({
12 | contexts : ["message_list"],
13 | id: "edit_email_subject_entry",
14 | onclick : editEmailSubjectMain.edit.bind(editEmailSubjectMain),
15 | title: messenger.i18n.getMessage("lang.menuTitle")
16 | });
17 |
18 | /*messenger.menus.create({
19 | contexts : ["message_list"],
20 | id: "dup2_email_subject_entry",
21 | onclick : editEmailSubjectMain.dup2.bind(editEmailSubjectMain),
22 | title: messenger.i18n.getMessage("lang.menuTitle2")
23 | });*/
24 |
25 | messenger.runtime.onMessage.addListener(editEmailSubjectMain.handleMessage);
26 | browser.commands.onCommand.addListener(async (command) => {
27 | if (command == "edit-action")
28 | {
29 | editEmailSubjectMain.editLink(await messenger.mailTabs.getSelectedMessages());
30 | }
31 | else if (command == "duplicate-action")
32 | {
33 | editEmailSubjectMain.dup2Link(await messenger.mailTabs.getSelectedMessages());
34 | }
35 | });
36 |
37 | }
38 |
39 | main();
40 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "applications": {
3 | "gecko": {
4 | "id": "IMAPNotes@valinet.ro",
5 | "strict_min_version": "78.0"
6 | }
7 | },
8 | "manifest_version": 2,
9 | "name": "IMAP Notes",
10 | "version": "1.2",
11 | "author": "valinet",
12 | "description": "Allows editing IMAP notes created by Apple devices.",
13 | "default_locale": "en-US",
14 | "icons": {
15 | "64": "content/images/editemailsubjectmx-icon-64px.png",
16 | "32": "content/images/editemailsubjectmx-icon-32px.png",
17 | "16": "content/images/editemailsubjectmx-icon-16px.png"
18 | },
19 | "permissions": ["storage", "menus", "messagesRead", "messagesMove", "accountsRead", "messagesDelete"],
20 | "background": {
21 | "scripts": ["content/scripts/preferences.js", "content/editemailsubject.js", "background.js"]
22 | },
23 | "experiment_apis": {
24 | "MessageModification": {
25 | "schema": "api/MessageModification/schema.json",
26 | "parent": {
27 | "scopes": ["addon_parent"],
28 | "paths": [["MessageModification"]],
29 | "script": "api/MessageModification/implementation.js"
30 | }
31 | },
32 | "LegacyPrefs": {
33 | "schema": "api/LegacyPrefs/schema.json",
34 | "parent": {
35 | "scopes": ["addon_parent"],
36 | "paths": [["LegacyPrefs"]],
37 | "script": "api/LegacyPrefs/implementation.js"
38 | }
39 | }
40 | },
41 | "options_ui": {
42 | "page": "/content/options/options.html",
43 | "browser_style": true,
44 | "open_in_tab": true
45 | },
46 | "commands": {
47 | "edit-action": {
48 | "suggested_key": {
49 | "default": "Ctrl+Shift+Z"
50 | },
51 | "description": "Edit IMAP note(s)"
52 | },
53 | "duplicate-action": {
54 | "suggested_key": {
55 | "default": "Ctrl+Shift+X"
56 | },
57 | "description": "Duplicate IMAP note(s)"
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/api/LegacyPrefs/implementation.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable object-shorthand */
2 |
3 | var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
4 | var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
5 |
6 | var LegacyPrefs = class extends ExtensionCommon.ExtensionAPI {
7 | getAPI(context) {
8 |
9 | const PrefTypes = {
10 | [Services.prefs.PREF_STRING] : "string",
11 | [Services.prefs.PREF_INT] : "number",
12 | [Services.prefs.PREF_BOOL] : "boolean",
13 | [Services.prefs.PREF_INVALID] : "invalid"
14 | };
15 |
16 | return {
17 | LegacyPrefs: {
18 |
19 | // get may only return something, if a value is set
20 | get: async function(aName, aDefault) {
21 | let prefType = Services.prefs.getPrefType(aName);
22 | if (prefType == Services.prefs.PREF_INVALID) {
23 | return null;
24 | }
25 |
26 | if (typeof aDefault != PrefTypes[prefType]) {
27 | throw new Error("PrefType of <" + aName + "> is <" + PrefTypes[prefType] + "> and does not match the type of its default value <" + aDefault + "> which is <" + typeof aDefault + ">!");
28 | }
29 |
30 | switch (typeof aDefault) {
31 | case "string":
32 | return Services.prefs.getCharPref(aName, aDefault);
33 |
34 | case "number":
35 | return Services.prefs.getIntPref(aName, aDefault);
36 |
37 | case "boolean":
38 | return Services.prefs.getBoolPref(aName, aDefault);
39 |
40 | default:
41 | throw new Error("Preference <" + aName + "> has an unsupported type <" + typeof aDefault + ">. Allowed are string, number and boolean.");
42 | }
43 |
44 | },
45 |
46 | clear: async function(aName) {
47 | Services.prefs.clearUserPref(aName);
48 | }
49 |
50 | },
51 | };
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/src/content/scripts/locales.js:
--------------------------------------------------------------------------------
1 | /*
2 | license: The MIT License, Copyright (c) 2016-2019 YUKI "Piro" Hiroshi
3 | original: http://github.com/piroor/webextensions-lib-l10n
4 |
5 | */
6 |
7 | (function (addonId, keyPrefix){
8 |
9 | let localization = {
10 | i18n: null,
11 |
12 | updateString(string) {
13 | let re = new RegExp(keyPrefix + "(.+?)__", "g");
14 | return string.replace(re, matched => {
15 | const key = matched.slice(keyPrefix.length, -2);
16 | return messenger.i18n.getMessage(key) || matched;
17 | });
18 | },
19 |
20 | updateSubtree(node) {
21 | const texts = document.evaluate(
22 | 'descendant::text()[contains(self::text(), "' + keyPrefix + '")]',
23 | node,
24 | null,
25 | XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
26 | null
27 | );
28 | for (let i = 0, maxi = texts.snapshotLength; i < maxi; i++) {
29 | const text = texts.snapshotItem(i);
30 | if (text.nodeValue.includes(keyPrefix)) text.nodeValue = this.updateString(text.nodeValue);
31 | }
32 |
33 | const attributes = document.evaluate(
34 | 'descendant::*/attribute::*[contains(., "' + keyPrefix + '")]',
35 | node,
36 | null,
37 | XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
38 | null
39 | );
40 | for (let i = 0, maxi = attributes.snapshotLength; i < maxi; i++) {
41 | const attribute = attributes.snapshotItem(i);
42 | if (attribute.value.includes(keyPrefix)) attribute.value = this.updateString(attribute.value);
43 | }
44 | },
45 |
46 | async updateDocument() {
47 | this.updateSubtree(document);
48 | }
49 | };
50 |
51 | // standard event if loaded by a standard window
52 | document.addEventListener('DOMContentLoaded', () => {
53 | localization.updateDocument();
54 | }, { once: true });
55 |
56 | // custom event, fired by the overlay loader after it has finished loading
57 | document.addEventListener("DOMOverlayLoaded_" + addonId, () => {
58 | localization.updateDocument();
59 | }, { once: true });
60 |
61 | })("EditMailSubject@jcp.convenant", "__EESMSG_");
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IMAP Notes
2 | This is a simple Thunderbird extension (compatible with Thunderbird 78+ as of January 2021) that allows editing notes created, edited and synced from the iPhone Notes app. The notes created in the app are actually saved in a "Notes" folder in your IMAP mail account.
3 |
4 | ## Usage
5 |
6 | Simply right click any message in your "Notes" folder and choose either either "Edit IMAP note", either "Duplicate IMAP note". When editing a note, in order to save changes, press [Ctrl] + [S] on your keyboard. Edits are not yet saved automatically (I cannot figure out what event to listen to in JavaScript to detect the popup window closing and still have the document available - I tried `beforeunload` and it does not work).
7 |
8 | ## Instructions
9 | IMAP Notes releases can be found [here](https://github.com/valinet/IMAPNotes/releases). Each release will list the relevant changes and provides a link to an XPI file to download the add-on.
10 |
11 | _Note: You need to save the XPI file on your computer (using "save as" from the context menu). If you just click on it, it will be installed in your Firefox browser where it will not work of course. The downloaded file can be installed in Thunderbird using the gear menu in the Add-On Manager._
12 |
13 | _Alternatively use Drag&Drop for installing from "Releases" directly to the TB/Addon Manager page. With having both pages open just grap the release in question on github and drag it over to the TB page, release and follow further instructions._
14 |
15 | The download contains 2 XPI files:
16 |
17 | * imapnotes.xpi - this will install the "Edit IMAP note" context menu entry, that allows you to edit a note that you select in a new window
18 | * imapnotes2.xpi - this will intsall the "Duplicate IMAP note" context menu entry which creates a copy of the currently selected note (so that you have a way to create new notes, but of course, the first note has to be created on the phone which will ensure the correct folder is created on the server)
19 |
20 | ## Credits
21 | Based on [EditEmailSubject-MX](https://github.com/cleidigh/EditEmailSubject-MX) extension.
22 |
23 | ## License
24 | [GPL v3](LICENSE)
--------------------------------------------------------------------------------
/src/api/MessageModification/schema.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "namespace": "MessageModification",
4 | "functions": [
5 | {
6 | "name": "setSubjectOfMessage",
7 | "type": "function",
8 | "description": "Set the subject of a Message.",
9 | "async": true,
10 | "parameters": [
11 | {
12 | "name": "aID",
13 | "type": "integer",
14 | "description": "ID of the requested message."
15 | },
16 | {
17 | "name": "aSubject",
18 | "type": "string",
19 | "description": "Subject"
20 | }
21 | ]
22 | },
23 | {
24 | "name": "selectMessage",
25 | "type": "function",
26 | "description": "Select a message in UI.",
27 | "async": true,
28 | "parameters": [
29 | {
30 | "name": "aID",
31 | "type": "integer",
32 | "description": "ID of the requested message."
33 | }
34 | ]
35 | },
36 | {
37 | "name": "getMessageFlags",
38 | "type": "function",
39 | "description": "Get the internal Thunderbird flags of a message",
40 | "async": true,
41 | "parameters": [
42 | {
43 | "name": "aID",
44 | "type": "integer",
45 | "description": "ID of the requested message."
46 | }
47 | ]
48 | },
49 | {
50 | "name": "addRaw",
51 | "type": "function",
52 | "description": "Add a new message.",
53 | "async": true,
54 | "parameters": [
55 | {
56 | "name": "aContent",
57 | "type": "string",
58 | "description": "New raw content"
59 | },
60 | {
61 | "name": "aMailFolder",
62 | "$ref": "folders.MailFolder",
63 | "description": "MailFolder to which the message should be added to."
64 | },
65 | {
66 | "name": "aID",
67 | "type": "integer",
68 | "description": "ID of a message whos flags and keywords should be cloned."
69 | }
70 | ]
71 | }
72 | ]
73 | }
74 | ]
75 |
--------------------------------------------------------------------------------
/scripts/dev-tools/localization/dtd-converter-py/checkJSON.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # /checkJSON.py/ gWahl 2019-12-17/
4 |
5 | import os
6 | import sys
7 | import json
8 |
9 | #------------------------------------------------
10 |
11 | def scan_json(dir):
12 | for name in os.listdir(dir):
13 | path = os.path.join(dir, name)
14 | #print("scann JSON: ", path)
15 | if os.path.isfile(path):
16 | if ('.json' == path[-5:]):
17 | # check the file for correctness
18 | print(" TESTING ", path)
19 | with open(path) as f:
20 | d = json.load(f)
21 |
22 | else:
23 | scan_json(path)
24 |
25 |
26 | if __name__ == "__main__":
27 |
28 | print ("""=== Check all .json files in the directory structure for JSON correctness ===""")
29 |
30 | if (len(sys.argv) == 2) and sys.argv[1] == "--help":
31 | print ("""
32 | Each JSON file is loaded with the python function json.load(f).
33 | Any incorrect json string would throw a python error.
34 |
35 | Example:
36 | Assume the .json file holds:
37 | "rf.options.list.startingday.label": "Startdag fan \'e wike",
38 | The file testing will throw an python error like this:
39 | json.decoder.JSONDecodeError: Invalid \escape: line 1 column 17672 (char 17671)
40 |
41 | More details will give an JSON Validator with:
42 | Error: Parse error on line 306:
43 | ...startingday.label": "Startdag fan \&apos
44 | -----------------------^
45 | Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '[', got 'undefined'
46 |
47 | Note:
48 | If an parse error is found this function will terminate leaving other
49 | files untested! The whole directory structure is only completely
50 | scanned with ALL .json files are correct!
51 |
52 | JSON Validators:
53 | https://jsonlint.com/
54 | http://jsoneditoronline.org/
55 | https://jsonformatter.curiousconcept.com/
56 |
57 | Arguments:
58 | No arguments. Scan tree from '.'
59 | Use --help argument to get this help listing
60 | """)
61 |
62 | exit()
63 |
64 | scan_json('.') #process .json files
65 |
66 | print( """ ------ Done ------------ """)
67 |
--------------------------------------------------------------------------------
/src/content/scripts/preferences.js:
--------------------------------------------------------------------------------
1 | var editEmailSubjectPreferences = {
2 | setDefaults: async function(defaultPrefs) {
3 | // set defaultPrefs in local storage, so we can access them from everywhere
4 | const prefs = Object.keys(defaultPrefs);
5 | for (const pref of prefs) {
6 | await messenger.storage.local.set({ ["pref.default." + pref] : defaultPrefs[pref] });
7 | }
8 | },
9 |
10 | migrateFromLegacy: async function(defaultPrefs, prefBranch) {
11 | const prefs = Object.keys(defaultPrefs);
12 | for (const pref of prefs) {
13 | let legacyValue = await messenger.LegacyPrefs.get(prefBranch + pref, defaultPrefs[pref]);
14 | if (legacyValue !== null) {
15 | console.log("Migrating legacy preference <" + prefBranch + pref + "> = <" + legacyValue + ">.");
16 | await messenger.storage.sync.set({ ["pref.value." + pref] : legacyValue });
17 | await messenger.LegacyPrefs.clear(prefBranch + pref);
18 | }
19 | }
20 | },
21 |
22 | load: async function(document) {
23 | for (let node of document.querySelectorAll("[preference]")) {
24 | if (node.getAttribute("instantApply") == "true") {
25 | node.addEventListener("command", function(event) {editEmailSubjectPreferences.savePref(event.target);});
26 | node.addEventListener("change", function(event) {editEmailSubjectPreferences.savePref(event.target);});
27 | }
28 | this.loadPref(node);
29 | }
30 | },
31 |
32 | save: async function(document) {
33 | for (let node of document.querySelectorAll("[preference]")) {
34 | this.savePref(node);
35 | }
36 | },
37 |
38 | loadPref: async function(node) {
39 | let prefName = node.getAttribute("preference");
40 | let prefValue = await this.getPrefValue(prefName);
41 | let nodeName = node.tagName.toLowerCase().split(":").pop() + (node.hasAttribute("type") ? "." + node.getAttribute("type").toLowerCase() : "");
42 |
43 | // nodename will have the namespace prefix removed and the value of the type attribute (if any) appended
44 | switch (nodeName) {
45 | case "checkbox":
46 | case "input.checkbox":
47 | node.checked = prefValue;
48 | break;
49 |
50 | case "textbox":
51 | case "input.text":
52 | default:
53 | node.setAttribute("value", prefValue);
54 | break;
55 | }
56 | },
57 |
58 | savePref: async function(node) {
59 | let prefName = node.getAttribute("preference");
60 | let nodeName = node.tagName.toLowerCase().split(":").pop() + (node.hasAttribute("type") ? "." + node.getAttribute("type").toLowerCase() : "");
61 |
62 | // nodename will have the namespace prefix removed and the value of the type attribute (if any) appended
63 | switch (nodeName) {
64 | case "checkbox":
65 | case "input.checkbox":
66 | await this.setPrefValue(prefName, node.checked);
67 | break;
68 |
69 | case "textbox":
70 | case "input.text":
71 | default:
72 | await this.setPrefValue(prefName, node.value);
73 | break;
74 | }
75 | },
76 |
77 |
78 |
79 | getPrefValue: async function(aName, aFallback = null) {
80 | let defaultValue = await messenger.storage.local.get({ ["pref.default." + aName] : aFallback });
81 | let value = await messenger.storage.sync.get({ ["pref.value." + aName] : defaultValue["pref.default." + aName] });
82 | return value["pref.value." + aName];
83 | },
84 |
85 | setPrefValue: async function(aName, aValue) {
86 | await messenger.storage.sync.set({ ["pref.value." + aName] : aValue });
87 | }
88 |
89 | };
90 |
--------------------------------------------------------------------------------
/scripts/dev-tools/localization/dtd-converter-py/migrateLocale.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # /migrateLocale.py/ gWahl 2019-12-17/
4 |
5 | import os, sys, json, re, io
6 |
7 | #------------------------------------------------
8 |
9 | def newDir(dir):
10 | if not os.path.exists(dir):
11 | print("Directory doesn't exist. Creating. <" + dir + ">")
12 | os.makedirs(dir)
13 |
14 | def convert(source, destination, current = None):
15 | dir = source if current == None else current
16 | messages = ""
17 |
18 | for name in os.listdir(dir):
19 | path = os.path.join(dir, name)
20 |
21 | if os.path.isfile(path):
22 | if path.endswith('.dtd'):
23 | messages = messages + convert_dtd(path, dir)
24 | if path.endswith('.properties'):
25 | messages = messages + convert_prop(path, dir)
26 |
27 | else:
28 | convert(source, destination, path)
29 |
30 | if (messages):
31 | # map the path from the source into the destination folder
32 | dest = dir.replace(source, destination);
33 | messagesjson = os.path.join(dest, "messages.json")
34 | newDir(dest)
35 |
36 | # check if the messagesjson already exists
37 | oldData = None
38 | if os.path.exists(messagesjson):
39 | with open(messagesjson, "r", encoding='utf-8') as f:
40 | oldData = json.load(f)
41 |
42 | # merge data
43 | newData = json.loads("{" + messages[:-1] + "}")
44 | if oldData:
45 | mergedData = oldData
46 | mergedData.update(newData)
47 | else:
48 | mergedData = newData
49 |
50 | # write pretty printed json file
51 | final = json.dumps(mergedData, indent=4, sort_keys=True, ensure_ascii=False)
52 | with io.open(messagesjson, "w", encoding='utf-8') as f:
53 | f.write(final)
54 |
55 | # check the file for correctness
56 | print(" -> TESTING " + messagesjson)
57 | with open(messagesjson, "r", encoding='utf-8') as f:
58 | d = json.load(f)
59 | #print(d)
60 |
61 |
62 |
63 |
64 | def convert_dtd(path, dir):
65 | print(" CONVERTING <" + path + "> to JSON")
66 |
67 | p = re.compile(r'\s+')
68 | sdtd = ''
69 |
70 | dtd = io.open(path, 'r', encoding='utf-8')
71 | dtdLines = dtd.readlines()
72 |
73 | for line in dtdLines:
74 | sline = line.strip().replace('\r','').replace('\n','')
75 | #print("next line >>" + line + "<<", len(line))
76 |
77 | if sline != '' and sline.find('