mirror of
https://github.com/Ximi1970/systray-x.git
synced 2026-05-07 18:15:28 +02:00
Merge branch 'develop' into feature-unread
This commit is contained in:
27
README.references.txt
Normal file
27
README.references.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
Arguments for <script>
|
||||
https://stackoverflow.com/questions/5292372/how-to-pass-parameters-to-a-script-tag
|
||||
|
||||
|
||||
Loading css using <script>
|
||||
https://stackoverflow.com/questions/574944/how-to-load-up-css-files-using-javascript
|
||||
|
||||
|
||||
Input <input>
|
||||
https://www.w3schools.com/tags/tag_input.asp
|
||||
|
||||
Input types
|
||||
https://www.w3schools.com/tags/att_input_type.asp
|
||||
|
||||
|
||||
Tree view
|
||||
https://www.w3schools.com/howto/howto_js_treeview.asp
|
||||
|
||||
Nested table
|
||||
https://codepen.io/st-iv/pen/xxbRxEj
|
||||
|
||||
|
||||
|
||||
Tree table
|
||||
https://stackoverflow.com/questions/5636375/how-to-create-a-collapsing-tree-table-in-html-css-js
|
||||
http://maxdesign.com.au/articles/tree-table/
|
||||
|
||||
26
webext/_locales/en-US/messages.json
Normal file
26
webext/_locales/en-US/messages.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"extensionName": {
|
||||
"message": "SysTray-X",
|
||||
"description": "Name of the webextension"
|
||||
},
|
||||
|
||||
"extensionDescription": {
|
||||
"message": "System tray control add-on",
|
||||
"description": "Webextension descriptions"
|
||||
},
|
||||
|
||||
"tabWindows": {
|
||||
"message": "Windows",
|
||||
"description": "Tab for Window options"
|
||||
},
|
||||
|
||||
"tabIcon": {
|
||||
"message": "Icon",
|
||||
"description": "Tab for Icon options"
|
||||
},
|
||||
|
||||
"tabMail": {
|
||||
"message": "Mail",
|
||||
"description": "Tab for Mail options"
|
||||
}
|
||||
}
|
||||
102
webext/css/options.css
Normal file
102
webext/css/options.css
Normal file
@@ -0,0 +1,102 @@
|
||||
body {
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
/* Style the tab */
|
||||
.tab {
|
||||
overflow: hidden;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Style the buttons inside the tab */
|
||||
.tab button {
|
||||
background-color: inherit;
|
||||
float: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
transition: 0.3s;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
/* Change background color of buttons on hover */
|
||||
.tab button:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
/* Create an active/current tablink class */
|
||||
.tab button.active {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
/* Style the tab content */
|
||||
.tabcontent {
|
||||
display: none;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* check box tree view */
|
||||
.box {
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none; /* Safari 3.1+ */
|
||||
-moz-user-select: none; /* Firefox 2+ */
|
||||
-ms-user-select: none; /* IE 10+ */
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.box::before {
|
||||
content: "\2610";
|
||||
color: grey;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.check-box::before {
|
||||
content: "\2611";
|
||||
color: dodgerblue;
|
||||
}
|
||||
|
||||
ul,
|
||||
#accountsTree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Remove margins and padding from the parent ul */
|
||||
#accountsTree {
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.caret::before {
|
||||
content: "\25B6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Hide the nested list */
|
||||
.nested {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
|
||||
.active {
|
||||
display: block;
|
||||
}
|
||||
47
webext/js/i18n.js
Normal file
47
webext/js/i18n.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
license: The MIT License, Copyright (c) 2016-2018 YUKI "Piro" Hiroshi
|
||||
original:
|
||||
http://github.com/piroor/webextensions-lib-l10n
|
||||
*/
|
||||
var i18n = {
|
||||
updateString(aString) {
|
||||
return aString.replace(/__MSG_(.+?)__/g, aMatched => {
|
||||
const key = aMatched.slice(6, -2);
|
||||
return browser.i18n.getMessage(key) || aMatched;
|
||||
});
|
||||
},
|
||||
|
||||
updateDocument() {
|
||||
const texts = document.evaluate(
|
||||
'descendant::text()[contains(self::text(), "__MSG_")]',
|
||||
document,
|
||||
null,
|
||||
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
||||
null
|
||||
);
|
||||
for (let i = 0, maxi = texts.snapshotLength; i < maxi; i++) {
|
||||
const text = texts.snapshotItem(i);
|
||||
text.nodeValue = this.updateString(text.nodeValue);
|
||||
}
|
||||
|
||||
const attributes = document.evaluate(
|
||||
'descendant::*/attribute::*[contains(., "__MSG_")]',
|
||||
document,
|
||||
null,
|
||||
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
||||
null
|
||||
);
|
||||
for (let i = 0, maxi = attributes.snapshotLength; i < maxi; i++) {
|
||||
const attribute = attributes.snapshotItem(i);
|
||||
attribute.value = this.updateString(attribute.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
i18n.updateDocument();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
125
webext/js/options_accounts.js
Normal file
125
webext/js/options_accounts.js
Normal file
@@ -0,0 +1,125 @@
|
||||
var SysTrayX = {};
|
||||
|
||||
SysTrayX.Accounts = {
|
||||
initialized: false,
|
||||
|
||||
init: function() {
|
||||
if (this.initialized) {
|
||||
console.log("Accounts already initialized");
|
||||
return;
|
||||
}
|
||||
console.log("Enabling Accounts");
|
||||
|
||||
this.getAccounts().then(this.getAccountsCb);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
/*
|
||||
* Use the messages API to get the unread messages (Promise)
|
||||
* Be aware that the data is only avaiable inside the callback
|
||||
*/
|
||||
getAccounts: async function() {
|
||||
return await browser.accounts.list();
|
||||
},
|
||||
|
||||
/*
|
||||
* Callback for getAccounts
|
||||
*/
|
||||
getAccountsCb: function(mailAccount) {
|
||||
let accounts = new Object();
|
||||
let i;
|
||||
|
||||
for (i = 0; i < mailAccount.length; i++) {
|
||||
console.debug("SysTrayX accounts id: " + mailAccount[i].id);
|
||||
console.debug("SysTrayX accounts name: " + mailAccount[i].name);
|
||||
console.debug("SysTrayX accounts type: " + mailAccount[i].type);
|
||||
|
||||
if (!accounts[mailAccount[i].type]) {
|
||||
accounts[mailAccount[i].type] = [];
|
||||
}
|
||||
accounts[mailAccount[i].type].push({
|
||||
id: mailAccount[i].id,
|
||||
name: mailAccount[i].name
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Build tree
|
||||
*/
|
||||
|
||||
// Get base
|
||||
let treeBase = document.getElementById("accountsTree");
|
||||
|
||||
for (let prop in accounts) {
|
||||
let typeLi = document.createElement("li");
|
||||
|
||||
let typeSpan = document.createElement("span");
|
||||
if (accounts[prop]) {
|
||||
typeSpan.setAttribute("class", "caret");
|
||||
}
|
||||
let typeText = document.createTextNode(prop);
|
||||
typeSpan.appendChild(typeText);
|
||||
typeLi.appendChild(typeSpan);
|
||||
|
||||
if (accounts[prop]) {
|
||||
let typeUl = document.createElement("ul");
|
||||
typeUl.setAttribute("class", "nested");
|
||||
|
||||
for (let i = 0; i < accounts[prop].length; ++i) {
|
||||
let typeLi = document.createElement("li");
|
||||
let typeInput = document.createElement("input");
|
||||
typeInput.setAttribute("type", "checkbox");
|
||||
typeInput.setAttribute("name", accounts[prop][i].name);
|
||||
typeInput.setAttribute("value", accounts[prop][i].id);
|
||||
typeInput.setAttribute("checked", "true");
|
||||
typeLi.appendChild(typeInput);
|
||||
let typeText = document.createTextNode(" " + accounts[prop][i].name);
|
||||
typeLi.appendChild(typeText);
|
||||
typeUl.appendChild(typeLi);
|
||||
}
|
||||
typeLi.appendChild(typeUl);
|
||||
}
|
||||
|
||||
treeBase.appendChild(typeLi);
|
||||
|
||||
// Restore saved selection
|
||||
function setAccounts(result) {
|
||||
console.debug("Restore account selection");
|
||||
|
||||
let treeBase = document.getElementById("accountsTree");
|
||||
let accounts = result.accounts || [];
|
||||
for (let i = 0; i < accounts.length; ++i) {
|
||||
let checkbox = treeBase.querySelector(
|
||||
`input[value=${accounts[i].id}]`
|
||||
);
|
||||
if (checkbox) {
|
||||
checkbox.checked = accounts[i].checked;
|
||||
}
|
||||
}
|
||||
|
||||
console.debug("Restore account selection done");
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
console.log(`GetAccounts Error: ${error}`);
|
||||
}
|
||||
|
||||
let getAccounts = browser.storage.sync.get("accounts");
|
||||
getAccounts.then(setAccounts, onError);
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate tree
|
||||
*/
|
||||
let toggler = document.getElementsByClassName("caret");
|
||||
for (let i = 0; i < toggler.length; i++) {
|
||||
toggler[i].addEventListener("click", function() {
|
||||
this.parentElement.querySelector(".nested").classList.toggle("active");
|
||||
this.classList.toggle("caret-down");
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SysTrayX.Accounts.init();
|
||||
25
webext/js/options_tabbutton.js
Normal file
25
webext/js/options_tabbutton.js
Normal file
@@ -0,0 +1,25 @@
|
||||
function openTab(evt) {
|
||||
var i, tabcontent, tablinks, tabName;
|
||||
tabName = evt.currentTarget.id;
|
||||
|
||||
tabcontent = document.getElementsByClassName("tabcontent");
|
||||
for (i = 0; i < tabcontent.length; i++) {
|
||||
tabcontent[i].style.display = "none";
|
||||
}
|
||||
tablinks = document.getElementsByClassName("tablinks");
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
|
||||
let el = document.getElementById(tabName + "Content");
|
||||
el.style.display = "block";
|
||||
|
||||
evt.currentTarget.className += " active";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
let tablinks = document.getElementsByClassName("tablinks");
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
document.getElementById(tablinks[i].id).addEventListener("click", openTab);
|
||||
}
|
||||
});
|
||||
7
webext/js/styles.js
Normal file
7
webext/js/styles.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var stylesheet = document.currentScript.getAttribute("stylesheet");
|
||||
document
|
||||
.getElementsByTagName("head")[0]
|
||||
.insertAdjacentHTML(
|
||||
"beforeend",
|
||||
`<link rel=\"stylesheet\" href=${stylesheet} />`
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "SysTray-X",
|
||||
"description": "System tray control add-on",
|
||||
"name": "__MSG_extensionName__",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"version": "0.1",
|
||||
"author": "Maxime Rijnders",
|
||||
"homepage_url": "https://github.com/Ximi1970/systray-x",
|
||||
@@ -17,11 +17,15 @@
|
||||
"48": "icons/message.svg"
|
||||
},
|
||||
|
||||
"permissions": [
|
||||
"messagesRead"
|
||||
],
|
||||
|
||||
"default_locale": "en-US",
|
||||
|
||||
"permissions": ["accountsRead", "messagesRead", "storage"],
|
||||
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
|
||||
"options_ui": {
|
||||
"page": "options.html"
|
||||
}
|
||||
}
|
||||
|
||||
60
webext/options.html
Normal file
60
webext/options.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<script src="js/styles.js" stylesheet="css/options.css"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
|
||||
<script src="js/options_accounts.js"></script>
|
||||
|
||||
<div class="tab">
|
||||
<button class="tablinks active" id="Windows">__MSG_tabWindows__</button>
|
||||
<button class="tablinks" id="Icon">__MSG_tabIcon__</button>
|
||||
<button class="tablinks" id="Mail">__MSG_tabMail__</button>
|
||||
</div>
|
||||
|
||||
<div id="WindowsContent" class="tabcontent" style="display:block">
|
||||
<form>
|
||||
<h3>Windows</h3>
|
||||
<p>Windows options here</p>
|
||||
|
||||
<p>Please select your opton:</p>
|
||||
<input type="radio" name="options_test" value="Option1" /> Option 1<br />
|
||||
<input type="radio" name="options_test" value="Option2" /> Option 2<br />
|
||||
<input type="radio" name="options_test" value="Option3" /> Option 3<br />
|
||||
|
||||
<p>Please check the boxes:</p>
|
||||
<input type="checkbox" name="check1" value="Check1" /> Check 1<br />
|
||||
<input type="checkbox" name="check2" value="Check2" /> Check 2<br />
|
||||
<input type="checkbox" name="check3" value="Check3" /> Check 3<br />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="IconContent" class="tabcontent">
|
||||
<form>
|
||||
<h3>Icon</h3>
|
||||
<p>Icon options here.</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="MailContent" class="tabcontent">
|
||||
<form>
|
||||
<h3>Included accounts</h3>
|
||||
|
||||
<ul id="accountsTree"></ul>
|
||||
</form>
|
||||
</div>
|
||||
<br />
|
||||
<form name="saveform">
|
||||
<label>Save preferences</label>
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
|
||||
<script src="js/options_tabbutton.js"></script>
|
||||
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
76
webext/options.js
Normal file
76
webext/options.js
Normal file
@@ -0,0 +1,76 @@
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
|
||||
console.debug("Save preferences");
|
||||
|
||||
browser.storage.sync.set({
|
||||
optionsRadioTest: document.querySelector(
|
||||
'input[name="options_test"]:checked'
|
||||
).value,
|
||||
optionsCheck1: document.querySelector('input[name="check1"]').checked,
|
||||
optionsCheck2: document.querySelector('input[name="check2"]').checked,
|
||||
optionsCheck3: document.querySelector('input[name="check3"]').checked
|
||||
});
|
||||
|
||||
/*
|
||||
* Get accounts
|
||||
*/
|
||||
|
||||
console.debug("Store accounts");
|
||||
|
||||
let treeBase = document.getElementById("accountsTree");
|
||||
let inputs = treeBase.querySelectorAll("input");
|
||||
let accounts = [];
|
||||
for (let i = 0; i < inputs.length; ++i) {
|
||||
accounts.push({
|
||||
id: inputs[i].value,
|
||||
name: inputs[i].name,
|
||||
checked: inputs[i].checked
|
||||
});
|
||||
}
|
||||
|
||||
// Store accounts
|
||||
browser.storage.sync.set({
|
||||
accounts: accounts
|
||||
});
|
||||
|
||||
console.debug("Store accounts done");
|
||||
}
|
||||
|
||||
function restoreOptions() {
|
||||
console.debug("Restore preferences");
|
||||
|
||||
function setCurrentRadioChoice(result) {
|
||||
let selector = result.optionsRadioTest || "Option1";
|
||||
let radioButton = document.querySelector(`[value=${selector}]`);
|
||||
radioButton.checked = true;
|
||||
}
|
||||
|
||||
function setCurrentCheckChoice(result) {
|
||||
let checkbox1 = document.querySelector('[name="check1"]');
|
||||
checkbox1.checked = result.optionsCheck1 || false;
|
||||
let checkbox2 = document.querySelector('[name="check2"]');
|
||||
checkbox2.checked = result.optionsCheck2 || false;
|
||||
let checkbox3 = document.querySelector('[name="check3"]');
|
||||
checkbox3.checked = result.optionsCheck3 || false;
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
|
||||
var getting = browser.storage.sync.get("optionsRadioTest");
|
||||
getting.then(setCurrentRadioChoice, onError);
|
||||
|
||||
var getting = browser.storage.sync.get([
|
||||
"optionsCheck1",
|
||||
"optionsCheck2",
|
||||
"optionsCheck3"
|
||||
]);
|
||||
getting.then(setCurrentCheckChoice, onError);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", restoreOptions);
|
||||
document
|
||||
.querySelector('[name="saveform"]')
|
||||
.addEventListener("submit", saveOptions);
|
||||
Reference in New Issue
Block a user