From f4f183bc4635e69481f9e2264db2f7cc446056b9 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 1 Jul 2020 15:51:21 +0200 Subject: [PATCH] Rendeer SVG icons inline to overcome cross-site restrictions on using the `use` attribute of the `` element. --- images/icons.svg | 220 +++++++++++++++++++++++++ karma.conf.js | 1 - package-lock.json | 10 -- package.json | 1 - sass/_messages.scss | 6 + sass/font-awesome.scss | 15 +- src/components/fa-icon.js | 70 ++++++++ src/components/font-awesome.js | 20 +++ src/components/help_messages.js | 12 +- src/components/icons.js | 61 +++++++ src/components/message-actions.js | 5 +- src/converse-chatboxviews.js | 7 +- src/templates/chatboxes.html | 2 - src/templates/converse.js | 9 + src/templates/directives/retraction.js | 7 +- webpack.prod.js | 1 - 16 files changed, 417 insertions(+), 30 deletions(-) create mode 100644 images/icons.svg create mode 100644 src/components/fa-icon.js create mode 100644 src/components/font-awesome.js create mode 100644 src/components/icons.js delete mode 100644 src/templates/chatboxes.html create mode 100644 src/templates/converse.js diff --git a/images/icons.svg b/images/icons.svg new file mode 100644 index 000000000..ed6df375c --- /dev/null +++ b/images/icons.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/karma.conf.js b/karma.conf.js index 7d55d0e1a..ad11ec75a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -61,7 +61,6 @@ module.exports = function(config) { ], proxies: { - "/dist/\@fortawesome/fontawesome-free/sprites/solid.svg": "/base/dist/\@fortawesome/fontawesome-free/sprites/solid.svg", "/dist/images/custom_emojis/": "/base/dist/images/custom_emojis/" }, diff --git a/package-lock.json b/package-lock.json index 072b883a0..5ac1a8388 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10000,16 +10000,6 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, - "fa-icons": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/fa-icons/-/fa-icons-0.2.0.tgz", - "integrity": "sha512-HxGOWM8gpYiilRnsOykLNyt65aC+pmJ2ulxGaWvDRsLWU9DzvN8zNoz6EIlRKJ7ytvvqpOORhxIYRndaKn36nA==", - "dev": true, - "requires": { - "@fortawesome/fontawesome-free": "^5.12.1", - "lit-element": "^2.2.1" - } - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", diff --git a/package.json b/package.json index 4e82a6c85..337efbd48 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,6 @@ "eslint": "^7.3.0", "eslint-plugin-lodash": "^7.1.0", "exports-loader": "^0.7.0", - "fa-icons": "^0.2.0", "fast-text-encoding": "^1.0.2", "file-loader": "^6.0.0", "html-webpack-plugin": "^4.2.0", diff --git a/sass/_messages.scss b/sass/_messages.scss index 1641e9a42..af0e93276 100644 --- a/sass/_messages.scss +++ b/sass/_messages.scss @@ -265,6 +265,12 @@ padding: 0.5em 1em; text-align: left; white-space: nowrap; + + converse-icon { + margin-right: 0.25em; + } + + &:hover { color: var(--text-color); background-color: var(--list-item-hover-color); diff --git a/sass/font-awesome.scss b/sass/font-awesome.scss index 9794fee51..253dbb8bf 100644 --- a/sass/font-awesome.scss +++ b/sass/font-awesome.scss @@ -67,25 +67,28 @@ #conversejs, .converse-website { /* Apparent font-awesome bug? The circle has some kind of bottom margin */ - fa-icon:before { + converse-icon:before { content: none !important; } - .far:not(fa-icon) { + .far:not(converse-icon) { font-family: 'ConverseFontAwesomeRegular' !important; font-weight: 400; } - .fa:not(fa-icon), - .fas:not(fa-icon) { + .fa:not(converse-icon), + .fas:not(converse-icon) { font-family: 'ConverseFontAwesomeSolid' !important; font-weight: 900; } - .fab:not(fa-icon) { + .fab:not(converse-icon) { font-family: 'ConverseFontAwesomeBrands'; } - .fa:not(fa-icon), .far:not(fa-icon), .fas:not(fa-icon), .fab:not(fa-icon) { + .fa:not(converse-icon), + .far:not(converse-icon), + .fas:not(converse-icon), + .fab:not(converse-icon) { display: inline-block; font-size: inherit; text-rendering: auto; diff --git a/src/components/fa-icon.js b/src/components/fa-icon.js new file mode 100644 index 000000000..3bd117cdf --- /dev/null +++ b/src/components/fa-icon.js @@ -0,0 +1,70 @@ +import { html, css } from 'lit-element'; +import { CustomElement } from './element.js'; + + +class ConverseIcon extends CustomElement { + + static get properties () { + return { + color: String, + class_name: { attribute: "class" }, + style: String, + size: String + }; + } + + static get styles () { + return css` + :host { + display: inline-block; + padding: 0; + margin: 0; + } + :host svg { + fill: var(--fa-icon-fill-color, currentcolor); + width: var(--fa-icon-width, 19px); + height: var(--fa-icon-height, 19px); + } + `; + } + + getSources () { + const get_prefix = class_name => { + const data = class_name.split(" "); + return ['solid', normalizeIconName(data[1])]; + }; + const normalizeIconName = name => { + const icon = name.replace("fa-", ""); + return icon; + }; + const data = get_prefix(this.class_name); + return `#${data[1]}`; + } + + constructor () { + super(); + this.class_name = ""; + this.style = ""; + this.size = ""; + this.color = ""; + } + + firstUpdated () { + this.src = this.getSources(); + } + + _parseStyles () { + return ` + ${this.size ? `width: ${this.size};` : ''} + ${this.size ? `height: ${this.size};` : ''} + ${this.color ? `fill: ${this.color};` : ''} + ${this.style} + `; + } + + render () { + return html` `; + } +} + +customElements.define("converse-icon", ConverseIcon); diff --git a/src/components/font-awesome.js b/src/components/font-awesome.js new file mode 100644 index 000000000..93d86b0fa --- /dev/null +++ b/src/components/font-awesome.js @@ -0,0 +1,20 @@ +import { CustomElement } from './element.js'; +import { html } from "lit-element"; +import { unsafeSVG } from 'lit-html/directives/unsafe-svg.js'; +import { until } from 'lit-html/directives/until.js'; + + +export class FontAwesome extends CustomElement { + + constructor () { + super(); + const promise = import(/*webpackChunkName: "icons" */ '../../images/icons.svg'); + this.data = promise.then(d => html`${unsafeSVG(d.default())}`); + } + + render () { // eslint-disable-line class-methods-use-this + return html`${until(this.data, '')}`; + } +} + +window.customElements.define('converse-fontawesome', FontAwesome); diff --git a/src/components/help_messages.js b/src/components/help_messages.js index 88070d165..e97dea94f 100644 --- a/src/components/help_messages.js +++ b/src/components/help_messages.js @@ -1,7 +1,7 @@ -import 'fa-icons'; -import xss from "xss/dist/xss"; +import './icons.js'; +import xss from 'xss/dist/xss'; import { CustomElement } from './element.js'; -import { _converse, api } from "@converse/headless/converse-core"; +import { _converse, api } from '@converse/headless/converse-core'; import { html } from 'lit-element'; import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'; @@ -21,7 +21,11 @@ class ChatHelp extends CustomElement { const icon_color = this.chat_type === _converse.CHATROOMS_TYPE ? 'var(--chatroom-head-bg-color)' : 'var(--chat-head-color)'; const isodate = (new Date()).toISOString(); return [ - html``, + html``, ...this.messages.map(m => this.renderHelpMessage({ isodate, 'markup': xss.filterXSS(m, {'whiteList': {'strong': []}}) diff --git a/src/components/icons.js b/src/components/icons.js new file mode 100644 index 000000000..a7264f1d6 --- /dev/null +++ b/src/components/icons.js @@ -0,0 +1,61 @@ +/** + * @module icons.js + * @copyright Alfredo Medrano Sánchez and the Converse.js contributors + * @description + * Component inspired by the one from fa-icons + * https://github.com/obsidiansoft-io/fa-icons/blob/master/LICENSE + * @license Mozilla Public License (MPLv2) + */ + +import { html, css } from 'lit-element'; +import { CustomElement } from './element.js'; + + +class ConverseIcon extends CustomElement { + + static get properties () { + return { + color: String, + class_name: { attribute: "class" }, + style: String, + size: String + }; + } + + static get styles () { + return css` + :host { + display: inline-block; + padding: 0; + margin: 0; + } + `; + } + + constructor () { + super(); + this.class_name = ""; + this.style = ""; + this.size = ""; + this.color = ""; + } + + getSource () { + return `#${this.class_name.split(" ")[1].replace("fa-", "")}`; + } + + getStyles () { + return ` + ${this.size ? `width: ${this.size};` : ''} + ${this.size ? `height: ${this.size};` : ''} + ${this.color ? `fill: ${this.color};` : ''} + ${this.style} + `; + } + + render () { + return html` `; + } +} + +customElements.define("converse-icon", ConverseIcon); diff --git a/src/components/message-actions.js b/src/components/message-actions.js index fee24a178..1d6921337 100644 --- a/src/components/message-actions.js +++ b/src/components/message-actions.js @@ -25,7 +25,10 @@ class MessageActions extends CustomElement { static getActionsDropdownItem (o) { return html` `; diff --git a/src/converse-chatboxviews.js b/src/converse-chatboxviews.js index fe8a7e7c8..899841298 100644 --- a/src/converse-chatboxviews.js +++ b/src/converse-chatboxviews.js @@ -6,10 +6,11 @@ import "@converse/headless/converse-chatboxes"; import tpl_avatar from "templates/avatar.svg"; import tpl_background_logo from "templates/background_logo.html"; -import tpl_chatboxes from "templates/chatboxes.html"; +import tpl_converse from "templates/converse.js"; import { Overview } from "@converse/skeletor/src/overview"; import { View } from "@converse/skeletor/src/view"; import { _converse, api, converse } from "@converse/headless/converse-core"; +import { render } from "lit-html"; import { result } from "lodash-es"; const u = converse.env.utils; @@ -106,10 +107,10 @@ converse.plugins.add('converse-chatboxviews', { render () { try { - this.el.innerHTML = tpl_chatboxes(); + render(tpl_converse(), this.el); } catch (e) { this._ensureElement(); - this.el.innerHTML = tpl_chatboxes(); + render(tpl_converse(), this.el); } this.row_el = this.el.querySelector('.row'); }, diff --git a/src/templates/chatboxes.html b/src/templates/chatboxes.html deleted file mode 100644 index 37ac3f930..000000000 --- a/src/templates/chatboxes.html +++ /dev/null @@ -1,2 +0,0 @@ -
-
diff --git a/src/templates/converse.js b/src/templates/converse.js new file mode 100644 index 000000000..50758538e --- /dev/null +++ b/src/templates/converse.js @@ -0,0 +1,9 @@ +import { html } from 'lit-html'; +import '../components/font-awesome.js'; + + +export default () => html` +
+
+ +`; diff --git a/src/templates/directives/retraction.js b/src/templates/directives/retraction.js index c4924bf14..4bf3e2401 100644 --- a/src/templates/directives/retraction.js +++ b/src/templates/directives/retraction.js @@ -6,7 +6,12 @@ import { directive, html } from "lit-html"; const i18n_retract_message = __('Retract this message'); const tpl_retract = (o) => html` `; diff --git a/webpack.prod.js b/webpack.prod.js index e6a1fddd1..458397571 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -21,7 +21,6 @@ module.exports = merge(common, { new CopyWebpackPlugin({ patterns: [ {from: 'sounds', to: 'sounds'}, - {from: 'node_modules/@fortawesome/fontawesome-free/sprites/solid.svg', to: '@fortawesome/fontawesome-free/sprites/solid.svg'}, {from: 'images/favicon.ico', to: 'images/favicon.ico'}, {from: 'images/custom_emojis', to: 'images/custom_emojis'}, {from: 'logo/conversejs-filled-192.png', to: 'images/logo'},