JC Brand 1 tháng trước cách đây
mục cha
commit
596e4b9417

+ 1 - 1
src/headless/plugins/disco/utils.js

@@ -63,7 +63,7 @@ function addClientFeatures () {
 export async function initializeDisco () {
     addClientFeatures();
     api.connection.get().addHandler(
-        stanza => onDiscoInfoRequest(stanza),
+        (stanza) => onDiscoInfoRequest(stanza),
         Strophe.NS.DISCO_INFO,
         'iq', 'get', null, null
     );

+ 6 - 6
src/headless/shared/api/promise.js

@@ -47,15 +47,15 @@ export default {
          * @param {boolean} [replace=true] Whether this promise should be replaced with a new one when the user logs out.
          * @example _converse.api.promises.add('foo-completed');
          */
-        add (promises, replace=true) {
+        add(promises, replace = true) {
             promises = Array.isArray(promises) ? promises : [promises];
-            promises.forEach(name => {
+            promises.forEach((name) => {
                 /** @type {import('../types').ReplaceableOpenPromise} */
                 const promise = getOpenPromise();
                 promise.replace = replace;
                 _converse.promises[name] = promise;
             });
-        }
+        },
     },
 
     /**
@@ -66,9 +66,9 @@ export default {
      * or a function which should eventually return a truthy value.
      * @returns {Promise}
      */
-    waitUntil (condition) {
+    waitUntil(condition) {
         if (isFunction(condition)) {
-            return waitUntil(/** @type {Function} */(condition));
+            return waitUntil(/** @type {Function} */ (condition));
         } else {
             const promise = _converse.promises[condition];
             if (promise === undefined) {
@@ -77,4 +77,4 @@ export default {
             return promise;
         }
     },
-}
+};

+ 14 - 15
src/headless/shared/settings/utils.js

@@ -1,20 +1,19 @@
-import isEqual from "lodash-es/isEqual.js";
+import isEqual from 'lodash-es/isEqual.js';
 import pick from 'lodash-es/pick';
 import { EventEmitter } from '@converse/skeletor';
 import { DEFAULT_SETTINGS } from './constants.js';
 import { merge } from '../../utils/object.js';
 
-
 let app_settings;
 let init_settings = {}; // Container for settings passed in via converse.initialize
 
 class AppSettings extends EventEmitter(Object) {}
 
-export function getAppSettings () {
+export function getAppSettings() {
     return app_settings;
 }
 
-export function initAppSettings (settings) {
+export function initAppSettings(settings) {
     init_settings = settings;
 
     app_settings = new AppSettings();
@@ -24,21 +23,21 @@ export function initAppSettings (settings) {
     Object.assign(app_settings, DEFAULT_SETTINGS, allowed_settings);
 }
 
-export function getInitSettings () {
+export function getInitSettings() {
     return init_settings;
 }
 
-export function getAppSetting (key) {
+export function getAppSetting(key) {
     if (Object.keys(DEFAULT_SETTINGS).includes(key)) {
         return app_settings[key];
     }
 }
 
-export function extendAppSettings (settings) {
+export function extendAppSettings(settings) {
     merge(DEFAULT_SETTINGS, settings);
     // When updating the settings, we need to avoid overwriting the
     // initialization_settings (i.e. the settings passed in via converse.initialize).
-    const allowed_keys = Object.keys(settings).filter(k => k in DEFAULT_SETTINGS);
+    const allowed_keys = Object.keys(settings).filter((k) => k in DEFAULT_SETTINGS);
     const allowed_site_settings = pick(init_settings, allowed_keys);
     const updated_settings = Object.assign(pick(settings, allowed_keys), allowed_site_settings);
     merge(app_settings, updated_settings);
@@ -49,15 +48,15 @@ export function extendAppSettings (settings) {
  * @param {Function} func
  * @param {any} context
  */
-export function registerListener (name, func, context) {
-    app_settings.on(name, func, context)
+export function registerListener(name, func, context) {
+    app_settings.on(name, func, context);
 }
 
 /**
  * @param {string} name
  * @param {Function} func
  */
-export function unregisterListener (name, func) {
+export function unregisterListener(name, func) {
     app_settings.off(name, func);
 }
 
@@ -65,7 +64,7 @@ export function unregisterListener (name, func) {
  * @param {Object|string} key An object containing config settings or alternatively a string key
  * @param {string} [val] The value, if the previous parameter is a key
  */
-export function updateAppSettings (key, val) {
+export function updateAppSettings(key, val) {
     if (key == null) return this; // eslint-disable-line no-eq-null
 
     let attrs;
@@ -76,15 +75,15 @@ export function updateAppSettings (key, val) {
         attrs[key] = val;
     }
 
-    const allowed_keys = Object.keys(attrs).filter(k => k in DEFAULT_SETTINGS);
+    const allowed_keys = Object.keys(attrs).filter((k) => k in DEFAULT_SETTINGS);
     const changed = {};
-    allowed_keys.forEach(k => {
+    allowed_keys.forEach((k) => {
         const val = attrs[k];
         if (!isEqual(app_settings[k], val)) {
             changed[k] = val;
             app_settings[k] = val;
         }
     });
-    Object.keys(changed).forEach(k => app_settings.trigger('change:' + k, changed[k]));
+    Object.keys(changed).forEach((k) => app_settings.trigger('change:' + k, changed[k]));
     app_settings.trigger('change', changed);
 }

+ 7 - 9
src/plugins/bookmark-views/components/bookmarks-list.js

@@ -1,22 +1,20 @@
-import debounce from "lodash-es/debounce";
+import debounce from 'lodash-es/debounce';
 import { Model } from '@converse/skeletor';
 import { _converse, api, u } from '@converse/headless';
 import tplBookmarksList from './templates/list.js';
-import tplSpinner from "templates/spinner.js";
+import tplSpinner from 'templates/spinner.js';
 import { CustomElement } from 'shared/components/element.js';
 
 import '../styles/bookmarks.scss';
 
-const { initStorage }  = u;
-
+const { initStorage } = u;
 
 export default class BookmarksView extends CustomElement {
-
-    async initialize () {
+    async initialize() {
         await api.waitUntil('bookmarksInitialized');
         const { bookmarks, chatboxes } = _converse.state;
 
-        this.liveFilter = debounce((ev) => this.model.set({'text': ev.target.value}), 100);
+        this.liveFilter = debounce((ev) => this.model.set({ text: ev.target.value }), 100);
 
         this.listenTo(bookmarks, 'add', () => this.requestUpdate());
         this.listenTo(bookmarks, 'remove', () => this.requestUpdate());
@@ -37,14 +35,14 @@ export default class BookmarksView extends CustomElement {
         });
     }
 
-    render () {
+    render() {
         return _converse.state.bookmarks && this.model ? tplBookmarksList(this) : tplSpinner();
     }
 
     /**
      * @param {Event} ev
      */
-    clearFilter (ev) {
+    clearFilter(ev) {
         ev?.stopPropagation?.();
         this.model.set('text', '');
     }

+ 5 - 6
src/plugins/bookmark-views/modals/bookmark-list.js

@@ -1,16 +1,15 @@
-import { api } from "@converse/headless";
-import BaseModal from "plugins/modal/modal.js";
-import { html } from "lit";
+import { api } from '@converse/headless';
+import BaseModal from 'plugins/modal/modal.js';
+import { html } from 'lit';
 import { __ } from 'i18n';
 import '../components/bookmarks-list.js';
 
 export default class BookmarkListModal extends BaseModal {
-
-    renderModal () {
+    renderModal() {
         return html`<converse-bookmarks></converse-bookmarks>`;
     }
 
-    getModalTitle () {
+    getModalTitle() {
         return __('Bookmarks');
     }
 }

+ 182 - 182
src/plugins/omemo/utils.js

@@ -5,20 +5,20 @@
  * @typedef {import('@converse/headless').ChatBox} ChatBox
  * @typedef {import('@converse/headless/types/shared/message').default} BaseMessage
  */
-import { html } from "lit";
-import { __ } from "i18n";
-import { until } from "lit/directives/until.js";
-import { _converse, converse, api, log, u, constants, MUC } from "@converse/headless";
-import tplAudio from "shared/texture/templates/audio.js";
-import tplFile from "templates/file.js";
-import tplImage from "shared/texture/templates/image.js";
-import tplVideo from "shared/texture/templates/video.js";
-import { KEY_ALGO, UNTRUSTED, TAG_LENGTH } from "./consts.js";
-import { MIMETYPES_MAP } from "utils/file.js";
-import { IQError, UserFacingError } from "shared/errors.js";
-import DeviceLists from "./devicelists.js";
-import {getFileName} from "utils/html.js";
-import {Texture} from "shared/texture/texture.js";
+import { html } from 'lit';
+import { __ } from 'i18n';
+import { until } from 'lit/directives/until.js';
+import { _converse, converse, api, log, u, constants, MUC } from '@converse/headless';
+import tplAudio from 'shared/texture/templates/audio.js';
+import tplFile from 'templates/file.js';
+import tplImage from 'shared/texture/templates/image.js';
+import tplVideo from 'shared/texture/templates/video.js';
+import { KEY_ALGO, UNTRUSTED, TAG_LENGTH } from './consts.js';
+import { MIMETYPES_MAP } from 'utils/file.js';
+import { IQError, UserFacingError } from 'shared/errors.js';
+import DeviceLists from './devicelists.js';
+import { getFileName } from 'utils/html.js';
+import { Texture } from 'shared/texture/texture.js';
 
 const { Strophe, sizzle, stx } = converse.env;
 const { CHATROOMS_TYPE, PRIVATE_CHAT_TYPE } = constants;
@@ -41,10 +41,10 @@ const {
  * @param {string} fp
  */
 export function formatFingerprint(fp) {
-    fp = fp.replace(/^05/, "");
+    fp = fp.replace(/^05/, '');
     for (let i = 1; i < 8; i++) {
         const idx = i * 8 + i - 1;
-        fp = fp.slice(0, idx) + " " + fp.slice(idx);
+        fp = fp.slice(0, idx) + ' ' + fp.slice(idx);
     }
     return fp;
 }
@@ -53,9 +53,9 @@ export function formatFingerprint(fp) {
  * @param {string} fp
  */
 export function formatFingerprintForQRCode(fp) {
-    const sid = _converse.state.omemo_store.get("device_id");
-    const jid = _converse.session.get("bare_jid");
-    fp = fp.replace(/^05/, "");
+    const sid = _converse.state.omemo_store.get('device_id');
+    const jid = _converse.session.get('bare_jid');
+    fp = fp.replace(/^05/, '');
     return `xmpp:${jid}?omemo-sid-${sid}=${fp}`;
 }
 
@@ -65,31 +65,31 @@ export function formatFingerprintForQRCode(fp) {
  */
 export function handleMessageSendError(e, chat) {
     if (e instanceof IQError) {
-        chat.save("omemo_supported", false);
+        chat.save('omemo_supported', false);
 
         const err_msgs = [];
         if (sizzle(`presence-subscription-required[xmlns="${Strophe.NS.PUBSUB_ERROR}"]`, e.iq).length) {
             err_msgs.push(
                 __(
                     "Sorry, we're unable to send an encrypted message because %1$s " +
-                        "requires you to be subscribed to their presence in order to see their OMEMO information",
-                    e.iq.getAttribute("from")
+                        'requires you to be subscribed to their presence in order to see their OMEMO information',
+                    e.iq.getAttribute('from')
                 )
             );
         } else if (sizzle(`remote-server-not-found[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]`, e.iq).length) {
             err_msgs.push(
                 __(
                     "Sorry, we're unable to send an encrypted message because the remote server for %1$s could not be found",
-                    e.iq.getAttribute("from")
+                    e.iq.getAttribute('from')
                 )
             );
         } else {
-            err_msgs.push(__("Unable to send an encrypted message due to an unexpected error."));
+            err_msgs.push(__('Unable to send an encrypted message due to an unexpected error.'));
             err_msgs.push(e.iq.outerHTML);
         }
-        api.alert("error", __("Error"), err_msgs);
+        api.alert('error', __('Error'), err_msgs);
     } else if (e instanceof UserFacingError) {
-        api.alert("error", __("Error"), [e.message]);
+        api.alert('error', __('Error'), [e.message]);
     }
     throw e;
 }
@@ -109,14 +109,14 @@ export async function contactHasOMEMOSupport(jid) {
  * @return {MessageAttributes}
  */
 export function getOutgoingMessageAttributes(chat, attrs) {
-    if (chat.get("omemo_active") && attrs.body) {
+    if (chat.get('omemo_active') && attrs.body) {
         return {
             ...attrs,
             is_encrypted: true,
             plaintext: attrs.body,
             body: __(
-                "This is an OMEMO encrypted message which your client doesn’t seem to support. " +
-                    "Find more information on https://conversations.im/omemo"
+                'This is an OMEMO encrypted message which your client doesn’t seem to support. ' +
+                    'Find more information on https://conversations.im/omemo'
             ),
         };
     }
@@ -139,17 +139,17 @@ async function encryptMessage(plaintext) {
     //
     // https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
     const iv = crypto.getRandomValues(new window.Uint8Array(12));
-    const key = await crypto.subtle.generateKey(KEY_ALGO, true, ["encrypt", "decrypt"]);
+    const key = await crypto.subtle.generateKey(KEY_ALGO, true, ['encrypt', 'decrypt']);
     const algo = /** @type {AesGcmParams} */ {
         iv,
-        name: "AES-GCM",
+        name: 'AES-GCM',
         tagLength: TAG_LENGTH,
     };
     const encrypted = await crypto.subtle.encrypt(algo, key, stringToArrayBuffer(plaintext));
     const length = encrypted.byteLength - ((128 + 7) >> 3);
     const ciphertext = encrypted.slice(0, length);
     const tag = encrypted.slice(length);
-    const exported_key = await crypto.subtle.exportKey("raw", key);
+    const exported_key = await crypto.subtle.exportKey('raw', key);
     return {
         tag,
         key: exported_key,
@@ -164,10 +164,10 @@ async function encryptMessage(plaintext) {
  * @returns {Promise<string>}
  */
 async function decryptMessage(obj) {
-    const key_obj = await crypto.subtle.importKey("raw", obj.key, KEY_ALGO, true, ["encrypt", "decrypt"]);
+    const key_obj = await crypto.subtle.importKey('raw', obj.key, KEY_ALGO, true, ['encrypt', 'decrypt']);
     const cipher = appendArrayBuffer(base64ToArrayBuffer(obj.payload), obj.tag);
     const algo = /** @type {AesGcmParams} */ {
-        name: "AES-GCM",
+        name: 'AES-GCM',
         iv: base64ToArrayBuffer(obj.iv),
         tagLength: TAG_LENGTH,
     };
@@ -180,9 +180,9 @@ async function decryptMessage(obj) {
  */
 export async function encryptFile(file) {
     const iv = crypto.getRandomValues(new Uint8Array(12));
-    const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);
-    const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, await file.arrayBuffer());
-    const exported_key = await window.crypto.subtle.exportKey("raw", key);
+    const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
+    const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, await file.arrayBuffer());
+    const exported_key = await window.crypto.subtle.exportKey('raw', key);
     const encrypted_file = new File([encrypted], file.name, { type: file.type, lastModified: file.lastModified });
 
     Object.assign(encrypted_file, { xep454_ivkey: arrayBufferToHex(iv) + arrayBufferToHex(exported_key) });
@@ -194,7 +194,7 @@ export async function encryptFile(file) {
  * @param {import('@converse/headless/shared/types').FileUploadMessageAttributes} attrs
  */
 export function setEncryptedFileURL(message, attrs) {
-    const url = attrs.oob_url.replace(/^https?:/, "aesgcm:") + "#" + message.file.xep454_ivkey;
+    const url = attrs.oob_url.replace(/^https?:/, 'aesgcm:') + '#' + message.file.xep454_ivkey;
     return Object.assign(attrs, {
         oob_url: null, // Since only the body gets encrypted, we don't set the oob_url
         message: url,
@@ -208,9 +208,9 @@ export function setEncryptedFileURL(message, attrs) {
  * @param {ArrayBuffer} cipher
  */
 async function decryptFile(iv, key, cipher) {
-    const key_obj = await crypto.subtle.importKey("raw", hexToArrayBuffer(key), "AES-GCM", false, ["decrypt"]);
+    const key_obj = await crypto.subtle.importKey('raw', hexToArrayBuffer(key), 'AES-GCM', false, ['decrypt']);
     const algo = /** @type {AesGcmParams} */ {
-        name: "AES-GCM",
+        name: 'AES-GCM',
         iv: hexToArrayBuffer(iv),
     };
     return crypto.subtle.decrypt(algo, key_obj, cipher);
@@ -241,17 +241,17 @@ async function downloadFile(url) {
  */
 async function getAndDecryptFile(url_text) {
     const url = new URL(url_text);
-    const protocol = window.location.hostname === "localhost" && url.hostname === "localhost" ? "http" : "https";
+    const protocol = window.location.hostname === 'localhost' && url.hostname === 'localhost' ? 'http' : 'https';
     const http_url = url.toString().replace(/^aesgcm/, protocol);
     const cipher = await downloadFile(http_url);
     if (cipher === null) {
         log.error(`Could not decrypt a received encrypted file ${url.toString()} since it could not be downloaded`);
-        return new Error(__("Error: could not decrypt a received encrypted file, because it could not be downloaded"));
+        return new Error(__('Error: could not decrypt a received encrypted file, because it could not be downloaded'));
     }
 
     const hash = url.hash.slice(1);
     const key = hash.substring(hash.length - 64);
-    const iv = hash.replace(key, "");
+    const iv = hash.replace(key, '');
     let content;
     try {
         content = await decryptFile(iv, key, cipher);
@@ -260,10 +260,10 @@ async function getAndDecryptFile(url_text) {
         log.error(e);
         return null;
     }
-    const [filename, extension] = url.pathname.split("/").pop().split(".");
+    const [filename, extension] = url.pathname.split('/').pop().split('.');
     const mimetype = MIMETYPES_MAP[extension];
     try {
-        const file = new File([content], filename, { "type": mimetype });
+        const file = new File([content], filename, { 'type': mimetype });
         return URL.createObjectURL(file);
     } catch (e) {
         log.error(`Could not decrypt file ${url.toString()}`);
@@ -280,7 +280,7 @@ async function getAndDecryptFile(url_text) {
  */
 function getTemplateForObjectURL(file_url, obj_url, richtext) {
     if (isError(obj_url)) {
-        return html`<p class="error">${/** @type {Error} */(obj_url).message}</p>`;
+        return html`<p class="error">${/** @type {Error} */ (obj_url).message}</p>`;
     }
 
     if (isImageURL(file_url)) {
@@ -290,9 +290,9 @@ function getTemplateForObjectURL(file_url, obj_url, richtext) {
             onLoad: richtext.onImgLoad,
         });
     } else if (isAudioURL(file_url)) {
-        return tplAudio(/** @type {string} */(obj_url));
+        return tplAudio(/** @type {string} */ (obj_url));
     } else if (isVideoURL(file_url)) {
-        return tplVideo(/** @type {string} */(obj_url));
+        return tplVideo(/** @type {string} */ (obj_url));
     } else {
         return tplFile(obj_url, getFileName(file_url));
     }
@@ -327,7 +327,7 @@ function addEncryptedFiles(text, offset, richtext) {
     objs.forEach((o) => {
         const promise = getAndDecryptFile(o.url).then((obj_url) => getTemplateForObjectURL(o.url, obj_url, richtext));
 
-        const template = html`${until(promise, "")}`;
+        const template = html`${until(promise, '')}`;
         richtext.addTemplateResult(o.start + offset, o.end + offset, template);
     });
 }
@@ -336,7 +336,7 @@ function addEncryptedFiles(text, offset, richtext) {
  * @param {import('shared/texture/texture.js').Texture} richtext
  */
 export function handleEncryptedFiles(richtext) {
-    if (!_converse.state.config.get("trusted")) {
+    if (!_converse.state.config.get('trusted')) {
         return;
     }
     richtext.addAnnotations(
@@ -359,32 +359,32 @@ export function handleEncryptedFiles(richtext) {
  */
 export async function parseEncryptedMessage(stanza, attrs) {
     if (
-        api.settings.get("clear_cache_on_logout") ||
+        api.settings.get('clear_cache_on_logout') ||
         !attrs.is_encrypted ||
         attrs.encryption_namespace !== Strophe.NS.OMEMO
     ) {
         return attrs;
     }
     const encrypted_el = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).pop();
-    const header = encrypted_el.querySelector("header");
-    attrs.encrypted = { "device_id": header.getAttribute("sid") };
+    const header = encrypted_el.querySelector('header');
+    attrs.encrypted = { 'device_id': header.getAttribute('sid') };
 
     const device_id = await api.omemo?.getDeviceID();
     const key = device_id && sizzle(`key[rid="${device_id}"]`, encrypted_el).pop();
     if (key) {
         Object.assign(attrs.encrypted, {
-            iv: header.querySelector("iv").textContent,
+            iv: header.querySelector('iv').textContent,
             key: key.textContent,
-            payload: encrypted_el.querySelector("payload")?.textContent || null,
-            prekey: ["true", "1"].includes(key.getAttribute("prekey")),
+            payload: encrypted_el.querySelector('payload')?.textContent || null,
+            prekey: ['true', '1'].includes(key.getAttribute('prekey')),
         });
     } else {
         return Object.assign(attrs, {
-            error_condition: "not-encrypted-for-this-device",
-            error_type: "Decryption",
+            error_condition: 'not-encrypted-for-this-device',
+            error_type: 'Decryption',
             is_ephemeral: true,
             is_error: true,
-            type: "error",
+            type: 'error',
         });
     }
     // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
@@ -396,32 +396,32 @@ export async function parseEncryptedMessage(stanza, attrs) {
 }
 
 export function onChatBoxesInitialized() {
-    _converse.state.chatboxes.on("add", (chatbox) => {
+    _converse.state.chatboxes.on('add', (chatbox) => {
         checkOMEMOSupported(chatbox);
-        if (chatbox.get("type") === CHATROOMS_TYPE) {
-            chatbox.occupants.on("add", (o) => onOccupantAdded(chatbox, o));
-            chatbox.features.on("change", () => checkOMEMOSupported(chatbox));
+        if (chatbox.get('type') === CHATROOMS_TYPE) {
+            chatbox.occupants.on('add', (o) => onOccupantAdded(chatbox, o));
+            chatbox.features.on('change', () => checkOMEMOSupported(chatbox));
         }
     });
 }
 
 export function onChatInitialized(el) {
-    el.listenTo(el.model.messages, "add", (message) => {
-        if (message.get("is_encrypted") && !message.get("is_error")) {
-            el.model.save("omemo_supported", true);
+    el.listenTo(el.model.messages, 'add', (message) => {
+        if (message.get('is_encrypted') && !message.get('is_error')) {
+            el.model.save('omemo_supported', true);
         }
     });
-    el.listenTo(el.model, "change:omemo_supported", () => {
-        if (!el.model.get("omemo_supported") && el.model.get("omemo_active")) {
-            el.model.set("omemo_active", false);
+    el.listenTo(el.model, 'change:omemo_supported', () => {
+        if (!el.model.get('omemo_supported') && el.model.get('omemo_active')) {
+            el.model.set('omemo_active', false);
         } else {
             // Manually trigger an update, setting omemo_active to
             // false above will automatically trigger one.
-            el.querySelector("converse-chat-toolbar")?.requestUpdate();
+            el.querySelector('converse-chat-toolbar')?.requestUpdate();
         }
     });
-    el.listenTo(el.model, "change:omemo_active", () => {
-        el.querySelector("converse-chat-toolbar").requestUpdate();
+    el.listenTo(el.model, 'change:omemo_active', () => {
+        el.querySelector('converse-chat-toolbar').requestUpdate();
     });
 }
 
@@ -454,15 +454,15 @@ function getJIDForDecryption(attrs) {
     if (!from_jid) {
         Object.assign(attrs, {
             error_text: __(
-                "Sorry, could not decrypt a received OMEMO " +
+                'Sorry, could not decrypt a received OMEMO ' +
                     "message because we don't have the XMPP address for that user."
             ),
-            error_type: "Decryption",
+            error_type: 'Decryption',
             is_ephemeral: true,
             is_error: true,
-            type: "error",
+            type: 'error',
         });
-        throw new Error("Could not find JID to decrypt OMEMO message for");
+        throw new Error('Could not find JID to decrypt OMEMO message for');
     }
     return from_jid;
 }
@@ -473,27 +473,27 @@ async function handleDecryptedWhisperMessage(attrs, key_and_tag) {
     const encrypted = attrs.encrypted;
     let device = devicelist.devices.get(encrypted.device_id);
     if (!device) {
-        device = await devicelist.devices.create({ "id": encrypted.device_id, "jid": from_jid }, { "promise": true });
+        device = await devicelist.devices.create({ 'id': encrypted.device_id, 'jid': from_jid }, { 'promise': true });
     }
     if (encrypted.payload) {
         const key = key_and_tag.slice(0, 16);
         const tag = key_and_tag.slice(16);
         const result = await omemo.decryptMessage(Object.assign(encrypted, { key, tag }));
-        device.save("active", true);
+        device.save('active', true);
         return result;
     }
 }
 
 function getDecryptionErrorAttributes(e) {
     return {
-        "error_text":
-            __("Sorry, could not decrypt a received OMEMO message due to an error.") + ` ${e.name} ${e.message}`,
-        "error_condition": e.name,
-        "error_message": e.message,
-        "error_type": "Decryption",
-        "is_ephemeral": true,
-        "is_error": true,
-        "type": "error",
+        'error_text':
+            __('Sorry, could not decrypt a received OMEMO message due to an error.') + ` ${e.name} ${e.message}`,
+        'error_condition': e.name,
+        'error_message': e.message,
+        'error_type': 'Decryption',
+        'is_ephemeral': true,
+        'is_error': true,
+        'type': 'error',
     };
 }
 
@@ -506,7 +506,7 @@ async function decryptPrekeyWhisperMessage(attrs) {
     const key = base64ToArrayBuffer(attrs.encrypted.key);
     let key_and_tag;
     try {
-        key_and_tag = await session_cipher.decryptPreKeyWhisperMessage(key, "binary");
+        key_and_tag = await session_cipher.decryptPreKeyWhisperMessage(key, 'binary');
     } catch (e) {
         // TODO from the XEP:
         // There are various reasons why decryption of an
@@ -539,9 +539,9 @@ async function decryptPrekeyWhisperMessage(attrs) {
         await omemo_store.generateMissingPreKeys();
         await omemo_store.publishBundle();
         if (plaintext) {
-            return Object.assign(attrs, { "plaintext": plaintext });
+            return Object.assign(attrs, { 'plaintext': plaintext });
         } else {
-            return Object.assign(attrs, { "is_only_key": true });
+            return Object.assign(attrs, { 'is_only_key': true });
         }
     } catch (e) {
         log.error(`${e.name} ${e.message}`);
@@ -557,9 +557,9 @@ async function decryptWhisperMessage(attrs) {
     const session_cipher = getSessionCipher(from_jid, parseInt(attrs.encrypted.device_id, 10));
     const key = base64ToArrayBuffer(attrs.encrypted.key);
     try {
-        const key_and_tag = await session_cipher.decryptWhisperMessage(key, "binary");
+        const key_and_tag = await session_cipher.decryptWhisperMessage(key, 'binary');
         const plaintext = await handleDecryptedWhisperMessage(attrs, key_and_tag);
-        return Object.assign(attrs, { "plaintext": plaintext });
+        return Object.assign(attrs, { 'plaintext': plaintext });
     } catch (e) {
         log.error(`${e.name} ${e.message}`);
         return Object.assign(attrs, getDecryptionErrorAttributes(e));
@@ -573,18 +573,18 @@ async function decryptWhisperMessage(attrs) {
  * @returns {import('./types').Bundle}
  */
 export function parseBundle(bundle_el) {
-    const signed_prekey_public_el = bundle_el.querySelector("signedPreKeyPublic");
-    const signed_prekey_signature_el = bundle_el.querySelector("signedPreKeySignature");
+    const signed_prekey_public_el = bundle_el.querySelector('signedPreKeyPublic');
+    const signed_prekey_signature_el = bundle_el.querySelector('signedPreKeySignature');
     const prekeys = sizzle(`prekeys > preKeyPublic`, bundle_el).map(
         /** @param {Element} el */ (el) => ({
-            id: parseInt(el.getAttribute("preKeyId"), 10),
+            id: parseInt(el.getAttribute('preKeyId'), 10),
             key: el.textContent,
         })
     );
     return {
-        identity_key: bundle_el.querySelector("identityKey").textContent.trim(),
+        identity_key: bundle_el.querySelector('identityKey').textContent.trim(),
         signed_prekey: {
-            id: parseInt(signed_prekey_public_el.getAttribute("signedPreKeyId"), 10),
+            id: parseInt(signed_prekey_public_el.getAttribute('signedPreKeyId'), 10),
             public_key: signed_prekey_public_el.textContent,
             signature: signed_prekey_signature_el.textContent,
         },
@@ -604,13 +604,13 @@ export async function generateFingerprints(jid) {
  * @param {import('./device.js').default} device
  */
 export async function generateFingerprint(device) {
-    if (device.get("bundle")?.fingerprint) {
+    if (device.get('bundle')?.fingerprint) {
         return;
     }
     const bundle = await device.getBundle();
-    bundle["fingerprint"] = arrayBufferToHex(base64ToArrayBuffer(bundle["identity_key"]));
-    device.save("bundle", bundle);
-    device.trigger("change:bundle"); // Doesn't get triggered automatically due to pass-by-reference
+    bundle['fingerprint'] = arrayBufferToHex(base64ToArrayBuffer(bundle['identity_key']));
+    device.save('bundle', bundle);
+    device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference
 }
 
 /**
@@ -618,7 +618,7 @@ export async function generateFingerprint(device) {
  * @returns {Promise<import('./devices.js').default>}
  */
 export async function getDevicesForContact(jid) {
-    await api.waitUntil("OMEMOInitialized");
+    await api.waitUntil('OMEMOInitialized');
     const devicelist = await api.omemo.devicelists.get(jid, true);
     await devicelist.fetchDevices();
     return devicelist.devices;
@@ -638,9 +638,9 @@ export async function generateDeviceID() {
     const { libsignal } = /** @type WindowWithLibsignal */ (window);
 
     /* Generates a device ID, making sure that it's unique */
-    const bare_jid = _converse.session.get("bare_jid");
+    const bare_jid = _converse.session.get('bare_jid');
     const devicelist = await api.omemo.devicelists.get(bare_jid, true);
-    const existing_ids = devicelist.devices.pluck("id");
+    const existing_ids = devicelist.devices.pluck('id');
     let device_id = libsignal.KeyHelper.generateRegistrationId();
 
     // Before publishing a freshly generated device id for the first time,
@@ -650,7 +650,7 @@ export async function generateDeviceID() {
         device_id = libsignal.KeyHelper.generateRegistrationId();
         i++;
         if (i === 10) {
-            throw new Error("Unable to generate a unique device ID");
+            throw new Error('Unable to generate a unique device ID');
         }
     }
     return device_id.toString();
@@ -661,12 +661,12 @@ export async function generateDeviceID() {
  */
 async function buildSession(device) {
     const { libsignal } = /** @type WindowWithLibsignal */ (window);
-    const address = new libsignal.SignalProtocolAddress(device.get("jid"), device.get("id"));
+    const address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id'));
     const sessionBuilder = new libsignal.SessionBuilder(_converse.state.omemo_store, address);
     const prekey = device.getRandomPreKey();
     const bundle = await device.getBundle();
     return sessionBuilder.processPreKey({
-        registrationId: parseInt(device.get("id"), 10),
+        registrationId: parseInt(device.get('id'), 10),
         identityKey: base64ToArrayBuffer(bundle.identity_key),
         signedPreKey: {
             keyId: bundle.signed_prekey.id, // <Number>
@@ -684,12 +684,12 @@ async function buildSession(device) {
  * @param {import('./device.js').default} device
  */
 export async function getSession(device) {
-    if (!device.get("bundle")) {
-        log.error(`Could not build an OMEMO session for device ${device.get("id")} because we don't have its bundle`);
+    if (!device.get('bundle')) {
+        log.error(`Could not build an OMEMO session for device ${device.get('id')} because we don't have its bundle`);
         return null;
     }
     const { libsignal } = /** @type WindowWithLibsignal */ (window);
-    const address = new libsignal.SignalProtocolAddress(device.get("jid"), device.get("id"));
+    const address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id'));
     const session = await _converse.state.omemo_store.loadSession(address.toString());
     if (session) {
         return session;
@@ -697,7 +697,7 @@ export async function getSession(device) {
         try {
             return await buildSession(device);
         } catch (e) {
-            log.error(`Could not build an OMEMO session for device ${device.get("id")}`);
+            log.error(`Could not build an OMEMO session for device ${device.get('id')}`);
             log.error(e);
             return null;
         }
@@ -709,15 +709,15 @@ export async function getSession(device) {
  */
 async function updateBundleFromStanza(stanza) {
     const items_el = sizzle(`items`, stanza).pop();
-    if (!items_el || !items_el.getAttribute("node").startsWith(Strophe.NS.OMEMO_BUNDLES)) {
+    if (!items_el || !items_el.getAttribute('node').startsWith(Strophe.NS.OMEMO_BUNDLES)) {
         return;
     }
-    const device_id = items_el.getAttribute("node").split(":")[1];
-    const jid = stanza.getAttribute("from");
+    const device_id = items_el.getAttribute('node').split(':')[1];
+    const jid = stanza.getAttribute('from');
     const bundle_el = sizzle(`item > bundle`, items_el).pop();
     const devicelist = await api.omemo.devicelists.get(jid, true);
-    const device = devicelist.devices.get(device_id) || devicelist.devices.create({ "id": device_id, jid });
-    device.save({ "bundle": parseBundle(bundle_el) });
+    const device = devicelist.devices.get(device_id) || devicelist.devices.create({ 'id': device_id, jid });
+    device.save({ 'bundle': parseBundle(bundle_el) });
 }
 
 /**
@@ -728,27 +728,27 @@ async function updateDevicesFromStanza(stanza) {
     if (!items_el) return;
 
     const device_selector = `item list[xmlns="${Strophe.NS.OMEMO}"] device`;
-    const device_ids = sizzle(device_selector, items_el).map((d) => d.getAttribute("id"));
-    const jid = stanza.getAttribute("from");
+    const device_ids = sizzle(device_selector, items_el).map((d) => d.getAttribute('id'));
+    const jid = stanza.getAttribute('from');
     const devicelist = await api.omemo.devicelists.get(jid, true);
     const devices = devicelist.devices;
-    const removed_ids = devices.pluck("id").filter((id) => !device_ids.includes(id));
+    const removed_ids = devices.pluck('id').filter((id) => !device_ids.includes(id));
 
-    const bare_jid = _converse.session.get("bare_jid");
+    const bare_jid = _converse.session.get('bare_jid');
 
     removed_ids.forEach(
         /** @param {string} id */ (id) => {
-            if (jid === bare_jid && id === _converse.state.omemo_store.get("device_id")) {
+            if (jid === bare_jid && id === _converse.state.omemo_store.get('device_id')) {
                 return; // We don't set the current device as inactive
             }
-            devices.get(id).save("active", false);
+            devices.get(id).save('active', false);
         }
     );
     device_ids.forEach(
         /** @param {string} device_id */ (device_id) => {
             const device = devices.get(device_id);
             if (device) {
-                device.save("active", true);
+                device.save('active', true);
             } else {
                 devices.create({ id: device_id, jid });
             }
@@ -767,7 +767,7 @@ async function updateDevicesFromStanza(stanza) {
 async function handlePEPPush(message) {
     try {
         if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"]`, message).length) {
-            await api.waitUntil("OMEMOInitialized");
+            await api.waitUntil('OMEMOInitialized');
             await updateDevicesFromStanza(message);
             await updateBundleFromStanza(message);
         }
@@ -787,15 +787,15 @@ export function registerPEPPushHandler() {
             return true;
         },
         null,
-        "message",
-        "headline"
+        'message',
+        'headline'
     );
 }
 
 export async function restoreOMEMOSession() {}
 
 async function fetchDeviceLists() {
-    const bare_jid = _converse.session.get("bare_jid");
+    const bare_jid = _converse.session.get('bare_jid');
 
     _converse.state.devicelists = new DeviceLists();
     const id = `converse.devicelists-${bare_jid}`;
@@ -822,8 +822,8 @@ export async function initOMEMO(reconnecting) {
     if (reconnecting) {
         return;
     }
-    if (!_converse.state.config.get("trusted") || api.settings.get("clear_cache_on_logout")) {
-        log.warn("Not initializing OMEMO, since this browser is not trusted or clear_cache_on_logout is set to true");
+    if (!_converse.state.config.get('trusted') || api.settings.get('clear_cache_on_logout')) {
+        log.warn('Not initializing OMEMO, since this browser is not trusted or clear_cache_on_logout is set to true');
         return;
     }
     try {
@@ -831,7 +831,7 @@ export async function initOMEMO(reconnecting) {
         await api.omemo.session.restore();
         await _converse.state.omemo_store.publishBundle();
     } catch (e) {
-        log.error("Could not initialize OMEMO support");
+        log.error('Could not initialize OMEMO support');
         log.error(e);
         return;
     }
@@ -840,7 +840,7 @@ export async function initOMEMO(reconnecting) {
      * @event _converse#OMEMOInitialized
      * @example _converse.api.listen.on('OMEMOInitialized', () => { ... });
      */
-    api.trigger("OMEMOInitialized");
+    api.trigger('OMEMOInitialized');
 }
 
 /**
@@ -848,36 +848,36 @@ export async function initOMEMO(reconnecting) {
  * @param {import('@converse/headless/types/plugins/muc/occupant').default} occupant
  */
 async function onOccupantAdded(chatroom, occupant) {
-    if (occupant.isSelf() || !chatroom.features.get("nonanonymous") || !chatroom.features.get("membersonly")) {
+    if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) {
         return;
     }
-    if (chatroom.get("omemo_active")) {
-        const supported = await contactHasOMEMOSupport(occupant.get("jid"));
+    if (chatroom.get('omemo_active')) {
+        const supported = await contactHasOMEMOSupport(occupant.get('jid'));
         if (!supported) {
             chatroom.createMessage({
-                "message": __(
+                'message': __(
                     "%1$s doesn't appear to have a client that supports OMEMO. " +
-                        "Encrypted chat will no longer be possible in this grouchat.",
-                    occupant.get("nick")
+                        'Encrypted chat will no longer be possible in this grouchat.',
+                    occupant.get('nick')
                 ),
-                "type": "error",
+                'type': 'error',
             });
-            chatroom.save({ "omemo_active": false, "omemo_supported": false });
+            chatroom.save({ 'omemo_active': false, 'omemo_supported': false });
         }
     }
 }
 
 async function checkOMEMOSupported(chatbox) {
     let supported;
-    if (chatbox.get("type") === CHATROOMS_TYPE) {
-        await api.waitUntil("OMEMOInitialized");
-        supported = chatbox.features.get("nonanonymous") && chatbox.features.get("membersonly");
-    } else if (chatbox.get("type") === PRIVATE_CHAT_TYPE) {
-        supported = await contactHasOMEMOSupport(chatbox.get("jid"));
+    if (chatbox.get('type') === CHATROOMS_TYPE) {
+        await api.waitUntil('OMEMOInitialized');
+        supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly');
+    } else if (chatbox.get('type') === PRIVATE_CHAT_TYPE) {
+        supported = await contactHasOMEMOSupport(chatbox.get('jid'));
     }
-    chatbox.set("omemo_supported", !!supported);
-    if (supported && api.settings.get("omemo_default")) {
-        chatbox.set("omemo_active", true);
+    chatbox.set('omemo_supported', !!supported);
+    if (supported && api.settings.get('omemo_default')) {
+        chatbox.set('omemo_active', true);
     }
 }
 
@@ -887,14 +887,14 @@ async function checkOMEMOSupported(chatbox) {
 function toggleOMEMO(ev) {
     ev.stopPropagation();
     ev.preventDefault();
-    const toolbar_el = u.ancestor(ev.target, "converse-chat-toolbar");
-    if (!toolbar_el.model.get("omemo_supported")) {
+    const toolbar_el = u.ancestor(ev.target, 'converse-chat-toolbar');
+    if (!toolbar_el.model.get('omemo_supported')) {
         let messages;
-        if (toolbar_el.model.get("type") === CHATROOMS_TYPE) {
+        if (toolbar_el.model.get('type') === CHATROOMS_TYPE) {
             messages = [
                 __(
-                    "Cannot use end-to-end encryption in this groupchat, " +
-                        "either the groupchat has some anonymity or not all participants support OMEMO."
+                    'Cannot use end-to-end encryption in this groupchat, ' +
+                        'either the groupchat has some anonymity or not all participants support OMEMO.'
                 ),
             ];
         } else {
@@ -905,9 +905,9 @@ function toggleOMEMO(ev) {
                 ),
             ];
         }
-        return api.alert("error", __("Error"), messages);
+        return api.alert('error', __('Error'), messages);
     }
-    toolbar_el.model.save({ "omemo_active": !toolbar_el.model.get("omemo_active") });
+    toolbar_el.model.save({ 'omemo_active': !toolbar_el.model.get('omemo_active') });
 }
 
 /**
@@ -916,24 +916,24 @@ function toggleOMEMO(ev) {
  */
 export function getOMEMOToolbarButton(toolbar_el, buttons) {
     const model = toolbar_el.model;
-    const is_muc = model.get("type") === CHATROOMS_TYPE;
+    const is_muc = model.get('type') === CHATROOMS_TYPE;
     let title;
-    if (model.get("omemo_supported")) {
-        const i18n_plaintext = __("Messages are being sent in plaintext");
-        const i18n_encrypted = __("Messages are sent encrypted");
-        title = model.get("omemo_active") ? i18n_encrypted : i18n_plaintext;
+    if (model.get('omemo_supported')) {
+        const i18n_plaintext = __('Messages are being sent in plaintext');
+        const i18n_encrypted = __('Messages are sent encrypted');
+        title = model.get('omemo_active') ? i18n_encrypted : i18n_plaintext;
     } else if (is_muc) {
         title = __(
-            "This groupchat needs to be members-only and non-anonymous in " +
-                "order to support OMEMO encrypted messages"
+            'This groupchat needs to be members-only and non-anonymous in ' +
+                'order to support OMEMO encrypted messages'
         );
     } else {
-        title = __("OMEMO encryption is not supported");
+        title = __('OMEMO encryption is not supported');
     }
 
     let color;
-    if (model.get("omemo_supported")) {
-        if (model.get("omemo_active")) {
+    if (model.get('omemo_supported')) {
+        if (model.get('omemo_active')) {
             color = is_muc ? `var(--muc-color)` : `var(--chat-color)`;
         } else {
             color = `var(--error-color)`;
@@ -946,12 +946,12 @@ export function getOMEMOToolbarButton(toolbar_el, buttons) {
             type="button"
             class="btn toggle-omemo"
             title="${title}"
-            data-disabled=${!model.get("omemo_supported")}
+            data-disabled=${!model.get('omemo_supported')}
             @click=${toggleOMEMO}
         >
             <converse-icon
-                class="fa ${model.get("omemo_active") ? `fa-lock` : `fa-unlock`}"
-                path-prefix="${api.settings.get("assets_path")}"
+                class="fa ${model.get('omemo_active') ? `fa-lock` : `fa-unlock`}"
+                path-prefix="${api.settings.get('assets_path')}"
                 size="1em"
                 color="${color}"
             ></converse-icon>
@@ -965,29 +965,29 @@ export function getOMEMOToolbarButton(toolbar_el, buttons) {
  * @returns {Promise<import('./device.js').default[]>}
  */
 async function getBundlesAndBuildSessions(chatbox) {
-    const no_devices_err = __("Sorry, no devices found to which we can send an OMEMO encrypted message.");
+    const no_devices_err = __('Sorry, no devices found to which we can send an OMEMO encrypted message.');
     let devices;
     if (chatbox instanceof MUC) {
         const collections = await Promise.all(
             chatbox.occupants.map(
                 /** @param {import('@converse/headless/types/plugins/muc/occupant').default} o */
-                (o) => getDevicesForContact(o.get("jid"))
+                (o) => getDevicesForContact(o.get('jid'))
             )
         );
         devices = collections.reduce((a, b) => a.concat(b.models), []);
-    } else if (chatbox.get("type") === PRIVATE_CHAT_TYPE) {
-        const their_devices = await getDevicesForContact(chatbox.get("jid"));
+    } else if (chatbox.get('type') === PRIVATE_CHAT_TYPE) {
+        const their_devices = await getDevicesForContact(chatbox.get('jid'));
         if (their_devices.length === 0) {
             throw new UserFacingError(no_devices_err);
         }
-        const bare_jid = _converse.session.get("bare_jid");
+        const bare_jid = _converse.session.get('bare_jid');
         const own_list = await api.omemo.devicelists.get(bare_jid);
         const own_devices = own_list.devices;
         devices = [...own_devices.models, ...their_devices.models];
     }
     // Filter out our own device
-    const id = _converse.state.omemo_store.get("device_id");
-    devices = devices.filter(/** @param {import('./device.js').default} d */ (d) => d.get("id") !== id);
+    const id = _converse.state.omemo_store.get('device_id');
+    devices = devices.filter(/** @param {import('./device.js').default} d */ (d) => d.get('id') !== id);
 
     // Fetch bundles if necessary
     await Promise.all(devices.map((d) => d.getBundle()));
@@ -1009,7 +1009,7 @@ async function getBundlesAndBuildSessions(chatbox) {
  * @param {import('./device.js').default} device
  */
 function encryptKey(key_and_tag, device) {
-    return getSessionCipher(device.get("jid"), device.get("id"))
+    return getSessionCipher(device.get('jid'), device.get('id'))
         .encrypt(key_and_tag)
         .then((payload) => ({ payload, device }));
 }
@@ -1022,14 +1022,14 @@ function encryptKey(key_and_tag, device) {
 export async function createOMEMOMessageStanza(chat, data) {
     let { stanza } = data;
     const { message } = data;
-    if (!message.get("is_encrypted")) {
+    if (!message.get('is_encrypted')) {
         return data;
     }
-    if (!message.get("body")) {
-        throw new Error("No message body to encrypt!");
+    if (!message.get('body')) {
+        throw new Error('No message body to encrypt!');
     }
     const devices = await getBundlesAndBuildSessions(chat);
-    const { key_and_tag, iv, payload } = await omemo.encryptMessage(message.get("plaintext"));
+    const { key_and_tag, iv, payload } = await omemo.encryptMessage(message.get('plaintext'));
 
     // The 16 bytes key and the GCM authentication tag (The tag
     // SHOULD have at least 128 bit) are concatenated and for each
@@ -1039,7 +1039,7 @@ export async function createOMEMOMessageStanza(chat, data) {
     // long-standing SignalProtocol session.
     const dicts = await Promise.all(
         devices
-            .filter((device) => device.get("trusted") != UNTRUSTED && device.get("active"))
+            .filter((device) => device.get('trusted') != UNTRUSTED && device.get('active'))
             .map((device) => encryptKey(key_and_tag, device))
     );
 
@@ -1053,13 +1053,13 @@ export async function createOMEMOMessageStanza(chat, data) {
         .cnode(
             stx`
             <encrypted xmlns="${Strophe.NS.OMEMO}">
-                <header sid="${_converse.state.omemo_store.get("device_id")}">
+                <header sid="${_converse.state.omemo_store.get('device_id')}">
                     ${dicts.map(({ payload, device }) => {
                         const prekey = 3 == parseInt(payload.type, 10);
                         if (prekey) {
-                            return stx`<key rid="${device.get("id")}" prekey="true">${btoa(payload.body)}</key>`;
+                            return stx`<key rid="${device.get('id')}" prekey="true">${btoa(payload.body)}</key>`;
                         }
-                        return stx`<key rid="${device.get("id")}">${btoa(payload.body)}</key>`;
+                        return stx`<key rid="${device.get('id')}">${btoa(payload.body)}</key>`;
                     })}
                     <iv>${iv}</iv>
                 </header>

+ 5 - 7
src/shared/components/element.js

@@ -1,28 +1,26 @@
 import { LitElement } from 'lit';
 import { EventEmitter } from '@converse/skeletor';
 
-
 export class CustomElement extends EventEmitter(LitElement) {
-
-    constructor () {
+    constructor() {
         super();
     }
 
-    createRenderRoot () {
+    createRenderRoot() {
         // Render without the shadow DOM
         return this;
     }
 
-    initialize () {
+    initialize() {
         return null;
     }
 
-    connectedCallback () {
+    connectedCallback() {
         super.connectedCallback();
         return this.initialize();
     }
 
-    disconnectedCallback () {
+    disconnectedCallback() {
         super.disconnectedCallback();
         this.stopListening();
     }