diff --git a/lib/gui/app/components/svg-icon.js b/lib/gui/app/components/svg-icon.js
index b7f70a6a..3398d5cc 100644
--- a/lib/gui/app/components/svg-icon.js
+++ b/lib/gui/app/components/svg-icon.js
@@ -29,6 +29,7 @@ const propTypes = require('prop-types')
const react2angular = require('react2angular').react2angular
const path = require('path')
const fs = require('fs')
+const analytics = require('../modules/analytics')
const MODULE_NAME = 'Etcher.Components.SVGIcon'
const angularSVGIcon = angular.module(MODULE_NAME, [])
@@ -37,6 +38,29 @@ const DEFAULT_SIZE = '40px'
const domParser = new window.DOMParser()
+/**
+ * @summary Try to parse SVG contents and return it data encoded
+ *
+ * @param {String} contents - SVG XML contents
+ * @returns {String|null}
+ *
+ * @example
+ * const encodedSVG = tryParseSVGContents('')
+ *
+ * img.src = encodedSVG
+ */
+const tryParseSVGContents = (contents) => {
+ const doc = domParser.parseFromString(contents, 'image/svg+xml')
+ const parserError = doc.querySelector('parsererror')
+ const svg = doc.querySelector('svg')
+
+ if (!parserError && svg) {
+ return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
+ }
+
+ return null
+}
+
/**
* @summary SVG element that takes both filepaths and file contents
* @type {Object}
@@ -59,31 +83,52 @@ class SVGIcon extends react.Component {
// eslint-disable-next-line no-underscore-dangle
: global.__dirname
- // This means the path to the icon should be
- // relative to *this directory*.
- // TODO: There might be a way to compute the path
- // relatively to the `index.html`.
- const imagePath = path.join(baseDirectory, 'assets', this.props.path)
+ let svgData = ''
- let contents = ''
+ _.find(this.props.contents, (content) => {
+ const attempt = tryParseSVGContents(content)
- if (_.startsWith(this.props.path, '<')) {
- contents = this.props.path
- } else {
- contents = fs.readFileSync(imagePath, {
- encoding: 'utf8'
+ if (attempt) {
+ svgData = attempt
+ return true
+ }
+
+ return false
+ })
+
+ if (!svgData) {
+ _.find(this.props.paths, (relativePath) => {
+ // This means the path to the icon should be
+ // relative to *this directory*.
+ // TODO: There might be a way to compute the path
+ // relatively to the `index.html`.
+ const imagePath = path.join(baseDirectory, 'assets', relativePath)
+
+ const contents = _.attempt(() => {
+ return fs.readFileSync(imagePath, {
+ encoding: 'utf8'
+ })
+ })
+
+ if (_.isError(contents)) {
+ analytics.logException(contents)
+ return false
+ }
+
+ const parsed = _.attempt(tryParseSVGContents, contents)
+
+ if (parsed) {
+ svgData = parsed
+ return true
+ }
+
+ return false
})
}
const width = this.props.width || DEFAULT_SIZE
const height = this.props.height || DEFAULT_SIZE
- const doc = domParser.parseFromString(contents, 'image/svg+xml')
- const parserError = doc.querySelector('parsererror')
- const svg = doc.querySelector('svg')
- const svgXml = svg && _.isNil(parserError) ? svg.outerHTML : ''
- const svgData = `data:image/svg+xml,${encodeURIComponent(svgXml)}`
-
return react.createElement('img', {
className: 'svg-icon',
style: {
@@ -108,9 +153,14 @@ class SVGIcon extends react.Component {
SVGIcon.propTypes = {
/**
- * @summary SVG contents or path to the resource
+ * @summary Paths to SVG files to be tried in succession if any fails
*/
- path: propTypes.string.isRequired,
+ paths: propTypes.array,
+
+ /**
+ * @summary List of embedded SVG contents to be tried in succession if any fails
+ */
+ contents: propTypes.array,
/**
* @summary SVG image width unit
diff --git a/lib/gui/app/index.html b/lib/gui/app/index.html
index 1f9c1a68..3eba3e61 100644
--- a/lib/gui/app/index.html
+++ b/lib/gui/app/index.html
@@ -38,7 +38,7 @@
ng-hide="state.currentName === 'success'">
-