Integrate TinyWebEx modules/templates
12
.gitmodules
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[submodule "src/common/modules/AutomaticSettings"]
|
||||||
|
path = src/common/modules/AutomaticSettings
|
||||||
|
url = https://github.com/TinyWebEx/AutomaticSettings
|
||||||
|
[submodule "src/common/modules/MessageHandler"]
|
||||||
|
path = src/common/modules/MessageHandler
|
||||||
|
url = https://github.com/TinyWebEx/MessageHandler
|
||||||
|
[submodule "src/common/modules/Localizer"]
|
||||||
|
path = src/common/modules/Localizer
|
||||||
|
url = https://github.com/TinyWebEx/Localizer
|
||||||
|
[submodule "src/common/modules/AddonSettings"]
|
||||||
|
path = src/common/modules/AddonSettings
|
||||||
|
url = https://github.com/TinyWebEx/AddonSettings
|
|
@ -0,0 +1,5 @@
|
||||||
|
@ENT8R
|
||||||
|
|
||||||
|
translations:
|
||||||
|
German: @rugk
|
||||||
|
Turkish: Ömür Turan (@omurturan)
|
|
@ -9,7 +9,66 @@
|
||||||
"description": "Name of the extension."
|
"description": "Name of the extension."
|
||||||
},
|
},
|
||||||
"extensionDescription": {
|
"extensionDescription": {
|
||||||
"message": "Vereinfacht das Folgen und das Interagieren mit Nutzern auf anderen Instanzen.",
|
"message": "Vereinfacht das Folgen und Interagieren mit Nutzern auf anderen Instanzen.",
|
||||||
"description": "Description of the extension."
|
"description": "Description of the extension."
|
||||||
|
},
|
||||||
|
|
||||||
|
// errors or other messages (mostly for settings)
|
||||||
|
"errorShowingMessage": {
|
||||||
|
"message": "Konnte Nachricht nicht anzeigen.",
|
||||||
|
"description": "When there is an error when showing the error/info/…."
|
||||||
|
},
|
||||||
|
"couldNotLoadOptions": {
|
||||||
|
"message": "Konnte Einstellungen nicht laden.",
|
||||||
|
"description": "When one or all settings could not be loaded."
|
||||||
|
},
|
||||||
|
"couldNotSaveOption": {
|
||||||
|
"message": "Konnte diese Einstellung nicht speichern.",
|
||||||
|
"description": "When a setting could not be saved."
|
||||||
|
},
|
||||||
|
"messageUndoButton": {
|
||||||
|
"message": "Rückgängig machen",
|
||||||
|
"description": "The text of a button that undoes the last action."
|
||||||
|
},
|
||||||
|
"couldNotUndoAction": {
|
||||||
|
"message": "Konnte Aktion nicht rückgängig machen.",
|
||||||
|
"description": "Shown when an action cannot be undone."
|
||||||
|
},
|
||||||
|
"resettingOptionsWorked": {
|
||||||
|
"message": "Alle Einstellungen benutzen nun wieder die Standardwerte!",
|
||||||
|
"description": "The message shown, when the options of the settings were reset."
|
||||||
|
},
|
||||||
|
"resettingOptionsFailed": {
|
||||||
|
"message": "Konnte Optionen nicht zurück setzen!",
|
||||||
|
"description": "The message shown, when the options of the settings could not have been reset."
|
||||||
|
},
|
||||||
|
|
||||||
|
// options
|
||||||
|
"someSettingsAreManaged": {
|
||||||
|
"message": "Einige Einstellung werden von deinem Systemadministrator festgelegt und können nicht geändert werden.",
|
||||||
|
"description": "The message, which appears, when settings are pre-defined (as managed options) by administrators."
|
||||||
|
},
|
||||||
|
"optionIsDisabledBecauseManaged": {
|
||||||
|
"message": "Diese option ist deaktiviert, weil sie von deinem Systemadministrator festgelegt wurde.",
|
||||||
|
"description": "The title (tooltip) shown, when hovering over a disabled, managed option."
|
||||||
|
},
|
||||||
|
"optionLearnMore": {
|
||||||
|
"message": "Weitere Informationen",
|
||||||
|
"description": "When a link to an explainer needs to be added, this is the link text."
|
||||||
|
},
|
||||||
|
"optionsResetButton": {
|
||||||
|
"message": "Setze alle Einstellungen auf Standardwerte zurück",
|
||||||
|
"description": "The button to delete all current settings and load the defaults, shown in the add-on settings."
|
||||||
|
},
|
||||||
|
|
||||||
|
"optionMastodonHandle": {
|
||||||
|
"message": "Dein Mastodon-Handle: ",
|
||||||
|
"description": "This is an option shown in the add-on settings. You can enter your handle in the form mastodon@server.com there."
|
||||||
|
},
|
||||||
|
|
||||||
|
// ARIA labels/descriptions
|
||||||
|
"dismissIconDescription": {
|
||||||
|
"message": "Diese Nachricht schließen",
|
||||||
|
"description": "the label for the close button of the message box"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,64 @@
|
||||||
"extensionDescription": {
|
"extensionDescription": {
|
||||||
"message": "Simplifies following or interacting with other users on remote instances.",
|
"message": "Simplifies following or interacting with other users on remote instances.",
|
||||||
"description": "Description of the extension."
|
"description": "Description of the extension."
|
||||||
|
},
|
||||||
|
|
||||||
|
// errors or other messages (mostly for settings)
|
||||||
|
"errorShowingMessage": {
|
||||||
|
"message": "Could not show this message.",
|
||||||
|
"description": "When there is an error when showing the error/info/…."
|
||||||
|
},
|
||||||
|
"couldNotLoadOptions": {
|
||||||
|
"message": "Could not load settings.",
|
||||||
|
"description": "When one or all settings could not be loaded."
|
||||||
|
},
|
||||||
|
"couldNotSaveOption": {
|
||||||
|
"message": "Could not save this setting.",
|
||||||
|
"description": "When a setting could not be saved."
|
||||||
|
},
|
||||||
|
"messageUndoButton": {
|
||||||
|
"message": "Undo",
|
||||||
|
"description": "The text of a button that undoes the last action."
|
||||||
|
},
|
||||||
|
"couldNotUndoAction": {
|
||||||
|
"message": "Could not undo action.",
|
||||||
|
"description": "Shown when an action cannot be undone."
|
||||||
|
},
|
||||||
|
"resettingOptionsWorked": {
|
||||||
|
"message": "All settings are now back to defaults again!",
|
||||||
|
"description": "The message shown, when the options of the settings were reset."
|
||||||
|
},
|
||||||
|
"resettingOptionsFailed": {
|
||||||
|
"message": "Could not reset options!",
|
||||||
|
"description": "The message shown, when the options of the settings could not have been reset."
|
||||||
|
},
|
||||||
|
|
||||||
|
// options
|
||||||
|
"someSettingsAreManaged": {
|
||||||
|
"message": "Some settings are managed by your system administrator and cannot be changed.",
|
||||||
|
"description": "The message, which appears, when settings are pre-defined (as managed options) by administrators."
|
||||||
|
},
|
||||||
|
"optionIsDisabledBecauseManaged": {
|
||||||
|
"message": "This option is disabled, because it has been configured by your system administrator.",
|
||||||
|
"description": "The title (tooltip) shown, when hovering over a disabled, managed option."
|
||||||
|
},
|
||||||
|
"optionLearnMore": {
|
||||||
|
"message": "Learn more",
|
||||||
|
"description": "When a link to an explainer needs to be added, this is the link text."
|
||||||
|
},
|
||||||
|
"optionsResetButton": {
|
||||||
|
"message": "Reset all settings to defaults",
|
||||||
|
"description": "The button to delete all current settings and load the defaults, shown in the add-on settings."
|
||||||
|
},
|
||||||
|
|
||||||
|
"optionMastodonHandle": {
|
||||||
|
"message": "Your Mastodon handle: ",
|
||||||
|
"description": "This is an option shown in the add-on settings. You can enter your handle in the form mastodon@server.com there."
|
||||||
|
},
|
||||||
|
|
||||||
|
// ARIA labels/descriptions
|
||||||
|
"dismissIconDescription": {
|
||||||
|
"message": "Close this message",
|
||||||
|
"description": "the label for the close button of the message box"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
src/_locales/tr/messages.json
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
// manifest.json
|
||||||
|
|
||||||
|
// errors or other messages (mostly for settings)
|
||||||
|
"errorShowingMessage": {
|
||||||
|
"message": "Mesaj görüntülenemedi.",
|
||||||
|
"description": "When there is an error when showing the error/info/…."
|
||||||
|
},
|
||||||
|
"couldNotLoadOptions": {
|
||||||
|
"message": "Ayarlar yüklenemedi.",
|
||||||
|
"description": "When one or all settings could not be loaded."
|
||||||
|
},
|
||||||
|
"couldNotSaveOption": {
|
||||||
|
"message": "Ayarlar kaydedilemedi.",
|
||||||
|
"description": "When a setting could not be saved."
|
||||||
|
},
|
||||||
|
"messageUndoButton": {
|
||||||
|
"message": "Geri al",
|
||||||
|
"description": "The text of a button that undoes the last action."
|
||||||
|
},
|
||||||
|
"couldNotUndoAction": {
|
||||||
|
"message": "Geri alma işlemi başarısız.",
|
||||||
|
"description": "Shown when an action cannot be undone."
|
||||||
|
},
|
||||||
|
"resettingOptionsWorked": {
|
||||||
|
"message": "Bütün ayarlar varsayılan değerlere döndürüldü!",
|
||||||
|
"description": "The message shown, when the options of the settings were reset."
|
||||||
|
},
|
||||||
|
"resettingOptionsFailed": {
|
||||||
|
"message": "Seçenekleri sıfırlama başarısız oldu!",
|
||||||
|
"description": "The message shown, when the options of the settings could not have been reset."
|
||||||
|
},
|
||||||
|
|
||||||
|
// options
|
||||||
|
"someSettingsAreManaged": {
|
||||||
|
"message": "Bazı ayarlar sistem yöneticin tarafından düzenlendiği için değiştirilemez.",
|
||||||
|
"description": "The message, which appears, when settings are pre-defined (as managed options) by administrators."
|
||||||
|
},
|
||||||
|
"optionIsDisabledBecauseManaged": {
|
||||||
|
"message": "Bu özellik sistem yöneticin tarafından ayarlandığı için devredışı.",
|
||||||
|
"description": "The title (tooltip) shown, when hovering over a disabled, managed option."
|
||||||
|
},
|
||||||
|
"optionLearnMore": {
|
||||||
|
"message": "Daha fazla bilgi",
|
||||||
|
"description": "When a link to an explainer needs to be added, this is the link text."
|
||||||
|
},
|
||||||
|
"optionsResetButton": {
|
||||||
|
"message": "Bütün ayarları varsayılanlara sıfırla",
|
||||||
|
"description": "The button to delete all current settings and load the defaults, shown in the add-on settings."
|
||||||
|
},
|
||||||
|
|
||||||
|
// ARIA labels/descriptions
|
||||||
|
"dismissIconDescription": {
|
||||||
|
"message": "Bu mesajı kapat",
|
||||||
|
"description": "the label for the close button of the message box"
|
||||||
|
}
|
||||||
|
}
|
257
src/common/common.css
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
@import url("./variables.css");
|
||||||
|
|
||||||
|
body {
|
||||||
|
direction: __MSG_@@bidi_dir__;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://design.firefox.com/photon/components/links.html */
|
||||||
|
a {
|
||||||
|
color: var(--blue-60);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus {
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 0 2px var(--blue-50), 0 0 0 6px var(--blue-50-a30);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
color: var(--blue-70);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* external link symbol */
|
||||||
|
/* currently disabled, because it is not clear what an external link is, in our case */
|
||||||
|
/*a:not([class])[href*="//"]::after {
|
||||||
|
background-image: url(/common/img/open-in-new.svg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
margin: -.3rem .15rem 0 .25rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 16px;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/* small classes in order to avoid inline CSS */
|
||||||
|
.invisible {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* buttons https://design.firefox.com/photon/components/buttons.html */
|
||||||
|
.micro-button {
|
||||||
|
min-height: 24px;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
|
||||||
|
/* not documented, but looks ugly otherwise */
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
/* do not break over multiple lines */
|
||||||
|
/* white-space: nowrap; */
|
||||||
|
height: auto; /* currently, we rather prefer breaking until https://github.com/rugk/offline-qr-code/issues/12 is done */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use light color for dark backgrounds */
|
||||||
|
.micro-button:hover.success,
|
||||||
|
.micro-button:active.success,
|
||||||
|
.micro-button:hover.warning,
|
||||||
|
.micro-button:active.warning,
|
||||||
|
.micro-button:hover.error,
|
||||||
|
.micro-button:active.error {
|
||||||
|
color: var(--white-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.micro-button.info {
|
||||||
|
background-color: var(--grey-90-a10);
|
||||||
|
}
|
||||||
|
.micro-button:hover.info {
|
||||||
|
background-color: var(--grey-90-a20);
|
||||||
|
}
|
||||||
|
.micro-button:active.info {
|
||||||
|
background-color: var(--grey-90-a30);
|
||||||
|
}
|
||||||
|
|
||||||
|
.micro-button.success {
|
||||||
|
background-color: var(--green-60);
|
||||||
|
}
|
||||||
|
.micro-button:hover.success {
|
||||||
|
background-color: var(--green-70);
|
||||||
|
}
|
||||||
|
.micro-button:active.success {
|
||||||
|
background-color: var(--green-80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.micro-button.warning {
|
||||||
|
background-color: var(--yellow-60);
|
||||||
|
}
|
||||||
|
.micro-button:hover.warning {
|
||||||
|
background-color: var(--yellow-70);
|
||||||
|
}
|
||||||
|
.micro-button:active.warning {
|
||||||
|
background-color: var(--yellow-80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.micro-button.error {
|
||||||
|
background-color: var(--red-70);
|
||||||
|
color: var(--white-100);
|
||||||
|
}
|
||||||
|
.micro-button:hover.error {
|
||||||
|
background-color: var(--red-80);
|
||||||
|
}
|
||||||
|
.micro-button:active.error {
|
||||||
|
background-color: var(--red-90);
|
||||||
|
}
|
||||||
|
|
||||||
|
.micro-button:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0a84ff inset, 0 0 0 1px #0a84ff, 0 0 0 4px rgba(10, 132, 255, 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* message box */
|
||||||
|
/* follows https://design.firefox.com/photon/components/message-bars.html */
|
||||||
|
.message-box {
|
||||||
|
padding: 4px;
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
/* use whole width */
|
||||||
|
width: 100%;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
/* make errors selectable, so users can copy them */
|
||||||
|
-moz-user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
|
||||||
|
/* multiline */
|
||||||
|
hypens: auto;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
|
/* center-vertically */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
/* fade-in transition */
|
||||||
|
/* follow https://design.firefox.com/photon/motion/duration-and-easing.html */
|
||||||
|
opacity: 1;
|
||||||
|
max-height: 100px;
|
||||||
|
|
||||||
|
transition: opacity 150ms cubic-bezier(.07,.95,0,1),
|
||||||
|
max-height 200ms cubic-bezier(.07,.95,0,1);
|
||||||
|
}
|
||||||
|
.message-box.fade-hide {
|
||||||
|
max-height: 0px;
|
||||||
|
opacity: 0;
|
||||||
|
min-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add margin when messages are stacked on each other */
|
||||||
|
.message-box:not(.invisible) ~ .message-box:not(.invisible) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: var(--white-100);
|
||||||
|
background-color: var(--red-60);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: var(--grey-90);
|
||||||
|
background-color: var(--grey-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: var(--green-90);
|
||||||
|
background-color: var(--green-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: var(--yellow-90);
|
||||||
|
background-color: var(--yellow-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* message box action button */
|
||||||
|
.message-action-button {
|
||||||
|
margin-left: 8px;
|
||||||
|
|
||||||
|
/* center vertially */
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
|
||||||
|
/* some minimum margin to dismiss button or similar */
|
||||||
|
margin-right: 4px;
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
color: var(--grey-90);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* icons for the message boxes */
|
||||||
|
.message-box::before {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
/* fixed size */
|
||||||
|
background-size: 16px 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
min-width: 16px;
|
||||||
|
min-height: 16px;
|
||||||
|
|
||||||
|
content: "";
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error::before {
|
||||||
|
background-image: url('/common/img/error-white.svg');
|
||||||
|
}
|
||||||
|
.info::before {
|
||||||
|
background-image: url('/common/img/info-dark.svg');
|
||||||
|
}
|
||||||
|
.success::before {
|
||||||
|
background-image: url('/common/img/check.svg');
|
||||||
|
}
|
||||||
|
.warning::before {
|
||||||
|
background-image: url('/common/img/warning-dark.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dismiss {
|
||||||
|
box-sizing: content-box;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
/* some animation on hover */
|
||||||
|
transition: background-color 150ms cubic-bezier(.07,.95,0,1);
|
||||||
|
}
|
||||||
|
.icon-dismiss:hover {
|
||||||
|
background-color: var(--grey-90-a10);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.icon-dismiss:active {
|
||||||
|
background-color: var(--grey-90-a20);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.icon-dismiss:focus {
|
||||||
|
box-shadow: 0 0 0 1px var(--blue-50) inset, 0 0 0 1px var(--blue-50), 0 0 0 4px var(--blue-50-a30);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
9
src/common/common.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Just load/does common stuff.
|
||||||
|
*
|
||||||
|
* @module common
|
||||||
|
* @requires modules/Localizer
|
||||||
|
*/
|
||||||
|
|
||||||
|
// by default translate whole site
|
||||||
|
import "/common/modules/Localizer/Localizer.js";
|
6
src/common/img/check.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="context-fill" d="M6 14a1 1 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.639 1.146l-7 10a1 1 0 0 1-.732.427A.863.863 0 0 1 6 14z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 488 B |
6
src/common/img/close-white.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="rgba(249, 249, 250, .8)" d="M9.061 8l3.47-3.47a.75.75 0 0 0-1.061-1.06L8 6.939 4.53 3.47a.75.75 0 1 0-1.06 1.06L6.939 8 3.47 11.47a.75.75 0 1 0 1.06 1.06L8 9.061l3.47 3.47a.75.75 0 0 0 1.06-1.061z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 523 B |
6
src/common/img/close.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="context-fill" d="M9.061 8l3.47-3.47a.75.75 0 0 0-1.061-1.06L8 6.939 4.53 3.47a.75.75 0 1 0-1.06 1.06L6.939 8 3.47 11.47a.75.75 0 1 0 1.06 1.06L8 9.061l3.47 3.47a.75.75 0 0 0 1.06-1.061z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 512 B |
6
src/common/img/error-white.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 0a8 8 0 1 0 8 8 8 8 0 0 0-8-8zM7 5a1 1 0 0 1 2 0v3a1 1 0 0 1-2 0zm1 7.19A1.19 1.19 0 1 1 9.19 11 1.19 1.19 0 0 1 8 12.19z" fill="#fff"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 437 B |
6
src/common/img/info-dark.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#333" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 531 B |
6
src/common/img/info-light.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="rgba(249, 249, 250, .8)" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 550 B |
7
src/common/img/open-in-new.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#0060df" d="M5 1H4a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3v-1a1 1 0 0 0-2 0v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h1a1 1 0 1 0 0-2z"></path>
|
||||||
|
<path fill="#0060df" d="M14.935 1.618A1 1 0 0 0 14.012 1h-5a1 1 0 1 0 0 2h2.586L8.305 6.293A1 1 0 1 0 9.72 7.707l3.293-3.293V7a1 1 0 1 0 2 0V2a1 1 0 0 0-.077-.382z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 648 B |
6
src/common/img/warning-dark.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path fill="#333" d="M14.742 12.106L9.789 2.2a2 2 0 0 0-3.578 0l-4.953 9.91A2 2 0 0 0 3.047 15h9.905a2 2 0 0 0 1.79-2.894zM7 5a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0zm1 8.25A1.25 1.25 0 1 1 9.25 12 1.25 1.25 0 0 1 8 13.25z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 528 B |
1
src/common/modules/AddonSettings
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 57bb908833485bb34cd03f9b4976e4aeee4f4463
|
1
src/common/modules/AutomaticSettings
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 73534db51b389a4e20cab3ed6801fd02cc405c0b
|
1
src/common/modules/Localizer
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1aca1fb9b5dd7f872002e1fe168b9926c80c6f9c
|
1
src/common/modules/MessageHandler
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 9dc729591ea410f1c3108df8afadba5f26050fb0
|
9
src/common/modules/data/DefaultSettings.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Specifies the default settings of the add-on.
|
||||||
|
*
|
||||||
|
* @module data/DefaultSettings
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const DEFAULT_SETTINGS = Object.freeze({
|
||||||
|
debugMode: false,
|
||||||
|
});
|
20
src/common/modules/data/MessageLevel.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Contains a static object for messages.
|
||||||
|
*
|
||||||
|
* @module /common/modules/data/MessageLevel
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the message level to use,
|
||||||
|
*
|
||||||
|
* @readonly
|
||||||
|
* @enum {int}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
export const MESSAGE_LEVEL = Object.freeze({
|
||||||
|
"ERROR": 3,
|
||||||
|
"WARN": 2,
|
||||||
|
"INFO": 1,
|
||||||
|
"LOADING": -2,
|
||||||
|
"SUCCESS": -3
|
||||||
|
});
|
39
src/common/modules/lodash/.internal/baseGetTag.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
const objectProto = Object.prototype
|
||||||
|
const hasOwnProperty = objectProto.hasOwnProperty
|
||||||
|
const toString = objectProto.toString
|
||||||
|
const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base implementation of `getTag` without fallbacks for buggy environments.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} value The value to query.
|
||||||
|
* @returns {string} Returns the `toStringTag`.
|
||||||
|
*/
|
||||||
|
function baseGetTag(value) {
|
||||||
|
if (value == null) {
|
||||||
|
return value === undefined ? '[object Undefined]' : '[object Null]'
|
||||||
|
}
|
||||||
|
if (!(symToStringTag && symToStringTag in Object(value))) {
|
||||||
|
return toString.call(value)
|
||||||
|
}
|
||||||
|
const isOwn = hasOwnProperty.call(value, symToStringTag)
|
||||||
|
const tag = value[symToStringTag]
|
||||||
|
let unmasked = false
|
||||||
|
try {
|
||||||
|
value[symToStringTag] = undefined
|
||||||
|
unmasked = true
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const result = toString.call(value)
|
||||||
|
if (unmasked) {
|
||||||
|
if (isOwn) {
|
||||||
|
value[symToStringTag] = tag
|
||||||
|
} else {
|
||||||
|
delete value[symToStringTag]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export default baseGetTag
|
4
src/common/modules/lodash/.internal/freeGlobal.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/** Detect free variable `global` from Node.js. */
|
||||||
|
const freeGlobal = typeof global == 'object' && global !== null && global.Object === Object && global
|
||||||
|
|
||||||
|
export default freeGlobal
|
51
src/common/modules/lodash/.internal/getTag.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import baseGetTag from './baseGetTag.js'
|
||||||
|
|
||||||
|
/** `Object#toString` result references. */
|
||||||
|
const dataViewTag = '[object DataView]'
|
||||||
|
const mapTag = '[object Map]'
|
||||||
|
const objectTag = '[object Object]'
|
||||||
|
const promiseTag = '[object Promise]'
|
||||||
|
const setTag = '[object Set]'
|
||||||
|
const weakMapTag = '[object WeakMap]'
|
||||||
|
|
||||||
|
/** Used to detect maps, sets, and weakmaps. */
|
||||||
|
const dataViewCtorString = `${DataView}`
|
||||||
|
const mapCtorString = `${Map}`
|
||||||
|
const promiseCtorString = `${Promise}`
|
||||||
|
const setCtorString = `${Set}`
|
||||||
|
const weakMapCtorString = `${WeakMap}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `toStringTag` of `value`.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} value The value to query.
|
||||||
|
* @returns {string} Returns the `toStringTag`.
|
||||||
|
*/
|
||||||
|
let getTag = baseGetTag
|
||||||
|
|
||||||
|
// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
|
||||||
|
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
|
||||||
|
(getTag(new Map) != mapTag) ||
|
||||||
|
(getTag(Promise.resolve()) != promiseTag) ||
|
||||||
|
(getTag(new Set) != setTag) ||
|
||||||
|
(getTag(new WeakMap) != weakMapTag)) {
|
||||||
|
getTag = (value) => {
|
||||||
|
const result = baseGetTag(value)
|
||||||
|
const Ctor = result == objectTag ? value.constructor : undefined
|
||||||
|
const ctorString = Ctor ? `${Ctor}` : ''
|
||||||
|
|
||||||
|
if (ctorString) {
|
||||||
|
switch (ctorString) {
|
||||||
|
case dataViewCtorString: return dataViewTag
|
||||||
|
case mapCtorString: return mapTag
|
||||||
|
case promiseCtorString: return promiseTag
|
||||||
|
case setCtorString: return setTag
|
||||||
|
case weakMapCtorString: return weakMapTag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getTag
|
9
src/common/modules/lodash/.internal/root.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import freeGlobal from './freeGlobal.js'
|
||||||
|
|
||||||
|
/** Detect free variable `self`. */
|
||||||
|
const freeSelf = typeof self == 'object' && self !== null && self.Object === Object && self
|
||||||
|
|
||||||
|
/** Used as a reference to the global object. */
|
||||||
|
const root = freeGlobal || freeSelf || Function('return this')()
|
||||||
|
|
||||||
|
export default root
|
212
src/common/modules/lodash/debounce.js
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
import isObject from './isObject.js'
|
||||||
|
import root from './.internal/root.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a debounced function that delays invoking `func` until after `wait`
|
||||||
|
* milliseconds have elapsed since the last time the debounced function was
|
||||||
|
* invoked, or until the next browser frame is drawn. The debounced function
|
||||||
|
* comes with a `cancel` method to cancel delayed `func` invocations and a
|
||||||
|
* `flush` method to immediately invoke them. Provide `options` to indicate
|
||||||
|
* whether `func` should be invoked on the leading and/or trailing edge of the
|
||||||
|
* `wait` timeout. The `func` is invoked with the last arguments provided to the
|
||||||
|
* debounced function. Subsequent calls to the debounced function return the
|
||||||
|
* result of the last `func` invocation.
|
||||||
|
*
|
||||||
|
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||||||
|
* invoked on the trailing edge of the timeout only if the debounced function
|
||||||
|
* is invoked more than once during the `wait` timeout.
|
||||||
|
*
|
||||||
|
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||||||
|
* until the next tick, similar to `setTimeout` with a timeout of `0`.
|
||||||
|
*
|
||||||
|
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
|
||||||
|
* invocation will be deferred until the next frame is drawn (typically about
|
||||||
|
* 16ms).
|
||||||
|
*
|
||||||
|
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
|
* for details over the differences between `debounce` and `throttle`.
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
* @category Function
|
||||||
|
* @param {Function} func The function to debounce.
|
||||||
|
* @param {number} [wait=0]
|
||||||
|
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
|
||||||
|
* used (if available).
|
||||||
|
* @param {Object} [options={}] The options object.
|
||||||
|
* @param {boolean} [options.leading=false]
|
||||||
|
* Specify invoking on the leading edge of the timeout.
|
||||||
|
* @param {number} [options.maxWait]
|
||||||
|
* The maximum time `func` is allowed to be delayed before it's invoked.
|
||||||
|
* @param {boolean} [options.trailing=true]
|
||||||
|
* Specify invoking on the trailing edge of the timeout.
|
||||||
|
* @returns {Function} Returns the new debounced function.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* // Avoid costly calculations while the window size is in flux.
|
||||||
|
* jQuery(window).on('resize', debounce(calculateLayout, 150))
|
||||||
|
*
|
||||||
|
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
|
||||||
|
* jQuery(element).on('click', debounce(sendMail, 300, {
|
||||||
|
* 'leading': true,
|
||||||
|
* 'trailing': false
|
||||||
|
* }))
|
||||||
|
*
|
||||||
|
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
|
||||||
|
* const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
|
||||||
|
* const source = new EventSource('/stream')
|
||||||
|
* jQuery(source).on('message', debounced)
|
||||||
|
*
|
||||||
|
* // Cancel the trailing debounced invocation.
|
||||||
|
* jQuery(window).on('popstate', debounced.cancel)
|
||||||
|
*
|
||||||
|
* // Check for pending invocations.
|
||||||
|
* const status = debounced.pending() ? "Pending..." : "Ready"
|
||||||
|
*/
|
||||||
|
function debounce(func, wait, options) {
|
||||||
|
let lastArgs,
|
||||||
|
lastThis,
|
||||||
|
maxWait,
|
||||||
|
result,
|
||||||
|
timerId,
|
||||||
|
lastCallTime
|
||||||
|
|
||||||
|
let lastInvokeTime = 0
|
||||||
|
let leading = false
|
||||||
|
let maxing = false
|
||||||
|
let trailing = true
|
||||||
|
|
||||||
|
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
|
||||||
|
const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')
|
||||||
|
|
||||||
|
if (typeof func != 'function') {
|
||||||
|
throw new TypeError('Expected a function')
|
||||||
|
}
|
||||||
|
wait = +wait || 0
|
||||||
|
if (isObject(options)) {
|
||||||
|
leading = !!options.leading
|
||||||
|
maxing = 'maxWait' in options
|
||||||
|
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
|
||||||
|
trailing = 'trailing' in options ? !!options.trailing : trailing
|
||||||
|
}
|
||||||
|
|
||||||
|
function invokeFunc(time) {
|
||||||
|
const args = lastArgs
|
||||||
|
const thisArg = lastThis
|
||||||
|
|
||||||
|
lastArgs = lastThis = undefined
|
||||||
|
lastInvokeTime = time
|
||||||
|
result = func.apply(thisArg, args)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer(pendingFunc, wait) {
|
||||||
|
if (useRAF) {
|
||||||
|
return root.requestAnimationFrame(pendingFunc)
|
||||||
|
}
|
||||||
|
return setTimeout(pendingFunc, wait)
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelTimer(id) {
|
||||||
|
if (useRAF) {
|
||||||
|
return root.cancelAnimationFrame(id)
|
||||||
|
}
|
||||||
|
clearTimeout(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function leadingEdge(time) {
|
||||||
|
// Reset any `maxWait` timer.
|
||||||
|
lastInvokeTime = time
|
||||||
|
// Start the timer for the trailing edge.
|
||||||
|
timerId = startTimer(timerExpired, wait)
|
||||||
|
// Invoke the leading edge.
|
||||||
|
return leading ? invokeFunc(time) : result
|
||||||
|
}
|
||||||
|
|
||||||
|
function remainingWait(time) {
|
||||||
|
const timeSinceLastCall = time - lastCallTime
|
||||||
|
const timeSinceLastInvoke = time - lastInvokeTime
|
||||||
|
const timeWaiting = wait - timeSinceLastCall
|
||||||
|
|
||||||
|
return maxing
|
||||||
|
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
|
||||||
|
: timeWaiting
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldInvoke(time) {
|
||||||
|
const timeSinceLastCall = time - lastCallTime
|
||||||
|
const timeSinceLastInvoke = time - lastInvokeTime
|
||||||
|
|
||||||
|
// Either this is the first call, activity has stopped and we're at the
|
||||||
|
// trailing edge, the system time has gone backwards and we're treating
|
||||||
|
// it as the trailing edge, or we've hit the `maxWait` limit.
|
||||||
|
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
|
||||||
|
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
|
||||||
|
}
|
||||||
|
|
||||||
|
function timerExpired() {
|
||||||
|
const time = Date.now()
|
||||||
|
if (shouldInvoke(time)) {
|
||||||
|
return trailingEdge(time)
|
||||||
|
}
|
||||||
|
// Restart the timer.
|
||||||
|
timerId = startTimer(timerExpired, remainingWait(time))
|
||||||
|
}
|
||||||
|
|
||||||
|
function trailingEdge(time) {
|
||||||
|
timerId = undefined
|
||||||
|
|
||||||
|
// Only invoke if we have `lastArgs` which means `func` has been
|
||||||
|
// debounced at least once.
|
||||||
|
if (trailing && lastArgs) {
|
||||||
|
return invokeFunc(time)
|
||||||
|
}
|
||||||
|
lastArgs = lastThis = undefined
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
if (timerId !== undefined) {
|
||||||
|
cancelTimer(timerId)
|
||||||
|
}
|
||||||
|
lastInvokeTime = 0
|
||||||
|
lastArgs = lastCallTime = lastThis = timerId = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function flush() {
|
||||||
|
return timerId === undefined ? result : trailingEdge(Date.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
function pending() {
|
||||||
|
return timerId !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounced(...args) {
|
||||||
|
const time = Date.now()
|
||||||
|
const isInvoking = shouldInvoke(time)
|
||||||
|
|
||||||
|
lastArgs = args
|
||||||
|
lastThis = this
|
||||||
|
lastCallTime = time
|
||||||
|
|
||||||
|
if (isInvoking) {
|
||||||
|
if (timerId === undefined) {
|
||||||
|
return leadingEdge(lastCallTime)
|
||||||
|
}
|
||||||
|
if (maxing) {
|
||||||
|
// Handle invocations in a tight loop.
|
||||||
|
timerId = startTimer(timerExpired, wait)
|
||||||
|
return invokeFunc(lastCallTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timerId === undefined) {
|
||||||
|
timerId = startTimer(timerExpired, wait)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
debounced.cancel = cancel
|
||||||
|
debounced.flush = flush
|
||||||
|
debounced.pending = pending
|
||||||
|
return debounced
|
||||||
|
}
|
||||||
|
|
||||||
|
export default debounce
|
30
src/common/modules/lodash/isFunction.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import baseGetTag from './.internal/baseGetTag.js'
|
||||||
|
import isObject from './isObject.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if `value` is classified as a `Function` object.
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
* @category Lang
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* isFunction(_)
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isFunction(/abc/)
|
||||||
|
* // => false
|
||||||
|
*/
|
||||||
|
function isFunction(value) {
|
||||||
|
if (!isObject(value)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// The use of `Object#toString` avoids issues with the `typeof` operator
|
||||||
|
// in Safari 9 which returns 'object' for typed arrays and other constructors.
|
||||||
|
const tag = baseGetTag(value)
|
||||||
|
return tag == '[object Function]' || tag == '[object AsyncFunction]' ||
|
||||||
|
tag == '[object GeneratorFunction]' || tag == '[object Proxy]'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isFunction
|
29
src/common/modules/lodash/isObject.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Checks if `value` is the
|
||||||
|
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
|
||||||
|
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
* @category Lang
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* isObject({})
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isObject([1, 2, 3])
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isObject(Function)
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isObject(null)
|
||||||
|
* // => false
|
||||||
|
*/
|
||||||
|
function isObject(value) {
|
||||||
|
const type = typeof value
|
||||||
|
return value != null && (type == 'object' || type == 'function')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isObject
|
27
src/common/modules/lodash/isObjectLike.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
||||||
|
* and has a `typeof` result of "object".
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
* @category Lang
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* isObjectLike({})
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isObjectLike([1, 2, 3])
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isObjectLike(Function)
|
||||||
|
* // => false
|
||||||
|
*
|
||||||
|
* isObjectLike(null)
|
||||||
|
* // => false
|
||||||
|
*/
|
||||||
|
function isObjectLike(value) {
|
||||||
|
return typeof value == 'object' && value !== null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isObjectLike
|
44
src/common/modules/lodash/isPlainObject.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import baseGetTag from './.internal/baseGetTag.js'
|
||||||
|
import isObjectLike from './isObjectLike.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if `value` is a plain object, that is, an object created by the
|
||||||
|
* `Object` constructor or one with a `[[Prototype]]` of `null`.
|
||||||
|
*
|
||||||
|
* @since 0.8.0
|
||||||
|
* @category Lang
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* function Foo() {
|
||||||
|
* this.a = 1
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* isPlainObject(new Foo)
|
||||||
|
* // => false
|
||||||
|
*
|
||||||
|
* isPlainObject([1, 2, 3])
|
||||||
|
* // => false
|
||||||
|
*
|
||||||
|
* isPlainObject({ 'x': 0, 'y': 0 })
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isPlainObject(Object.create(null))
|
||||||
|
* // => true
|
||||||
|
*/
|
||||||
|
function isPlainObject(value) {
|
||||||
|
if (!isObjectLike(value) || baseGetTag(value) != '[object Object]') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (Object.getPrototypeOf(value) === null) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let proto = value
|
||||||
|
while (Object.getPrototypeOf(proto) !== null) {
|
||||||
|
proto = Object.getPrototypeOf(proto)
|
||||||
|
}
|
||||||
|
return Object.getPrototypeOf(value) === proto
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isPlainObject
|
23
src/common/modules/lodash/isString.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import getTag from './.internal/getTag.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if `value` is classified as a `String` primitive or object.
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
* @category Lang
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is a string, else `false`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* isString('abc')
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* isString(1)
|
||||||
|
* // => false
|
||||||
|
*/
|
||||||
|
function isString(value) {
|
||||||
|
const type = typeof value
|
||||||
|
return type == 'string' || (type == 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]')
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isString
|
70
src/common/modules/lodash/throttle.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import debounce from './debounce.js'
|
||||||
|
import isObject from './isObject.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a throttled function that only invokes `func` at most once per
|
||||||
|
* every `wait` milliseconds (or once per browser frame). The throttled function
|
||||||
|
* comes with a `cancel` method to cancel delayed `func` invocations and a
|
||||||
|
* `flush` method to immediately invoke them. Provide `options` to indicate
|
||||||
|
* whether `func` should be invoked on the leading and/or trailing edge of the
|
||||||
|
* `wait` timeout. The `func` is invoked with the last arguments provided to the
|
||||||
|
* throttled function. Subsequent calls to the throttled function return the
|
||||||
|
* result of the last `func` invocation.
|
||||||
|
*
|
||||||
|
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||||||
|
* invoked on the trailing edge of the timeout only if the throttled function
|
||||||
|
* is invoked more than once during the `wait` timeout.
|
||||||
|
*
|
||||||
|
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||||||
|
* until the next tick, similar to `setTimeout` with a timeout of `0`.
|
||||||
|
*
|
||||||
|
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
|
||||||
|
* invocation will be deferred until the next frame is drawn (typically about
|
||||||
|
* 16ms).
|
||||||
|
*
|
||||||
|
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
|
* for details over the differences between `throttle` and `debounce`.
|
||||||
|
*
|
||||||
|
* @since 0.1.0
|
||||||
|
* @category Function
|
||||||
|
* @param {Function} func The function to throttle.
|
||||||
|
* @param {number} [wait=0]
|
||||||
|
* The number of milliseconds to throttle invocations to; if omitted,
|
||||||
|
* `requestAnimationFrame` is used (if available).
|
||||||
|
* @param {Object} [options={}] The options object.
|
||||||
|
* @param {boolean} [options.leading=true]
|
||||||
|
* Specify invoking on the leading edge of the timeout.
|
||||||
|
* @param {boolean} [options.trailing=true]
|
||||||
|
* Specify invoking on the trailing edge of the timeout.
|
||||||
|
* @returns {Function} Returns the new throttled function.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* // Avoid excessively updating the position while scrolling.
|
||||||
|
* jQuery(window).on('scroll', throttle(updatePosition, 100))
|
||||||
|
*
|
||||||
|
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
||||||
|
* const throttled = throttle(renewToken, 300000, { 'trailing': false })
|
||||||
|
* jQuery(element).on('click', throttled)
|
||||||
|
*
|
||||||
|
* // Cancel the trailing throttled invocation.
|
||||||
|
* jQuery(window).on('popstate', throttled.cancel)
|
||||||
|
*/
|
||||||
|
function throttle(func, wait, options) {
|
||||||
|
let leading = true
|
||||||
|
let trailing = true
|
||||||
|
|
||||||
|
if (typeof func != 'function') {
|
||||||
|
throw new TypeError('Expected a function')
|
||||||
|
}
|
||||||
|
if (isObject(options)) {
|
||||||
|
leading = 'leading' in options ? !!options.leading : leading
|
||||||
|
trailing = 'trailing' in options ? !!options.trailing : trailing
|
||||||
|
}
|
||||||
|
return debounce(func, wait, {
|
||||||
|
'leading': leading,
|
||||||
|
'maxWait': wait,
|
||||||
|
'trailing': trailing
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default throttle
|
58
src/common/variables.css
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* Photon Colors CSS Variables v3.2.0 */
|
||||||
|
/* only a subset is included, the complete declaration can be found at
|
||||||
|
* https://firefoxux.github.io/design-tokens/photon-colors/photon-colors.css
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--blue-50: #0a84ff;
|
||||||
|
--blue-50-a30: rgba(10, 132, 255, 0.3);
|
||||||
|
--blue-60: #0060df;
|
||||||
|
--blue-70: #003eaa;
|
||||||
|
|
||||||
|
--green-50: #30e60b;
|
||||||
|
--green-60: #12bc00;
|
||||||
|
--green-70: #058b00;
|
||||||
|
--green-80: #006504;
|
||||||
|
--green-90: #003706;
|
||||||
|
|
||||||
|
--yellow-50: #ffe900;
|
||||||
|
--yellow-60: #d7b600;
|
||||||
|
--yellow-70: #a47f00;
|
||||||
|
--yellow-80: #715100;
|
||||||
|
--yellow-90: #3e2800;
|
||||||
|
|
||||||
|
--red-50: #ff0039;
|
||||||
|
--red-60: #d70022;
|
||||||
|
--red-70: #a4000f;
|
||||||
|
--red-80: #5a0002;
|
||||||
|
--red-90: #3e0200;
|
||||||
|
|
||||||
|
--red-60: #d70022;
|
||||||
|
|
||||||
|
--grey-20: #ededf0;
|
||||||
|
--grey-30: #d7d7db;
|
||||||
|
--grey-50: #737373;
|
||||||
|
--grey-90: #0c0c0d;
|
||||||
|
--grey-90-a10: rgba(12, 12, 13, 0.1);
|
||||||
|
--grey-90-a20: rgba(12, 12, 13, 0.2);
|
||||||
|
--grey-90-a30: rgba(12, 12, 13, 0.3);
|
||||||
|
|
||||||
|
--white-100: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* custom variables, independent of Mozilla's Photon colors
|
||||||
|
* or only based upon them */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--red-60-a90: rgba(215, 0, 34, 0.9);
|
||||||
|
--grey-20-a90: rgba(237, 237, 240, 0.9);
|
||||||
|
|
||||||
|
/* shadows https://design.firefox.com/photon/patterns/shadows.html */
|
||||||
|
--shadow-10: 0px 1px 4px var(--grey-90-a20);
|
||||||
|
--shadow-20: 0px 1px 4px var(--grey-90-a20);
|
||||||
|
--shadow-30: 0px 1px 4px var(--grey-90-a20);
|
||||||
|
}
|
4
src/options/fastLoad.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import * as MobileOptions from "/common/modules/AutomaticSettings/MobileOptions.js";
|
||||||
|
|
||||||
|
// hide not mobile compatible settings
|
||||||
|
MobileOptions.init();
|
|
@ -1,20 +1,18 @@
|
||||||
body {
|
body {
|
||||||
/* the style the other options in the settings pane use */
|
/* the style the other options in the settings pane use */
|
||||||
font-size: 1.25rem; /* TODO: adjust to font size on mobile */
|
font-size: 1.11em;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* on (small) mobile displays */
|
/* on (small) mobile displays */
|
||||||
@media (max-width: 700px) {
|
body.mobile {
|
||||||
body {
|
/* smaller size -> default on Firefox for Android is 14px */
|
||||||
/* smaller size -> default on Firefox for Android is 14px */
|
font-size: 14px;
|
||||||
font-size: 14px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* e.g. the icon is not even displayed there :) */
|
/* disable all options incomaptible with mobile devices */
|
||||||
.mobile-incompatible {
|
body.mobile .mobile-incompatible {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* https://design.firefox.com/photon/patterns/inactive.html */
|
/* https://design.firefox.com/photon/patterns/inactive.html */
|
||||||
|
@ -28,7 +26,7 @@ ul {
|
||||||
}
|
}
|
||||||
li {
|
li {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin-top: 8px;
|
margin-top: 10px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
/* in a fieldset e.g. we use a condensed version */
|
/* in a fieldset e.g. we use a condensed version */
|
||||||
|
@ -54,3 +52,24 @@ fieldset {
|
||||||
label + .setting {
|
label + .setting {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.helper-text {
|
||||||
|
display: block;
|
||||||
|
color: var(--grey-50);
|
||||||
|
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* some margin to align with checkbox */
|
||||||
|
input[type=checkbox] ~ .helper-text,
|
||||||
|
input[type=radio] ~ .helper-text {
|
||||||
|
/* 4px margin-left + 16px size + 3px margin-right + 5px space (#text) */
|
||||||
|
margin-left: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when a link is used in a helper text, add margin */
|
||||||
|
.helper-text > a {
|
||||||
|
margin-left: 4px;
|
||||||
|
overflow-wrap: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,20 +3,54 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="../common/common.css">
|
||||||
<link rel="stylesheet" href="options.css">
|
<link rel="stylesheet" href="options.css">
|
||||||
|
|
||||||
|
<script async src="./fastLoad.js" type="module"></script>
|
||||||
|
|
||||||
|
<script defer src="../common/common.js" type="module"></script>
|
||||||
<script defer src="./options.js" type="module"></script>
|
<script defer src="./options.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div class="message-container">
|
||||||
|
<div id="messageInfo" aria-label="info message" class="message-box info invisible fade-hide">
|
||||||
|
<span class="message-text">Some settings are managed by your administrator and cannot be changed.</span>
|
||||||
|
<a href="#">
|
||||||
|
<button class="message-action-button micro-button info invisible"></button>
|
||||||
|
</a>
|
||||||
|
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
|
||||||
|
</div>
|
||||||
|
<div id="messageSuccess" aria-label="success message" class="message-box success invisible fade-hide">
|
||||||
|
<span class="message-text">That worked!</span>
|
||||||
|
<a href="#">
|
||||||
|
<button class="message-action-button micro-button success invisible"></button>
|
||||||
|
</a>
|
||||||
|
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
|
||||||
|
</div>
|
||||||
|
<div id="messageError" aria-label="error message" class="message-box error invisible fade-hide">
|
||||||
|
<span class="message-text">An error happened.</span>
|
||||||
|
<a href="#">
|
||||||
|
<button class="message-action-button micro-button error invisible"></button>
|
||||||
|
</a>
|
||||||
|
<img class="icon-dismiss invisible" src="/common/img/close-white.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
|
||||||
|
</div>
|
||||||
|
<div id="messageWarning" aria-label="warning message" class="message-box warning invisible fade-hide">
|
||||||
|
<span class="message-text">There were some difficulties.</span>
|
||||||
|
<a href="#">
|
||||||
|
<button class="message-action-button micro-button warning invisible"></button>
|
||||||
|
</a>
|
||||||
|
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<form>
|
<form>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<label data-i18n="__MSG_optionMastodonHandle__" for="insertHandle">Enter you Mastodon handle: </label>
|
<label data-i18n="__MSG_optionMastodonHandle__" for="insertHandle">Your Mastodon handle: </label>
|
||||||
<input class="setting save-on-change" type="text" id="insertHandle" name="insert-handle">
|
<input class="setting save-on-change" type="text" id="insertHandle" name="insert-handle">
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button disabled data-i18n="__MSG_optionsResetButton__" type="button" name="reset-button" id="resetButton">Reset all settings to defaults</button>
|
<button data-i18n="__MSG_optionsResetButton__" type="button" name="reset-button" id="resetButton">Reset all settings to defaults</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
import * as Mastodon from "/common/modules/Mastodon.js";
|
/**
|
||||||
|
* Starter module for addon settings site.
|
||||||
|
*
|
||||||
|
* @requires modules/OptionHandler
|
||||||
|
*/
|
||||||
|
|
||||||
const insertHandle = document.getElementById("insertHandle");
|
import * as AddonSettings from "/common/modules/AddonSettings/AddonSettings.js";
|
||||||
|
import * as AutomaticSettings from "/common/modules/AutomaticSettings/AutomaticSettings.js";
|
||||||
|
|
||||||
insertHandle.addEventListener("input", () => {
|
// init modules
|
||||||
const ownMastodonSplit = Mastodon.splitUserHandle(insertHandle.value);
|
AutomaticSettings.setDefaultOptionProvider(AddonSettings.getDefaultValue);
|
||||||
|
AutomaticSettings.init();
|
||||||
return browser.storage.sync.set({
|
|
||||||
mastodonUsername: ownMastodonSplit.username,
|
|
||||||
mastodonServer: ownMastodonSplit.server,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.storage.sync.get(["mastodonUsername", "mastodonServer"]).then((handleObject) => {
|
|
||||||
const mastodonHandle = Mastodon.concatUserHandle(handleObject.mastodonUsername, handleObject.mastodonServer);
|
|
||||||
insertHandle.value = mastodonHandle;
|
|
||||||
});
|
|
||||||
|
|