diff --git a/web/client/embedded.html b/web/client/embedded.html
index dd9a1c8008..5c067f7f85 100644
--- a/web/client/embedded.html
+++ b/web/client/embedded.html
@@ -84,13 +84,9 @@
diff --git a/web/client/embeddedTemplate.html b/web/client/embeddedTemplate.html
index 0c121e9315..b31eb778d9 100644
--- a/web/client/embeddedTemplate.html
+++ b/web/client/embeddedTemplate.html
@@ -84,25 +84,23 @@
-
-
-
-
-
+
diff --git a/web/client/epics/widgets.js b/web/client/epics/widgets.js
index 0e57635f2e..55264d8cff 100644
--- a/web/client/epics/widgets.js
+++ b/web/client/epics/widgets.js
@@ -37,7 +37,6 @@ import { LOCATION_CHANGE } from 'connected-react-router';
import { saveAs } from 'file-saver';
import {downloadCanvasDataURL} from '../utils/FileUtils';
import converter from 'json-2-csv';
-import canvg from 'canvg-browser';
import { updateDependenciesMapOfMapList } from "../utils/WidgetsUtils";
const updateDependencyMap = (active, targetId, { dependenciesMap, mappings}) => {
@@ -218,17 +217,21 @@ export const exportWidgetImage = action$ =>
// svgOffsetY = svgOffsetY ? svgOffsetY : 0;
// svgCanv.setAttribute("width", Number.parseFloat(svgW) + left);
// svgCanv.setAttribute("height", svgH);
- canvg(canvas, svgString, {
- renderCallback: () => {
- const context = canvas.getContext("2d");
- context.globalCompositeOperation = "destination-over";
- // set background color
- context.fillStyle = '#fff'; // <- background color
- // draw background / rect on entire canvas
- context.fillRect(0, 0, canvas.width, canvas.height);
- downloadCanvasDataURL(canvas.toDataURL('image/jpeg', 1.0), `${title}.jpg`, "image/jpeg");
- }
- });
+ import('canvg-browser')
+ .then((mod) => {
+ const canvg = mod.default;
+ canvg(canvas, svgString, {
+ renderCallback: () => {
+ const context = canvas.getContext("2d");
+ context.globalCompositeOperation = "destination-over";
+ // set background color
+ context.fillStyle = '#fff'; // <- background color
+ // draw background / rect on entire canvas
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ downloadCanvasDataURL(canvas.toDataURL('image/jpeg', 1.0), `${title}.jpg`, "image/jpeg");
+ }
+ });
+ });
})
.filter( () => false);
/**
diff --git a/web/client/geostory-embedded-template.html b/web/client/geostory-embedded-template.html
index 8967e858d1..481840792f 100644
--- a/web/client/geostory-embedded-template.html
+++ b/web/client/geostory-embedded-template.html
@@ -94,24 +94,22 @@
-
-
-
-
-
+
diff --git a/web/client/geostory-embedded.html b/web/client/geostory-embedded.html
index 3d5a646e96..f01e5497f7 100644
--- a/web/client/geostory-embedded.html
+++ b/web/client/geostory-embedded.html
@@ -94,12 +94,8 @@
-
-
-
-
diff --git a/web/client/index.html b/web/client/index.html
index 86e8ffa07d..5cd64af541 100644
--- a/web/client/index.html
+++ b/web/client/index.html
@@ -84,14 +84,10 @@
-
-
-
-
diff --git a/web/client/indexTemplate.html b/web/client/indexTemplate.html
index 40ca1cd092..4d71ff0a81 100644
--- a/web/client/indexTemplate.html
+++ b/web/client/indexTemplate.html
@@ -84,26 +84,24 @@
-
-
-
-
-
+
diff --git a/web/client/components/styleeditor/hint/geocss.js b/web/client/libs/codemirror/hint/geocss.js
similarity index 100%
rename from web/client/components/styleeditor/hint/geocss.js
rename to web/client/libs/codemirror/hint/geocss.js
diff --git a/web/client/components/styleeditor/mode/geocss.js b/web/client/libs/codemirror/mode/geocss.js
similarity index 100%
rename from web/client/components/styleeditor/mode/geocss.js
rename to web/client/libs/codemirror/mode/geocss.js
diff --git a/web/client/libs/codemirror/react-codemirror-suspense.js b/web/client/libs/codemirror/react-codemirror-suspense.js
new file mode 100644
index 0000000000..d0ae1e2cba
--- /dev/null
+++ b/web/client/libs/codemirror/react-codemirror-suspense.js
@@ -0,0 +1,5 @@
+
+import { lazy } from 'react';
+import withSuspense from '../../components/misc/withSuspense';
+
+export default withSuspense()(lazy(() => import('./react-codemirror2')));
diff --git a/web/client/libs/codemirror/react-codemirror2.js b/web/client/libs/codemirror/react-codemirror2.js
new file mode 100644
index 0000000000..2fe94e9b2b
--- /dev/null
+++ b/web/client/libs/codemirror/react-codemirror2.js
@@ -0,0 +1,37 @@
+import React, { forwardRef } from 'react';
+import { Controlled } from 'react-codemirror2';
+
+import 'codemirror/lib/codemirror.css';
+import 'codemirror/addon/lint/lint';
+import 'codemirror/addon/lint/json-lint';
+import 'codemirror/mode/javascript/javascript';
+import 'codemirror/mode/sql/sql';
+import "codemirror/theme/material.css";
+import "codemirror/mode/xml/xml";
+import "codemirror/addon/display/autorefresh";
+
+import 'codemirror/addon/search/searchcursor';
+import 'codemirror/addon/selection/mark-selection';
+import 'codemirror/addon/hint/show-hint.css';
+import 'codemirror/addon/hint/show-hint';
+
+import cm from 'codemirror/lib/codemirror';
+
+import geoCssMode from './mode/geocss';
+import geoCssHint from './hint/geocss';
+
+geoCssMode(cm);
+geoCssHint(cm);
+// this component has been moved in a separated file to contains all the codemirror dependencies
+// in this way we are able to use suspense and import everything only when the component is mounted
+function CodeMirror({ editorDidMount = () => {}, ...props}, ref) {
+ return (
+
editorDidMount(...args, cm)}
+ />
+ );
+}
+
+export default forwardRef(CodeMirror);
diff --git a/web/client/libs/leaflet.js b/web/client/libs/leaflet.js
deleted file mode 100644
index cbcd612c5b..0000000000
--- a/web/client/libs/leaflet.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Copyright 2015, GeoSolutions Sas.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-module.exports = window.L;
diff --git a/web/client/components/misc/quillmodules/ResizeModule.js b/web/client/libs/quill/modules/ResizeModule.js
similarity index 100%
rename from web/client/components/misc/quillmodules/ResizeModule.js
rename to web/client/libs/quill/modules/ResizeModule.js
diff --git a/web/client/components/misc/quillmodules/__tests__/ResizeModule-test.js b/web/client/libs/quill/modules/__tests__/ResizeModule-test.js
similarity index 98%
rename from web/client/components/misc/quillmodules/__tests__/ResizeModule-test.js
rename to web/client/libs/quill/modules/__tests__/ResizeModule-test.js
index 75bfee764e..1c31b942c8 100644
--- a/web/client/components/misc/quillmodules/__tests__/ResizeModule-test.js
+++ b/web/client/libs/quill/modules/__tests__/ResizeModule-test.js
@@ -11,7 +11,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import ReactQuill from 'react-quill';
-import resizeModuleComp from '../../../misc/quillmodules/ResizeModule';
+import resizeModuleComp from '../ResizeModule';
const {Quill} = ReactQuill;
const {ResizeModule, IFrame, toolbarConfig, Toolbar, DisplaySize, Resize} = resizeModuleComp(Quill);
diff --git a/web/client/components/misc/quillmodules/assets/css/resizemodule.css b/web/client/libs/quill/modules/assets/css/resizemodule.css
similarity index 100%
rename from web/client/components/misc/quillmodules/assets/css/resizemodule.css
rename to web/client/libs/quill/modules/assets/css/resizemodule.css
diff --git a/web/client/libs/quill/react-quill-suspense.js b/web/client/libs/quill/react-quill-suspense.js
new file mode 100644
index 0000000000..4ec63a8daa
--- /dev/null
+++ b/web/client/libs/quill/react-quill-suspense.js
@@ -0,0 +1,6 @@
+
+
+import { lazy } from 'react';
+import withSuspense from '../../components/misc/withSuspense';
+
+export default withSuspense()(lazy(() => import('./react-quill')));
diff --git a/web/client/libs/quill/react-quill.js b/web/client/libs/quill/react-quill.js
new file mode 100644
index 0000000000..e340d743f9
--- /dev/null
+++ b/web/client/libs/quill/react-quill.js
@@ -0,0 +1,27 @@
+
+import { isFunction } from 'lodash';
+import React, { forwardRef } from 'react';
+import ReactQuillEditor from 'react-quill';
+import resizeModuleIFrameToolbarConfigFactory from './modules/ResizeModule';
+
+const { Quill } = ReactQuillEditor;
+
+const {ResizeModule, IFrame, toolbarConfig } = resizeModuleIFrameToolbarConfigFactory(Quill);
+
+Quill.register({
+ 'formats/video': IFrame,
+ 'modules/resizeModule': ResizeModule
+});
+
+function ReactQuill({ modules, ...props }, ref) {
+ return (
+
+ );
+}
+
+export default forwardRef(ReactQuill);
+
diff --git a/web/client/plugins/FeatureEditor.jsx b/web/client/plugins/FeatureEditor.jsx
index 06a413e198..9f283dcd6a 100644
--- a/web/client/plugins/FeatureEditor.jsx
+++ b/web/client/plugins/FeatureEditor.jsx
@@ -5,43 +5,14 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
-import React, {useEffect} from 'react';
-import {connect} from 'react-redux';
-import {createSelector, createStructuredSelector} from 'reselect';
-import {bindActionCreators} from 'redux';
-import { get, pick, isEqual } from 'lodash';
-import {compose, lifecycle} from 'recompose';
-import ReactDock from 'react-dock';
-import ContainerDimensions from 'react-container-dimensions';
-
-import Grid from '../components/data/featuregrid/FeatureGrid';
-import BorderLayout from '../components/layout/BorderLayout';
-
+import { lazy } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
import { createPlugin } from '../utils/PluginsUtils';
-import { toChangesMap} from '../utils/FeatureGridUtils';
-import { initPlugin, sizeChange, setUp, setSyncTool} from '../actions/featuregrid';
import * as epics from '../epics/featuregrid';
import featuregrid from '../reducers/featuregrid';
-import {mapLayoutValuesSelector} from '../selectors/maplayout';
-import {paginationInfo, describeSelector, wfsURLSelector, typeNameSelector} from '../selectors/query';
-import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedFeaturesSelector, getDockSize} from '../selectors/featuregrid';
-
-import {getPanels, getHeader, getFooter, getDialogs, getEmptyRowsView, getFilterRenderers} from './featuregrid/panels/index';
-import {gridTools, gridEvents, pageEvents, toolbarEvents} from './featuregrid/index';
-
-const EMPTY_ARR = [];
-const EMPTY_OBJ = {};
-
+import withSuspense from '../components/misc/withSuspense';
-const Dock = connect(createSelector(
- getDockSize,
- state => mapLayoutValuesSelector(state, {transform: true}),
- (size, dockStyle) => ({
- size,
- dockStyle
- })
-)
-)(ReactDock);
/**
* @name FeatureEditor
* @memberof plugins
@@ -161,182 +132,9 @@ const Dock = connect(createSelector(
* ```
*
*/
-const FeatureDock = (props = {
- tools: EMPTY_OBJ,
- dialogs: EMPTY_OBJ,
- select: EMPTY_ARR
-}) => {
- const virtualScroll = props.virtualScroll ?? true;
- const maxZoom = props?.pluginCfg?.maxZoom;
- const dockProps = {
- dimMode: "none",
- defaultSize: 0.35,
- fluid: true,
- isVisible: props.open,
- maxDockSize: 0.7,
- minDockSize: 0.1,
- position: "bottom",
- setDockSize: () => {},
- zIndex: 1060
- };
- // columns={[]}
- const items = props?.items ?? [];
- const toolbarItems = items.filter(({target}) => target === 'toolbar');
- // const editors = items.filter(({target}) => target === 'editors');
-
- useEffect(() => {
- props.initPlugin({virtualScroll, editingAllowedRoles: props.editingAllowedRoles, maxStoredPages: props.maxStoredPages});
- }, [
- virtualScroll,
- (props.editingAllowedRoles ?? []).join(","), // this avoids multiple calls when the array remains the equal
- props.maxStoredPages
- ]);
-
- return (
- { props.onSizeChange(size, dockProps); }}>
- {props.open &&
-
- { ({ height }) =>
- // added height to solve resize issue in firefox, edge and ie
-
- {getDialogs(props.tools)}
-
- }
-
-
- }
- );
-};
-const selector = createSelector(
- state => get(state, "featuregrid.open"),
- state => get(state, "featuregrid.customEditorsOptions"),
- state => get(state, "queryform.autocompleteEnabled"),
- state => wfsURLSelector(state),
- state => typeNameSelector(state),
- state => get(state, 'featuregrid.features') || EMPTY_ARR,
- describeSelector,
- state => get(state, "featuregrid.attributes"),
- state => get(state, "featuregrid.tools"),
- selectedFeaturesSelector,
- modeSelector,
- changesSelector,
- newFeaturesSelector,
- hasChangesSelector,
- state => get(state, 'featuregrid.focusOnEdit', false),
- state => get(state, 'featuregrid.enableColumnFilters'),
- createStructuredSelector(paginationInfo),
- state => get(state, 'featuregrid.pages'),
- state => get(state, 'featuregrid.pagination.size'),
- (open, customEditorsOptions, autocompleteEnabled, url, typeName, features = EMPTY_ARR, describe, attributes, tools, select, mode, changes, newFeatures = EMPTY_ARR, hasChanges, focusOnEdit, enableColumnFilters, pagination, pages, size) => ({
- open,
- customEditorsOptions,
- autocompleteEnabled,
- url,
- typeName,
- hasChanges,
- newFeatures,
- features,
- describe,
- attributes,
- tools,
- select,
- mode,
- focusOnEdit,
- enableColumnFilters,
- changes: toChangesMap(changes),
- pagination,
- pages,
- size
- })
-);
-const EditorPlugin = compose(
- connect(() => ({}),
- (dispatch) => ({
- onMount: bindActionCreators(setUp, dispatch),
- setSyncTool: bindActionCreators(setSyncTool, dispatch)
- })),
- lifecycle({
- componentDidMount() {
- // only the passed properties will be picked
- this.props.onMount(pick(this.props, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']));
- if (this.props.enableMapFilterSync) {
- this.props.setSyncTool(true);
- } else {
- this.props.setSyncTool(false);
- }
- },
- // TODO: fix this in contexts
- // due to multiple renders of plugins in contexts (one with default props, then with context props)
- // the options have to be updated when change.
- componentDidUpdate(oldProps) {
- const newOptions = pick(this.props, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']);
- const oldOptions = pick(oldProps, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']);
- if (!isEqual(newOptions, oldOptions) ) {
- this.props.onMount(newOptions);
- }
- if (this.props.enableMapFilterSync) {
- this.props.setSyncTool(true);
- } else {
- this.props.setSyncTool(false);
- }
- }
- }),
- connect(selector,
- (dispatch) => ({
- gridEvents: bindActionCreators(gridEvents, dispatch),
- pageEvents: bindActionCreators(pageEvents, dispatch),
- initPlugin: bindActionCreators((options) => initPlugin(options), dispatch),
- toolbarEvents: bindActionCreators(toolbarEvents, dispatch),
- gridTools: gridTools.map((t) => ({
- ...t,
- events: bindActionCreators(t.events, dispatch)
- })),
- onSizeChange: (...params) => dispatch(sizeChange(...params))
- })
- )
-)(FeatureDock);
+const EditorPlugin = connect(
+ createSelector([state => state?.featuregrid?.open], (open) => ({ open }))
+)(withSuspense(props => !!props.open)(lazy(() => import('./featuregrid/FeatureEditor'))));
export default createPlugin('FeatureEditor', {
component: EditorPlugin,
diff --git a/web/client/plugins/MapImport.jsx b/web/client/plugins/MapImport.jsx
index c152ceb9fb..c22c37e09e 100644
--- a/web/client/plugins/MapImport.jsx
+++ b/web/client/plugins/MapImport.jsx
@@ -49,8 +49,8 @@ import { mapTypeSelector } from '../selectors/maptype';
*/
export default {
MapImportPlugin: assign({loadPlugin: (resolve) => {
- require.ensure(['./import/Import'], () => {
- const Import = require('./import/Import').default;
+ import('./import/Import').then((importMod) => {
+ const Import = importMod.default;
const ImportPlugin = connect((state) => (
{
diff --git a/web/client/plugins/Print.jsx b/web/client/plugins/Print.jsx
index 75569fecc8..264dd19242 100644
--- a/web/client/plugins/Print.jsx
+++ b/web/client/plugins/Print.jsx
@@ -225,19 +225,21 @@ function mergeItems(standard, overrides) {
export default {
PrintPlugin: assign({
loadPlugin: (resolve) => {
- require.ensure('./print/index', () => {
+ Promise.all([
+ import('./print/index'),
+ import('../utils/PrintUtils')
+ ]).then(([printMod, utilsMod]) => {
+
const {
standardItems
- } = require('./print/index').default;
+ } = printMod.default;
const {
getDefaultPrintingService,
getLayoutName,
getPrintScales,
getNearestZoom
- } = require('../utils/PrintUtils');
-
-
+ } = utilsMod;
class Print extends React.Component {
static propTypes = {
map: PropTypes.object,
diff --git a/web/client/plugins/ShapeFile.jsx b/web/client/plugins/ShapeFile.jsx
index f4e521da68..afdc46ce66 100644
--- a/web/client/plugins/ShapeFile.jsx
+++ b/web/client/plugins/ShapeFile.jsx
@@ -27,8 +27,8 @@ import { createPlugin } from '../utils/PluginsUtils';
import Message from './locale/Message';
const loader = () => new Promise((resolve) => {
- require.ensure(['./shapefile/ShapeFile'], () => {
- const ShapeFile = require('./shapefile/ShapeFile').default;
+ import('./shapefile/ShapeFile').then((shapefileMod) => {
+ const ShapeFile = shapefileMod.default;
const ShapeFilePlugin = connect((state) => (
{
diff --git a/web/client/plugins/ThematicLayer.jsx b/web/client/plugins/ThematicLayer.jsx
index 6c659dc8bc..95e4ecc8bf 100644
--- a/web/client/plugins/ThematicLayer.jsx
+++ b/web/client/plugins/ThematicLayer.jsx
@@ -94,7 +94,7 @@ import { connect } from '../utils/PluginsUtils';
export default {
ThematicLayerPlugin: assign({
loadPlugin: (resolve)=> {
- require.ensure(['../components/TOC/fragments/settings/ThematicLayer'], () => {
+ import('../components/TOC/fragments/settings/ThematicLayer').then((thematicLayerMod) => {
const ThematicLayer = connect((state) => {
const customColors = state.thematic && state.thematic.colors;
return assign({}, {
@@ -130,7 +130,7 @@ export default {
onDirtyStyle: setDirty,
onInvalidInput: setInvalidInput,
onValidInput: resetInvalidInput
- })(require('../components/TOC/fragments/settings/ThematicLayer').default);
+ })(thematicLayerMod.default);
resolve(ThematicLayer);
});
}, enabler: (state) => state.layerSettings && state.layerSettings.expanded
diff --git a/web/client/plugins/__tests__/FeatureEditor-test.jsx b/web/client/plugins/__tests__/FeatureEditor-test.jsx
index 38a0cd0e2e..aab6c987c9 100644
--- a/web/client/plugins/__tests__/FeatureEditor-test.jsx
+++ b/web/client/plugins/__tests__/FeatureEditor-test.jsx
@@ -8,9 +8,9 @@
import expect from 'expect';
import React from 'react';
import ReactDOM from 'react-dom';
+import { waitFor } from '@testing-library/react';
import { getPluginForTest } from './pluginsTestUtils';
-import featuregrid from '../../reducers/featuregrid';
import FeatureEditor from "../FeatureEditor";
describe('FeatureEditor Plugin', () => {
@@ -24,28 +24,35 @@ describe('FeatureEditor Plugin', () => {
document.body.innerHTML = '';
setTimeout(done);
});
- it('render FeatureEditor plugin', () => {
- const {Plugin, store} = getPluginForTest(FeatureEditor, {featuregrid: {...featuregrid, open: true}});
+ it('render FeatureEditor plugin', (done) => {
+ const {Plugin, store} = getPluginForTest(FeatureEditor, {featuregrid: { open: true }});
ReactDOM.render( , document.getElementById("container"));
- const container = document.querySelector('.feature-grid-container');
- expect(container).toBeTruthy();
- const state = store.getState().featuregrid;
- expect(state.virtualScroll).toBe(true);
+ waitFor(() => expect(document.querySelector('.feature-grid-container')).toBeTruthy())
+ .then(() => {
+ const state = store.getState().featuregrid;
+ expect(state.virtualScroll).toBe(true);
+ done();
+ });
});
- it('onInit FeatureEditor plugin', () => {
+ it('onInit FeatureEditor plugin', (done) => {
const props = {
virtualScroll: false,
editingAllowedRoles: ['USER', 'ADMIN'],
maxStoredPages: 5
};
- const {Plugin, store} = getPluginForTest(FeatureEditor, {featuregrid});
+ const {Plugin, store} = getPluginForTest(FeatureEditor, { featuregrid: { open: true } });
ReactDOM.render( , document.getElementById("container"));
- const state = store.getState().featuregrid;
- expect(state.virtualScroll).toBeFalsy();
- expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
- expect(state.maxStoredPages).toBe(props.maxStoredPages);
+
+ waitFor(() => expect(document.querySelector('.feature-grid-container')).toBeTruthy())
+ .then(() => {
+ const state = store.getState().featuregrid;
+ expect(state.virtualScroll).toBeFalsy();
+ expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
+ expect(state.maxStoredPages).toBe(props.maxStoredPages);
+ done();
+ });
});
- it('onInit FeatureEditor plugin be-recalled when props change', () => {
+ it('onInit FeatureEditor plugin be-recalled when props change', (done) => {
const props = {
virtualScroll: false,
editingAllowedRoles: ['ADMIN'],
@@ -59,19 +66,23 @@ describe('FeatureEditor Plugin', () => {
editingAllowedRoles: ['USER', 'ADMIN'],
maxStoredPages: 5
};
- const {Plugin, store} = getPluginForTest(FeatureEditor, {featuregrid});
+ const {Plugin, store} = getPluginForTest(FeatureEditor, { featuregrid: { open: true } });
ReactDOM.render( , document.getElementById("container"));
- const state = store.getState().featuregrid;
- expect(state.virtualScroll).toBeFalsy();
- expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
- expect(state.maxStoredPages).toBe(props.maxStoredPages);
- ReactDOM.render( , document.getElementById("container"));
- const state2 = store.getState().featuregrid;
- expect(state2.virtualScroll).toBeTruthy(); // the default
- expect(state2.editingAllowedRoles).toEqual(props2.editingAllowedRoles); // changed
- expect(state2.maxStoredPages).toBe(props2.maxStoredPages);
- ReactDOM.render( , document.getElementById("container"));
- const state3 = store.getState().featuregrid;
- expect(state2.editingAllowedRoles === state3.editingAllowedRoles).toBeTruthy(); // no double call
+ waitFor(() => expect(document.querySelector('.feature-grid-container')).toBeTruthy())
+ .then(() => {
+ const state = store.getState().featuregrid;
+ expect(state.virtualScroll).toBeFalsy();
+ expect(state.editingAllowedRoles).toEqual(props.editingAllowedRoles);
+ expect(state.maxStoredPages).toBe(props.maxStoredPages);
+ ReactDOM.render( , document.getElementById("container"));
+ const state2 = store.getState().featuregrid;
+ expect(state2.virtualScroll).toBeTruthy(); // the default
+ expect(state2.editingAllowedRoles).toEqual(props2.editingAllowedRoles); // changed
+ expect(state2.maxStoredPages).toBe(props2.maxStoredPages);
+ ReactDOM.render( , document.getElementById("container"));
+ const state3 = store.getState().featuregrid;
+ expect(state2.editingAllowedRoles === state3.editingAllowedRoles).toBeTruthy(); // no double call
+ done();
+ });
});
});
diff --git a/web/client/plugins/featuregrid/FeatureEditor.jsx b/web/client/plugins/featuregrid/FeatureEditor.jsx
new file mode 100644
index 0000000000..49ab17ebaf
--- /dev/null
+++ b/web/client/plugins/featuregrid/FeatureEditor.jsx
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2017, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import React, {useEffect} from 'react';
+import {connect} from 'react-redux';
+import {createSelector, createStructuredSelector} from 'reselect';
+import {bindActionCreators} from 'redux';
+import { get, pick, isEqual } from 'lodash';
+import {compose, lifecycle} from 'recompose';
+import ReactDock from 'react-dock';
+import ContainerDimensions from 'react-container-dimensions';
+
+import Grid from '../../components/data/featuregrid/FeatureGrid';
+import BorderLayout from '../../components/layout/BorderLayout';
+import { toChangesMap} from '../../utils/FeatureGridUtils';
+import { initPlugin, sizeChange, setUp, setSyncTool} from '../../actions/featuregrid';
+import {mapLayoutValuesSelector} from '../../selectors/maplayout';
+import {paginationInfo, describeSelector, wfsURLSelector, typeNameSelector} from '../../selectors/query';
+import {modeSelector, changesSelector, newFeaturesSelector, hasChangesSelector, selectedFeaturesSelector, getDockSize} from '../../selectors/featuregrid';
+
+import {getPanels, getHeader, getFooter, getDialogs, getEmptyRowsView, getFilterRenderers} from './panels/index';
+import {gridTools, gridEvents, pageEvents, toolbarEvents} from './index';
+
+const EMPTY_ARR = [];
+const EMPTY_OBJ = {};
+
+
+const Dock = connect(createSelector(
+ getDockSize,
+ state => mapLayoutValuesSelector(state, {transform: true}),
+ (size, dockStyle) => ({
+ size,
+ dockStyle
+ })
+)
+)(ReactDock);
+/**
+ * @name FeatureEditor
+ * @memberof plugins
+ * @class
+ * @prop {object} cfg.customEditorsOptions Set of options used to connect the custom editors to the featuregrid. It contains a set of
+ * `rules`.
+ * Each rule in the `rules` array contains:
+ * - `editor`: the string name of the editor. For more information about custom editors and their specific props (`editorProps`), see {@link api/framework#components.data.featuregrid.editors.customEditors}
+ * - `regex`: An object with 2 regular expression, `attribute` and `typeName` that have to match with the specific attribute name and feature type name.
+ * - `editorProps`: the properties to pass to the specific editor.
+ * Example:
+ * ```json
+ * "customEditorsOptions": {
+ * "rules": [{
+ * "regex": {
+ * "attribute": "^NUMERIC_ATTRIBUTE_NAME$",
+ * "typeName": "^workspace:layer_name$"
+ * },
+ * "editor": "NumberEditor"
+ * }, {
+ * "regex": {
+ * "attribute": "^att_varchar_constr$",
+ * "typeName": "^test:mapstore_test$"
+ * },
+ * "editor": "DropDownEditor",
+ * "editorProps": {
+ * "values": ["Option1", "Option2", "Option3", "Option4"]
+ * }
+ * }
+ * }]
+ *}
+ * ```
+ * @prop {object} cfg.editingAllowedRoles array of user roles allowed to enter in edit mode
+ * @prop {boolean} cfg.virtualScroll default true. Activates virtualScroll. When false the grid uses normal pagination
+ * @prop {number} cfg.maxStoredPages default 5. In virtual Scroll mode determines the size of the loaded pages cache
+ * @prop {number} cfg.vsOverScan default 20. Number of rows to load above/below the visible slice of the grid
+ * @prop {number} cfg.scrollDebounce default 50. milliseconds of debounce interval between two scroll event
+ * @prop {boolean} cfg.showFilteredObject default false. Displays spatial filter selection area when true
+ * @prop {boolean} cfg.showTimeSync default false. Shows the button to enable time sync
+ * @prop {boolean} cfg.timeSync default false. If true, the timeSync is active by default.
+ * @prop {boolean} cfg.enableMapFilterSync default false. If true, the wms sync tool will be active by default.
+ * @prop {number} cfg.maxZoom the maximum zoom level for the "zoom to feature" functionality
+ * @prop {boolean} cfg.hideCloseButton hide the close button from the header
+ * @prop {boolean} cfg.hideLayerTitle hide the layer title from the header
+ * @prop {boolean} cfg.snapTool default true. Shows the button to enable snap tool.
+ * @prop {object} cfg.snapConfig object containing settings for snap tool.
+ * @prop {boolean} cfg.snapConfig.vertex activates or deactivates snapping to the vertices of vector shapes.
+ * @prop {boolean} cfg.snapConfig.edge activates or deactivates snapping to the edges of vector shapes.
+ * @prop {number} cfg.snapConfig.pixelTolerance Pixel tolerance for considering the pointer close enough to a segment or vertex for snapping.
+ * @prop {string} cfg.snapConfig.strategy defines strategy function for loading features. Supported values are "bbox" and "all".
+ * @prop {number} cfg.snapConfig.maxFeatures defines features limit for request that loads vector data of WMS layer.
+ * @prop {array} cfg.snapConfig.additionalLayers Array of additional layers to include into snapping layers list. Provides a way to include layers from "state.additionallayers"
+ *
+ * @classdesc
+ * `FeatureEditor` Plugin, also called *FeatureGrid*, provides functionalities to browse/edit data via WFS. The grid can be configured to use paging or
+ * virtual scroll mechanisms. By default virtual scroll is enabled. When on virtual scroll mode, the maxStoredPages param
+ * sets the size of loaded pages cache, while vsOverscan and scrollDebounce params determine the behavior of grid scrolling
+ * and of row loading.
+ * Furthermore it can be configured to use custom editor cells for certain layers/columns, specifying the rules to recognize them. If no rule matches, then it will be used the default editor based on the dataType of that column.
+ * Example:
+ * ```json
+ * {
+ * "name": "FeatureEditor",
+ * "cfg": {
+ * "maxZoom": 21,
+ * "customEditorsOptions": {
+ * "rules": [{
+ * "regex": {
+ * "attribute": "NAME_OF_THE_ATTRIBUTE",
+ * "url": "regex to match a specific url",
+ * "typeName": "layerName"
+ * },
+ * "editor": "DropDownEditor",
+ * "editorProps": {
+ * "values": ["Opt1", "Opt2"]
+ * }
+ * }]
+ * },
+ * "editingAllowedRoles": ["ADMIN"],
+ * "snapTool": true,
+ * "snapConfig": {
+ * "vertex": true,
+ * "edge": true,
+ * "pixelTolerance": 10,
+ * "additionalLayers": [
+ * "ADDITIONAL_LAYER_ID"
+ * ],
+ * "strategy": "bbox",
+ * "maxFeatures": 4000
+ * },
+ * }
+ * }
+ * ```
+ *
+ * As plugin container, it can render additional components coming from other plugins.
+ * You can render additional buttons to the Toolbar by configuring a container with your component and `"target": 'toolbar'`.
+ * The component will receive as props all the properties passed to the featuregrid Toolbar. Some of them are :
+ * - `disabled`: tells when the toolbar is completely disabled.
+ * - `mode`: "EDIT" or "VIEW". Tells if the feature grid is in edit mode or in view mode.
+ * - `results`: the results in the table (with virtual scrolling, only the available ones).
+ * - `
+ * Example:
+ * ```javascript
+ * createPlugin('MyPlugin',{
+ * containers: {
+ * FeatureEditor: {
+ * doNotHide: true,
+ * // position 20 to 99, to put buttons before "settings" and "sync" buttons (that should be at the end).
+ * // <1 to put them on top. > 1000 to but at the end (not suggested because of #7271)
+ * position: 20,
+ * name: "MyPlugin",
+ * target: "toolbar",
+ * Component: MyComponent
+ * },
+ * }
+ * })
+ * ```
+ *
+*/
+const FeatureDock = (props = {
+ tools: EMPTY_OBJ,
+ dialogs: EMPTY_OBJ,
+ select: EMPTY_ARR
+}) => {
+ const virtualScroll = props.virtualScroll ?? true;
+ const maxZoom = props?.pluginCfg?.maxZoom;
+ const dockProps = {
+ dimMode: "none",
+ defaultSize: 0.35,
+ fluid: true,
+ isVisible: props.open,
+ maxDockSize: 0.7,
+ minDockSize: 0.1,
+ position: "bottom",
+ setDockSize: () => {},
+ zIndex: 1060
+ };
+ // columns={[]}
+ const items = props?.items ?? [];
+ const toolbarItems = items.filter(({target}) => target === 'toolbar');
+ // const editors = items.filter(({target}) => target === 'editors');
+
+ useEffect(() => {
+ props.initPlugin({virtualScroll, editingAllowedRoles: props.editingAllowedRoles, maxStoredPages: props.maxStoredPages});
+ }, [
+ virtualScroll,
+ (props.editingAllowedRoles ?? []).join(","), // this avoids multiple calls when the array remains the equal
+ props.maxStoredPages
+ ]);
+
+ return (
+ { props.onSizeChange(size, dockProps); }}>
+ {props.open &&
+
+ { ({ height }) =>
+ // added height to solve resize issue in firefox, edge and ie
+
+ {getDialogs(props.tools)}
+
+ }
+
+
+ }
+ );
+};
+const selector = createSelector(
+ state => get(state, "featuregrid.open"),
+ state => get(state, "featuregrid.customEditorsOptions"),
+ state => get(state, "queryform.autocompleteEnabled"),
+ state => wfsURLSelector(state),
+ state => typeNameSelector(state),
+ state => get(state, 'featuregrid.features') || EMPTY_ARR,
+ describeSelector,
+ state => get(state, "featuregrid.attributes"),
+ state => get(state, "featuregrid.tools"),
+ selectedFeaturesSelector,
+ modeSelector,
+ changesSelector,
+ newFeaturesSelector,
+ hasChangesSelector,
+ state => get(state, 'featuregrid.focusOnEdit', false),
+ state => get(state, 'featuregrid.enableColumnFilters'),
+ createStructuredSelector(paginationInfo),
+ state => get(state, 'featuregrid.pages'),
+ state => get(state, 'featuregrid.pagination.size'),
+ (open, customEditorsOptions, autocompleteEnabled, url, typeName, features = EMPTY_ARR, describe, attributes, tools, select, mode, changes, newFeatures = EMPTY_ARR, hasChanges, focusOnEdit, enableColumnFilters, pagination, pages, size) => ({
+ open,
+ customEditorsOptions,
+ autocompleteEnabled,
+ url,
+ typeName,
+ hasChanges,
+ newFeatures,
+ features,
+ describe,
+ attributes,
+ tools,
+ select,
+ mode,
+ focusOnEdit,
+ enableColumnFilters,
+ changes: toChangesMap(changes),
+ pagination,
+ pages,
+ size
+ })
+);
+const EditorPlugin = compose(
+ connect(() => ({}),
+ (dispatch) => ({
+ onMount: bindActionCreators(setUp, dispatch),
+ setSyncTool: bindActionCreators(setSyncTool, dispatch)
+ })),
+ lifecycle({
+ componentDidMount() {
+ // only the passed properties will be picked
+ this.props.onMount(pick(this.props, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']));
+ if (this.props.enableMapFilterSync) {
+ this.props.setSyncTool(true);
+ } else {
+ this.props.setSyncTool(false);
+ }
+ },
+ // TODO: fix this in contexts
+ // due to multiple renders of plugins in contexts (one with default props, then with context props)
+ // the options have to be updated when change.
+ componentDidUpdate(oldProps) {
+ const newOptions = pick(this.props, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']);
+ const oldOptions = pick(oldProps, ['showFilteredObject', 'showTimeSync', 'timeSync', 'customEditorsOptions']);
+ if (!isEqual(newOptions, oldOptions) ) {
+ this.props.onMount(newOptions);
+ }
+ if (this.props.enableMapFilterSync) {
+ this.props.setSyncTool(true);
+ } else {
+ this.props.setSyncTool(false);
+ }
+ }
+ }),
+ connect(selector,
+ (dispatch) => ({
+ gridEvents: bindActionCreators(gridEvents, dispatch),
+ pageEvents: bindActionCreators(pageEvents, dispatch),
+ initPlugin: bindActionCreators((options) => initPlugin(options), dispatch),
+ toolbarEvents: bindActionCreators(toolbarEvents, dispatch),
+ gridTools: gridTools.map((t) => ({
+ ...t,
+ events: bindActionCreators(t.events, dispatch)
+ })),
+ onSizeChange: (...params) => dispatch(sizeChange(...params))
+ })
+ )
+)(FeatureDock);
+
+export default EditorPlugin;
diff --git a/web/client/plugins/timeline/Timeline.jsx b/web/client/plugins/timeline/Timeline.jsx
index 02f94be6f8..3d3ea4dc55 100644
--- a/web/client/plugins/timeline/Timeline.jsx
+++ b/web/client/plugins/timeline/Timeline.jsx
@@ -5,12 +5,13 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
-import React from 'react';
+import React, { lazy } from 'react';
import { connect } from 'react-redux';
import { isString, isObject, differenceBy, isNil, some } from 'lodash';
import { currentTimeSelector } from '../../selectors/dimension';
import { selectTime, selectLayer, onRangeChanged } from '../../actions/timeline';
+import withSuspense from '../../components/misc/withSuspense';
import {
itemsSelector,
@@ -214,7 +215,7 @@ const enhance = compose(
{white: true}
)
);
-import Timeline from '../../components/time/TimelineComponent';
+const Timeline = withSuspense()(lazy(() => import( /* webpackChunkName: 'components/TimelineComponent' */ '../../components/time/TimelineComponent')));
export default enhance(Timeline);
diff --git a/web/client/reducers/mapInfo.js b/web/client/reducers/mapInfo.js
index 2f66efec11..cae7ee3417 100644
--- a/web/client/reducers/mapInfo.js
+++ b/web/client/reducers/mapInfo.js
@@ -6,6 +6,11 @@
* LICENSE file in the root directory of this source tree.
*/
+import assign from 'object-assign';
+import { findIndex, isUndefined, isEmpty } from 'lodash';
+import buffer from 'turf-buffer';
+import intersect from 'turf-intersect';
+
import {
ERROR_FEATURE_INFO,
EXCEPTIONS_FEATURE_INFO,
@@ -37,8 +42,6 @@ import {
import { MAP_CONFIG_LOADED } from '../actions/config';
import { RESET_CONTROLS } from '../actions/controls';
-import assign from 'object-assign';
-import { findIndex, isUndefined, isEmpty } from 'lodash';
import { MAP_TYPE_CHANGED } from './../actions/maptype';
import { getValidator } from '../utils/MapInfoUtils';
@@ -338,8 +341,6 @@ function mapInfo(state = initState, action) {
});
}
case GET_VECTOR_INFO: {
- const buffer = require('turf-buffer');
- const intersect = require('turf-intersect');
const point = {
"type": "Feature",
"properties": {},
diff --git a/web/client/simple.html b/web/client/simple.html
index 3001ec93d6..d16c950044 100644
--- a/web/client/simple.html
+++ b/web/client/simple.html
@@ -5,13 +5,8 @@
MapStore HomePage
-
-
-
-
-
diff --git a/web/client/themes/default/icons.less b/web/client/themes/default/icons.less
index c015efe9e9..b00bedab8f 100644
--- a/web/client/themes/default/icons.less
+++ b/web/client/themes/default/icons.less
@@ -2,11 +2,7 @@
@font-face {
font-family: "mapstore2";
- src:url("icons/icons.eot");
- src:url("icons/icons.eot?#iefix") format("embedded-opentype"),
- url("icons/icons.woff") format("woff"),
- url("icons/icons.ttf") format("truetype"),
- url("icons/icons.svg#mapstore2") format("svg");
+ src:url("icons/icons.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
diff --git a/web/client/themes/default/icons.template.hbs b/web/client/themes/default/icons.template.hbs
index b24f8e3b2b..94d0b5cf01 100644
--- a/web/client/themes/default/icons.template.hbs
+++ b/web/client/themes/default/icons.template.hbs
@@ -2,11 +2,7 @@
@font-face {
font-family: "mapstore2";
- src:url("icons/icons.eot");
- src:url("icons/icons.eot?#iefix") format("embedded-opentype"),
- url("icons/icons.woff") format("woff"),
- url("icons/icons.ttf") format("truetype"),
- url("icons/icons.svg#mapstore2") format("svg");
+ src:url("icons/icons.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
diff --git a/web/client/utils/MarkerUtils.js b/web/client/utils/MarkerUtils.js
index a079259196..d8c15029db 100644
--- a/web/client/utils/MarkerUtils.js
+++ b/web/client/utils/MarkerUtils.js
@@ -6,12 +6,10 @@
* LICENSE file in the root directory of this source tree.
*/
-import csstree from 'css-tree';
-
import assign from 'object-assign';
-import fontawesome from 'raw-loader!./font-awesome.txt';
+import fontawesome from './font-awesome.json';
-const css = {
+const cssJSON = {
fontawesome
};
import baseImageUrl from '../components/mapcontrols/annotations/img/markers_default.png';
@@ -23,37 +21,15 @@ const shadowImage = new Image();
baseImage.src = baseImageUrl;
shadowImage.src = shadowImageUrl;
-const getNodeOfType = (node, condition) => {
- if (condition(node)) {
- return node;
- }
- if (node.children) {
- return node.children.reduce((previous, current) => {
- const result = getNodeOfType(current, condition);
- return result || previous;
- }, null);
- }
- return null;
-};
-
const glyphs = {};
const loadGlyphs = (font) => {
- const parsedCss = csstree.toPlainObject(csstree.parse(css[font]));
- return parsedCss.children.reduce((previous, rule) => {
- if (rule.prelude) {
- const classSelector = getNodeOfType(rule.prelude, (node) => node.type === 'ClassSelector');
- const pseudoClassSelector = getNodeOfType(rule.prelude, (node) => node.type === 'PseudoClassSelector');
- if (classSelector && classSelector.name && classSelector.name.indexOf('fa-') === 0 && pseudoClassSelector && pseudoClassSelector.name === 'before') {
- const text = getNodeOfType(getNodeOfType(rule.block, (node) => node.type === 'Declaration' && node.property === 'content').value, (node) => node.type === 'String').value;
- /* eslint-disable */
- return assign(previous, {
- [classSelector.name.substring(3)]: eval("'\\u" + text.substring(2, text.length - 1) + "'")
- });
- /* eslint-enable */
- }
- }
- return previous;
+ const fontJSON = cssJSON[font];
+ return Object.keys(fontJSON).reduce((acc, key) => {
+ return {
+ ...acc,
+ [key]: eval("'\\u" + fontJSON[key] + "'") // eslint-disable-line
+ };
}, {});
};
diff --git a/web/client/utils/font-awesome.json b/web/client/utils/font-awesome.json
new file mode 100644
index 0000000000..01b9e0973d
--- /dev/null
+++ b/web/client/utils/font-awesome.json
@@ -0,0 +1 @@
+{"glass":"f000","music":"f001","search":"f002","envelope-o":"f003","heart":"f004","star":"f005","star-o":"f006","user":"f007","film":"f008","th-large":"f009","th":"f00a","th-list":"f00b","check":"f00c","times":"f00d","search-plus":"f00e","search-minus":"f010","power-off":"f011","signal":"f012","cog":"f013","trash-o":"f014","home":"f015","file-o":"f016","clock-o":"f017","road":"f018","download":"f019","arrow-circle-o-down":"f01a","arrow-circle-o-up":"f01b","inbox":"f01c","play-circle-o":"f01d","repeat":"f01e","refresh":"f021","list-alt":"f022","lock":"f023","flag":"f024","headphones":"f025","volume-off":"f026","volume-down":"f027","volume-up":"f028","qrcode":"f029","barcode":"f02a","tag":"f02b","tags":"f02c","book":"f02d","bookmark":"f02e","print":"f02f","camera":"f030","font":"f031","bold":"f032","italic":"f033","text-height":"f034","text-width":"f035","align-left":"f036","align-center":"f037","align-right":"f038","align-justify":"f039","list":"f03a","outdent":"f03b","indent":"f03c","video-camera":"f03d","picture-o":"f03e","pencil":"f040","map-marker":"f041","adjust":"f042","tint":"f043","pencil-square-o":"f044","share-square-o":"f045","check-square-o":"f046","arrows":"f047","step-backward":"f048","fast-backward":"f049","backward":"f04a","play":"f04b","pause":"f04c","stop":"f04d","forward":"f04e","fast-forward":"f050","step-forward":"f051","eject":"f052","chevron-left":"f053","chevron-right":"f054","plus-circle":"f055","minus-circle":"f056","times-circle":"f057","check-circle":"f058","question-circle":"f059","info-circle":"f05a","crosshairs":"f05b","times-circle-o":"f05c","check-circle-o":"f05d","ban":"f05e","arrow-left":"f060","arrow-right":"f061","arrow-up":"f062","arrow-down":"f063","share":"f064","expand":"f065","compress":"f066","plus":"f067","minus":"f068","asterisk":"f069","exclamation-circle":"f06a","gift":"f06b","leaf":"f06c","fire":"f06d","eye":"f06e","eye-slash":"f070","exclamation-triangle":"f071","plane":"f072","calendar":"f073","random":"f074","comment":"f075","magnet":"f076","chevron-up":"f077","chevron-down":"f078","retweet":"f079","shopping-cart":"f07a","folder":"f07b","folder-open":"f07c","arrows-v":"f07d","arrows-h":"f07e","bar-chart":"f080","twitter-square":"f081","facebook-square":"f082","camera-retro":"f083","key":"f084","cogs":"f085","comments":"f086","thumbs-o-up":"f087","thumbs-o-down":"f088","star-half":"f089","heart-o":"f08a","sign-out":"f08b","linkedin-square":"f08c","thumb-tack":"f08d","external-link":"f08e","sign-in":"f090","trophy":"f091","github-square":"f092","upload":"f093","lemon-o":"f094","phone":"f095","square-o":"f096","bookmark-o":"f097","phone-square":"f098","twitter":"f099","facebook":"f09a","github":"f09b","unlock":"f09c","credit-card":"f09d","rss":"f09e","hdd-o":"f0a0","bullhorn":"f0a1","bell":"f0f3","certificate":"f0a3","hand-o-right":"f0a4","hand-o-left":"f0a5","hand-o-up":"f0a6","hand-o-down":"f0a7","arrow-circle-left":"f0a8","arrow-circle-right":"f0a9","arrow-circle-up":"f0aa","arrow-circle-down":"f0ab","globe":"f0ac","wrench":"f0ad","tasks":"f0ae","filter":"f0b0","briefcase":"f0b1","arrows-alt":"f0b2","users":"f0c0","link":"f0c1","cloud":"f0c2","flask":"f0c3","scissors":"f0c4","files-o":"f0c5","paperclip":"f0c6","floppy-o":"f0c7","square":"f0c8","bars":"f0c9","list-ul":"f0ca","list-ol":"f0cb","strikethrough":"f0cc","underline":"f0cd","table":"f0ce","magic":"f0d0","truck":"f0d1","pinterest":"f0d2","pinterest-square":"f0d3","google-plus-square":"f0d4","google-plus":"f0d5","money":"f0d6","caret-down":"f0d7","caret-up":"f0d8","caret-left":"f0d9","caret-right":"f0da","columns":"f0db","sort":"f0dc","sort-desc":"f0dd","sort-asc":"f0de","envelope":"f0e0","linkedin":"f0e1","undo":"f0e2","gavel":"f0e3","tachometer":"f0e4","comment-o":"f0e5","comments-o":"f0e6","bolt":"f0e7","sitemap":"f0e8","umbrella":"f0e9","clipboard":"f0ea","lightbulb-o":"f0eb","exchange":"f0ec","cloud-download":"f0ed","cloud-upload":"f0ee","user-md":"f0f0","stethoscope":"f0f1","suitcase":"f0f2","bell-o":"f0a2","coffee":"f0f4","cutlery":"f0f5","file-text-o":"f0f6","building-o":"f0f7","hospital-o":"f0f8","ambulance":"f0f9","medkit":"f0fa","fighter-jet":"f0fb","beer":"f0fc","h-square":"f0fd","plus-square":"f0fe","angle-double-left":"f100","angle-double-right":"f101","angle-double-up":"f102","angle-double-down":"f103","angle-left":"f104","angle-right":"f105","angle-up":"f106","angle-down":"f107","desktop":"f108","laptop":"f109","tablet":"f10a","mobile":"f10b","circle-o":"f10c","quote-left":"f10d","quote-right":"f10e","spinner":"f110","circle":"f111","reply":"f112","github-alt":"f113","folder-o":"f114","folder-open-o":"f115","smile-o":"f118","frown-o":"f119","meh-o":"f11a","gamepad":"f11b","keyboard-o":"f11c","flag-o":"f11d","flag-checkered":"f11e","terminal":"f120","code":"f121","reply-all":"f122","star-half-o":"f123","location-arrow":"f124","crop":"f125","code-fork":"f126","chain-broken":"f127","question":"f128","info":"f129","exclamation":"f12a","superscript":"f12b","subscript":"f12c","eraser":"f12d","puzzle-piece":"f12e","microphone":"f130","microphone-slash":"f131","shield":"f132","calendar-o":"f133","fire-extinguisher":"f134","rocket":"f135","maxcdn":"f136","chevron-circle-left":"f137","chevron-circle-right":"f138","chevron-circle-up":"f139","chevron-circle-down":"f13a","html5":"f13b","css3":"f13c","anchor":"f13d","unlock-alt":"f13e","bullseye":"f140","ellipsis-h":"f141","ellipsis-v":"f142","rss-square":"f143","play-circle":"f144","ticket":"f145","minus-square":"f146","minus-square-o":"f147","level-up":"f148","level-down":"f149","check-square":"f14a","pencil-square":"f14b","external-link-square":"f14c","share-square":"f14d","compass":"f14e","caret-square-o-down":"f150","caret-square-o-up":"f151","caret-square-o-right":"f152","eur":"f153","gbp":"f154","usd":"f155","inr":"f156","jpy":"f157","rub":"f158","krw":"f159","btc":"f15a","file":"f15b","file-text":"f15c","sort-alpha-asc":"f15d","sort-alpha-desc":"f15e","sort-amount-asc":"f160","sort-amount-desc":"f161","sort-numeric-asc":"f162","sort-numeric-desc":"f163","thumbs-up":"f164","thumbs-down":"f165","youtube-square":"f166","youtube":"f167","xing":"f168","xing-square":"f169","youtube-play":"f16a","dropbox":"f16b","stack-overflow":"f16c","instagram":"f16d","flickr":"f16e","adn":"f170","bitbucket":"f171","bitbucket-square":"f172","tumblr":"f173","tumblr-square":"f174","long-arrow-down":"f175","long-arrow-up":"f176","long-arrow-left":"f177","long-arrow-right":"f178","apple":"f179","windows":"f17a","android":"f17b","linux":"f17c","dribbble":"f17d","skype":"f17e","foursquare":"f180","trello":"f181","female":"f182","male":"f183","gratipay":"f184","sun-o":"f185","moon-o":"f186","archive":"f187","bug":"f188","vk":"f189","weibo":"f18a","renren":"f18b","pagelines":"f18c","stack-exchange":"f18d","arrow-circle-o-right":"f18e","arrow-circle-o-left":"f190","caret-square-o-left":"f191","dot-circle-o":"f192","wheelchair":"f193","vimeo-square":"f194","try":"f195","plus-square-o":"f196","space-shuttle":"f197","slack":"f198","envelope-square":"f199","wordpress":"f19a","openid":"f19b","university":"f19c","graduation-cap":"f19d","yahoo":"f19e","google":"f1a0","reddit":"f1a1","reddit-square":"f1a2","stumbleupon-circle":"f1a3","stumbleupon":"f1a4","delicious":"f1a5","digg":"f1a6","pied-piper-pp":"f1a7","pied-piper-alt":"f1a8","drupal":"f1a9","joomla":"f1aa","language":"f1ab","fax":"f1ac","building":"f1ad","child":"f1ae","paw":"f1b0","spoon":"f1b1","cube":"f1b2","cubes":"f1b3","behance":"f1b4","behance-square":"f1b5","steam":"f1b6","steam-square":"f1b7","recycle":"f1b8","car":"f1b9","taxi":"f1ba","tree":"f1bb","spotify":"f1bc","deviantart":"f1bd","soundcloud":"f1be","database":"f1c0","file-pdf-o":"f1c1","file-word-o":"f1c2","file-excel-o":"f1c3","file-powerpoint-o":"f1c4","file-image-o":"f1c5","file-archive-o":"f1c6","file-audio-o":"f1c7","file-video-o":"f1c8","file-code-o":"f1c9","vine":"f1ca","codepen":"f1cb","jsfiddle":"f1cc","life-ring":"f1cd","circle-o-notch":"f1ce","rebel":"f1d0","empire":"f1d1","git-square":"f1d2","git":"f1d3","hacker-news":"f1d4","tencent-weibo":"f1d5","qq":"f1d6","weixin":"f1d7","paper-plane":"f1d8","paper-plane-o":"f1d9","history":"f1da","circle-thin":"f1db","header":"f1dc","paragraph":"f1dd","sliders":"f1de","share-alt":"f1e0","share-alt-square":"f1e1","bomb":"f1e2","futbol-o":"f1e3","tty":"f1e4","binoculars":"f1e5","plug":"f1e6","slideshare":"f1e7","twitch":"f1e8","yelp":"f1e9","newspaper-o":"f1ea","wifi":"f1eb","calculator":"f1ec","paypal":"f1ed","google-wallet":"f1ee","cc-visa":"f1f0","cc-mastercard":"f1f1","cc-discover":"f1f2","cc-amex":"f1f3","cc-paypal":"f1f4","cc-stripe":"f1f5","bell-slash":"f1f6","bell-slash-o":"f1f7","trash":"f1f8","copyright":"f1f9","at":"f1fa","eyedropper":"f1fb","paint-brush":"f1fc","birthday-cake":"f1fd","area-chart":"f1fe","pie-chart":"f200","line-chart":"f201","lastfm":"f202","lastfm-square":"f203","toggle-off":"f204","toggle-on":"f205","bicycle":"f206","bus":"f207","ioxhost":"f208","angellist":"f209","cc":"f20a","ils":"f20b","meanpath":"f20c","buysellads":"f20d","connectdevelop":"f20e","dashcube":"f210","forumbee":"f211","leanpub":"f212","sellsy":"f213","shirtsinbulk":"f214","simplybuilt":"f215","skyatlas":"f216","cart-plus":"f217","cart-arrow-down":"f218","diamond":"f219","ship":"f21a","user-secret":"f21b","motorcycle":"f21c","street-view":"f21d","heartbeat":"f21e","venus":"f221","mars":"f222","mercury":"f223","transgender":"f224","transgender-alt":"f225","venus-double":"f226","mars-double":"f227","venus-mars":"f228","mars-stroke":"f229","mars-stroke-v":"f22a","mars-stroke-h":"f22b","neuter":"f22c","genderless":"f22d","facebook-official":"f230","pinterest-p":"f231","whatsapp":"f232","server":"f233","user-plus":"f234","user-times":"f235","bed":"f236","viacoin":"f237","train":"f238","subway":"f239","medium":"f23a","y-combinator":"f23b","optin-monster":"f23c","opencart":"f23d","expeditedssl":"f23e","battery-full":"f240","battery-three-quarters":"f241","battery-half":"f242","battery-quarter":"f243","battery-empty":"f244","mouse-pointer":"f245","i-cursor":"f246","object-group":"f247","object-ungroup":"f248","sticky-note":"f249","sticky-note-o":"f24a","cc-jcb":"f24b","cc-diners-club":"f24c","clone":"f24d","balance-scale":"f24e","hourglass-o":"f250","hourglass-start":"f251","hourglass-half":"f252","hourglass-end":"f253","hourglass":"f254","hand-rock-o":"f255","hand-paper-o":"f256","hand-scissors-o":"f257","hand-lizard-o":"f258","hand-spock-o":"f259","hand-pointer-o":"f25a","hand-peace-o":"f25b","trademark":"f25c","registered":"f25d","creative-commons":"f25e","gg":"f260","gg-circle":"f261","tripadvisor":"f262","odnoklassniki":"f263","odnoklassniki-square":"f264","get-pocket":"f265","wikipedia-w":"f266","safari":"f267","chrome":"f268","firefox":"f269","opera":"f26a","internet-explorer":"f26b","television":"f26c","contao":"f26d","500px":"f26e","amazon":"f270","calendar-plus-o":"f271","calendar-minus-o":"f272","calendar-times-o":"f273","calendar-check-o":"f274","industry":"f275","map-pin":"f276","map-signs":"f277","map-o":"f278","map":"f279","commenting":"f27a","commenting-o":"f27b","houzz":"f27c","vimeo":"f27d","black-tie":"f27e","fonticons":"f280","reddit-alien":"f281","edge":"f282","credit-card-alt":"f283","codiepie":"f284","modx":"f285","fort-awesome":"f286","usb":"f287","product-hunt":"f288","mixcloud":"f289","scribd":"f28a","pause-circle":"f28b","pause-circle-o":"f28c","stop-circle":"f28d","stop-circle-o":"f28e","shopping-bag":"f290","shopping-basket":"f291","hashtag":"f292","bluetooth":"f293","bluetooth-b":"f294","percent":"f295","gitlab":"f296","wpbeginner":"f297","wpforms":"f298","envira":"f299","universal-access":"f29a","wheelchair-alt":"f29b","question-circle-o":"f29c","blind":"f29d","audio-description":"f29e","volume-control-phone":"f2a0","braille":"f2a1","assistive-listening-systems":"f2a2","american-sign-language-interpreting":"f2a3","deaf":"f2a4","glide":"f2a5","glide-g":"f2a6","sign-language":"f2a7","low-vision":"f2a8","viadeo":"f2a9","viadeo-square":"f2aa","snapchat":"f2ab","snapchat-ghost":"f2ac","snapchat-square":"f2ad","pied-piper":"f2ae","first-order":"f2b0","yoast":"f2b1","themeisle":"f2b2","google-plus-official":"f2b3","font-awesome":"f2b4","handshake-o":"f2b5","envelope-open":"f2b6","envelope-open-o":"f2b7","linode":"f2b8","address-book":"f2b9","address-book-o":"f2ba","address-card":"f2bb","address-card-o":"f2bc","user-circle":"f2bd","user-circle-o":"f2be","user-o":"f2c0","id-badge":"f2c1","id-card":"f2c2","id-card-o":"f2c3","quora":"f2c4","free-code-camp":"f2c5","telegram":"f2c6","thermometer-full":"f2c7","thermometer-three-quarters":"f2c8","thermometer-half":"f2c9","thermometer-quarter":"f2ca","thermometer-empty":"f2cb","shower":"f2cc","bath":"f2cd","podcast":"f2ce","window-maximize":"f2d0","window-minimize":"f2d1","window-restore":"f2d2","window-close":"f2d3","window-close-o":"f2d4","bandcamp":"f2d5","grav":"f2d6","etsy":"f2d7","imdb":"f2d8","ravelry":"f2d9","eercast":"f2da","microchip":"f2db","snowflake-o":"f2dc","superpowers":"f2dd","wpexplorer":"f2de","meetup":"f2e0"}
\ No newline at end of file
diff --git a/web/client/utils/font-awesome.txt b/web/client/utils/font-awesome.txt
deleted file mode 100644
index a777b75189..0000000000
--- a/web/client/utils/font-awesome.txt
+++ /dev/null
@@ -1,2136 +0,0 @@
-.fa-glass:before {
- content: "\f000";
-}
-.fa-music:before {
- content: "\f001";
-}
-.fa-search:before {
- content: "\f002";
-}
-.fa-envelope-o:before {
- content: "\f003";
-}
-.fa-heart:before {
- content: "\f004";
-}
-.fa-star:before {
- content: "\f005";
-}
-.fa-star-o:before {
- content: "\f006";
-}
-.fa-user:before {
- content: "\f007";
-}
-.fa-film:before {
- content: "\f008";
-}
-.fa-th-large:before {
- content: "\f009";
-}
-.fa-th:before {
- content: "\f00a";
-}
-.fa-th-list:before {
- content: "\f00b";
-}
-.fa-check:before {
- content: "\f00c";
-}
-.fa-remove:before,
-.fa-close:before,
-.fa-times:before {
- content: "\f00d";
-}
-.fa-search-plus:before {
- content: "\f00e";
-}
-.fa-search-minus:before {
- content: "\f010";
-}
-.fa-power-off:before {
- content: "\f011";
-}
-.fa-signal:before {
- content: "\f012";
-}
-.fa-gear:before,
-.fa-cog:before {
- content: "\f013";
-}
-.fa-trash-o:before {
- content: "\f014";
-}
-.fa-home:before {
- content: "\f015";
-}
-.fa-file-o:before {
- content: "\f016";
-}
-.fa-clock-o:before {
- content: "\f017";
-}
-.fa-road:before {
- content: "\f018";
-}
-.fa-download:before {
- content: "\f019";
-}
-.fa-arrow-circle-o-down:before {
- content: "\f01a";
-}
-.fa-arrow-circle-o-up:before {
- content: "\f01b";
-}
-.fa-inbox:before {
- content: "\f01c";
-}
-.fa-play-circle-o:before {
- content: "\f01d";
-}
-.fa-rotate-right:before,
-.fa-repeat:before {
- content: "\f01e";
-}
-.fa-refresh:before {
- content: "\f021";
-}
-.fa-list-alt:before {
- content: "\f022";
-}
-.fa-lock:before {
- content: "\f023";
-}
-.fa-flag:before {
- content: "\f024";
-}
-.fa-headphones:before {
- content: "\f025";
-}
-.fa-volume-off:before {
- content: "\f026";
-}
-.fa-volume-down:before {
- content: "\f027";
-}
-.fa-volume-up:before {
- content: "\f028";
-}
-.fa-qrcode:before {
- content: "\f029";
-}
-.fa-barcode:before {
- content: "\f02a";
-}
-.fa-tag:before {
- content: "\f02b";
-}
-.fa-tags:before {
- content: "\f02c";
-}
-.fa-book:before {
- content: "\f02d";
-}
-.fa-bookmark:before {
- content: "\f02e";
-}
-.fa-print:before {
- content: "\f02f";
-}
-.fa-camera:before {
- content: "\f030";
-}
-.fa-font:before {
- content: "\f031";
-}
-.fa-bold:before {
- content: "\f032";
-}
-.fa-italic:before {
- content: "\f033";
-}
-.fa-text-height:before {
- content: "\f034";
-}
-.fa-text-width:before {
- content: "\f035";
-}
-.fa-align-left:before {
- content: "\f036";
-}
-.fa-align-center:before {
- content: "\f037";
-}
-.fa-align-right:before {
- content: "\f038";
-}
-.fa-align-justify:before {
- content: "\f039";
-}
-.fa-list:before {
- content: "\f03a";
-}
-.fa-dedent:before,
-.fa-outdent:before {
- content: "\f03b";
-}
-.fa-indent:before {
- content: "\f03c";
-}
-.fa-video-camera:before {
- content: "\f03d";
-}
-.fa-photo:before,
-.fa-image:before,
-.fa-picture-o:before {
- content: "\f03e";
-}
-.fa-pencil:before {
- content: "\f040";
-}
-.fa-map-marker:before {
- content: "\f041";
-}
-.fa-adjust:before {
- content: "\f042";
-}
-.fa-tint:before {
- content: "\f043";
-}
-.fa-edit:before,
-.fa-pencil-square-o:before {
- content: "\f044";
-}
-.fa-share-square-o:before {
- content: "\f045";
-}
-.fa-check-square-o:before {
- content: "\f046";
-}
-.fa-arrows:before {
- content: "\f047";
-}
-.fa-step-backward:before {
- content: "\f048";
-}
-.fa-fast-backward:before {
- content: "\f049";
-}
-.fa-backward:before {
- content: "\f04a";
-}
-.fa-play:before {
- content: "\f04b";
-}
-.fa-pause:before {
- content: "\f04c";
-}
-.fa-stop:before {
- content: "\f04d";
-}
-.fa-forward:before {
- content: "\f04e";
-}
-.fa-fast-forward:before {
- content: "\f050";
-}
-.fa-step-forward:before {
- content: "\f051";
-}
-.fa-eject:before {
- content: "\f052";
-}
-.fa-chevron-left:before {
- content: "\f053";
-}
-.fa-chevron-right:before {
- content: "\f054";
-}
-.fa-plus-circle:before {
- content: "\f055";
-}
-.fa-minus-circle:before {
- content: "\f056";
-}
-.fa-times-circle:before {
- content: "\f057";
-}
-.fa-check-circle:before {
- content: "\f058";
-}
-.fa-question-circle:before {
- content: "\f059";
-}
-.fa-info-circle:before {
- content: "\f05a";
-}
-.fa-crosshairs:before {
- content: "\f05b";
-}
-.fa-times-circle-o:before {
- content: "\f05c";
-}
-.fa-check-circle-o:before {
- content: "\f05d";
-}
-.fa-ban:before {
- content: "\f05e";
-}
-.fa-arrow-left:before {
- content: "\f060";
-}
-.fa-arrow-right:before {
- content: "\f061";
-}
-.fa-arrow-up:before {
- content: "\f062";
-}
-.fa-arrow-down:before {
- content: "\f063";
-}
-.fa-mail-forward:before,
-.fa-share:before {
- content: "\f064";
-}
-.fa-expand:before {
- content: "\f065";
-}
-.fa-compress:before {
- content: "\f066";
-}
-.fa-plus:before {
- content: "\f067";
-}
-.fa-minus:before {
- content: "\f068";
-}
-.fa-asterisk:before {
- content: "\f069";
-}
-.fa-exclamation-circle:before {
- content: "\f06a";
-}
-.fa-gift:before {
- content: "\f06b";
-}
-.fa-leaf:before {
- content: "\f06c";
-}
-.fa-fire:before {
- content: "\f06d";
-}
-.fa-eye:before {
- content: "\f06e";
-}
-.fa-eye-slash:before {
- content: "\f070";
-}
-.fa-warning:before,
-.fa-exclamation-triangle:before {
- content: "\f071";
-}
-.fa-plane:before {
- content: "\f072";
-}
-.fa-calendar:before {
- content: "\f073";
-}
-.fa-random:before {
- content: "\f074";
-}
-.fa-comment:before {
- content: "\f075";
-}
-.fa-magnet:before {
- content: "\f076";
-}
-.fa-chevron-up:before {
- content: "\f077";
-}
-.fa-chevron-down:before {
- content: "\f078";
-}
-.fa-retweet:before {
- content: "\f079";
-}
-.fa-shopping-cart:before {
- content: "\f07a";
-}
-.fa-folder:before {
- content: "\f07b";
-}
-.fa-folder-open:before {
- content: "\f07c";
-}
-.fa-arrows-v:before {
- content: "\f07d";
-}
-.fa-arrows-h:before {
- content: "\f07e";
-}
-.fa-bar-chart-o:before,
-.fa-bar-chart:before {
- content: "\f080";
-}
-.fa-twitter-square:before {
- content: "\f081";
-}
-.fa-facebook-square:before {
- content: "\f082";
-}
-.fa-camera-retro:before {
- content: "\f083";
-}
-.fa-key:before {
- content: "\f084";
-}
-.fa-gears:before,
-.fa-cogs:before {
- content: "\f085";
-}
-.fa-comments:before {
- content: "\f086";
-}
-.fa-thumbs-o-up:before {
- content: "\f087";
-}
-.fa-thumbs-o-down:before {
- content: "\f088";
-}
-.fa-star-half:before {
- content: "\f089";
-}
-.fa-heart-o:before {
- content: "\f08a";
-}
-.fa-sign-out:before {
- content: "\f08b";
-}
-.fa-linkedin-square:before {
- content: "\f08c";
-}
-.fa-thumb-tack:before {
- content: "\f08d";
-}
-.fa-external-link:before {
- content: "\f08e";
-}
-.fa-sign-in:before {
- content: "\f090";
-}
-.fa-trophy:before {
- content: "\f091";
-}
-.fa-github-square:before {
- content: "\f092";
-}
-.fa-upload:before {
- content: "\f093";
-}
-.fa-lemon-o:before {
- content: "\f094";
-}
-.fa-phone:before {
- content: "\f095";
-}
-.fa-square-o:before {
- content: "\f096";
-}
-.fa-bookmark-o:before {
- content: "\f097";
-}
-.fa-phone-square:before {
- content: "\f098";
-}
-.fa-twitter:before {
- content: "\f099";
-}
-.fa-facebook-f:before,
-.fa-facebook:before {
- content: "\f09a";
-}
-.fa-github:before {
- content: "\f09b";
-}
-.fa-unlock:before {
- content: "\f09c";
-}
-.fa-credit-card:before {
- content: "\f09d";
-}
-.fa-feed:before,
-.fa-rss:before {
- content: "\f09e";
-}
-.fa-hdd-o:before {
- content: "\f0a0";
-}
-.fa-bullhorn:before {
- content: "\f0a1";
-}
-.fa-bell:before {
- content: "\f0f3";
-}
-.fa-certificate:before {
- content: "\f0a3";
-}
-.fa-hand-o-right:before {
- content: "\f0a4";
-}
-.fa-hand-o-left:before {
- content: "\f0a5";
-}
-.fa-hand-o-up:before {
- content: "\f0a6";
-}
-.fa-hand-o-down:before {
- content: "\f0a7";
-}
-.fa-arrow-circle-left:before {
- content: "\f0a8";
-}
-.fa-arrow-circle-right:before {
- content: "\f0a9";
-}
-.fa-arrow-circle-up:before {
- content: "\f0aa";
-}
-.fa-arrow-circle-down:before {
- content: "\f0ab";
-}
-.fa-globe:before {
- content: "\f0ac";
-}
-.fa-wrench:before {
- content: "\f0ad";
-}
-.fa-tasks:before {
- content: "\f0ae";
-}
-.fa-filter:before {
- content: "\f0b0";
-}
-.fa-briefcase:before {
- content: "\f0b1";
-}
-.fa-arrows-alt:before {
- content: "\f0b2";
-}
-.fa-group:before,
-.fa-users:before {
- content: "\f0c0";
-}
-.fa-chain:before,
-.fa-link:before {
- content: "\f0c1";
-}
-.fa-cloud:before {
- content: "\f0c2";
-}
-.fa-flask:before {
- content: "\f0c3";
-}
-.fa-cut:before,
-.fa-scissors:before {
- content: "\f0c4";
-}
-.fa-copy:before,
-.fa-files-o:before {
- content: "\f0c5";
-}
-.fa-paperclip:before {
- content: "\f0c6";
-}
-.fa-save:before,
-.fa-floppy-o:before {
- content: "\f0c7";
-}
-.fa-square:before {
- content: "\f0c8";
-}
-.fa-navicon:before,
-.fa-reorder:before,
-.fa-bars:before {
- content: "\f0c9";
-}
-.fa-list-ul:before {
- content: "\f0ca";
-}
-.fa-list-ol:before {
- content: "\f0cb";
-}
-.fa-strikethrough:before {
- content: "\f0cc";
-}
-.fa-underline:before {
- content: "\f0cd";
-}
-.fa-table:before {
- content: "\f0ce";
-}
-.fa-magic:before {
- content: "\f0d0";
-}
-.fa-truck:before {
- content: "\f0d1";
-}
-.fa-pinterest:before {
- content: "\f0d2";
-}
-.fa-pinterest-square:before {
- content: "\f0d3";
-}
-.fa-google-plus-square:before {
- content: "\f0d4";
-}
-.fa-google-plus:before {
- content: "\f0d5";
-}
-.fa-money:before {
- content: "\f0d6";
-}
-.fa-caret-down:before {
- content: "\f0d7";
-}
-.fa-caret-up:before {
- content: "\f0d8";
-}
-.fa-caret-left:before {
- content: "\f0d9";
-}
-.fa-caret-right:before {
- content: "\f0da";
-}
-.fa-columns:before {
- content: "\f0db";
-}
-.fa-unsorted:before,
-.fa-sort:before {
- content: "\f0dc";
-}
-.fa-sort-down:before,
-.fa-sort-desc:before {
- content: "\f0dd";
-}
-.fa-sort-up:before,
-.fa-sort-asc:before {
- content: "\f0de";
-}
-.fa-envelope:before {
- content: "\f0e0";
-}
-.fa-linkedin:before {
- content: "\f0e1";
-}
-.fa-rotate-left:before,
-.fa-undo:before {
- content: "\f0e2";
-}
-.fa-legal:before,
-.fa-gavel:before {
- content: "\f0e3";
-}
-.fa-dashboard:before,
-.fa-tachometer:before {
- content: "\f0e4";
-}
-.fa-comment-o:before {
- content: "\f0e5";
-}
-.fa-comments-o:before {
- content: "\f0e6";
-}
-.fa-flash:before,
-.fa-bolt:before {
- content: "\f0e7";
-}
-.fa-sitemap:before {
- content: "\f0e8";
-}
-.fa-umbrella:before {
- content: "\f0e9";
-}
-.fa-paste:before,
-.fa-clipboard:before {
- content: "\f0ea";
-}
-.fa-lightbulb-o:before {
- content: "\f0eb";
-}
-.fa-exchange:before {
- content: "\f0ec";
-}
-.fa-cloud-download:before {
- content: "\f0ed";
-}
-.fa-cloud-upload:before {
- content: "\f0ee";
-}
-.fa-user-md:before {
- content: "\f0f0";
-}
-.fa-stethoscope:before {
- content: "\f0f1";
-}
-.fa-suitcase:before {
- content: "\f0f2";
-}
-.fa-bell-o:before {
- content: "\f0a2";
-}
-.fa-coffee:before {
- content: "\f0f4";
-}
-.fa-cutlery:before {
- content: "\f0f5";
-}
-.fa-file-text-o:before {
- content: "\f0f6";
-}
-.fa-building-o:before {
- content: "\f0f7";
-}
-.fa-hospital-o:before {
- content: "\f0f8";
-}
-.fa-ambulance:before {
- content: "\f0f9";
-}
-.fa-medkit:before {
- content: "\f0fa";
-}
-.fa-fighter-jet:before {
- content: "\f0fb";
-}
-.fa-beer:before {
- content: "\f0fc";
-}
-.fa-h-square:before {
- content: "\f0fd";
-}
-.fa-plus-square:before {
- content: "\f0fe";
-}
-.fa-angle-double-left:before {
- content: "\f100";
-}
-.fa-angle-double-right:before {
- content: "\f101";
-}
-.fa-angle-double-up:before {
- content: "\f102";
-}
-.fa-angle-double-down:before {
- content: "\f103";
-}
-.fa-angle-left:before {
- content: "\f104";
-}
-.fa-angle-right:before {
- content: "\f105";
-}
-.fa-angle-up:before {
- content: "\f106";
-}
-.fa-angle-down:before {
- content: "\f107";
-}
-.fa-desktop:before {
- content: "\f108";
-}
-.fa-laptop:before {
- content: "\f109";
-}
-.fa-tablet:before {
- content: "\f10a";
-}
-.fa-mobile-phone:before,
-.fa-mobile:before {
- content: "\f10b";
-}
-.fa-circle-o:before {
- content: "\f10c";
-}
-.fa-quote-left:before {
- content: "\f10d";
-}
-.fa-quote-right:before {
- content: "\f10e";
-}
-.fa-spinner:before {
- content: "\f110";
-}
-.fa-circle:before {
- content: "\f111";
-}
-.fa-mail-reply:before,
-.fa-reply:before {
- content: "\f112";
-}
-.fa-github-alt:before {
- content: "\f113";
-}
-.fa-folder-o:before {
- content: "\f114";
-}
-.fa-folder-open-o:before {
- content: "\f115";
-}
-.fa-smile-o:before {
- content: "\f118";
-}
-.fa-frown-o:before {
- content: "\f119";
-}
-.fa-meh-o:before {
- content: "\f11a";
-}
-.fa-gamepad:before {
- content: "\f11b";
-}
-.fa-keyboard-o:before {
- content: "\f11c";
-}
-.fa-flag-o:before {
- content: "\f11d";
-}
-.fa-flag-checkered:before {
- content: "\f11e";
-}
-.fa-terminal:before {
- content: "\f120";
-}
-.fa-code:before {
- content: "\f121";
-}
-.fa-mail-reply-all:before,
-.fa-reply-all:before {
- content: "\f122";
-}
-.fa-star-half-empty:before,
-.fa-star-half-full:before,
-.fa-star-half-o:before {
- content: "\f123";
-}
-.fa-location-arrow:before {
- content: "\f124";
-}
-.fa-crop:before {
- content: "\f125";
-}
-.fa-code-fork:before {
- content: "\f126";
-}
-.fa-unlink:before,
-.fa-chain-broken:before {
- content: "\f127";
-}
-.fa-question:before {
- content: "\f128";
-}
-.fa-info:before {
- content: "\f129";
-}
-.fa-exclamation:before {
- content: "\f12a";
-}
-.fa-superscript:before {
- content: "\f12b";
-}
-.fa-subscript:before {
- content: "\f12c";
-}
-.fa-eraser:before {
- content: "\f12d";
-}
-.fa-puzzle-piece:before {
- content: "\f12e";
-}
-.fa-microphone:before {
- content: "\f130";
-}
-.fa-microphone-slash:before {
- content: "\f131";
-}
-.fa-shield:before {
- content: "\f132";
-}
-.fa-calendar-o:before {
- content: "\f133";
-}
-.fa-fire-extinguisher:before {
- content: "\f134";
-}
-.fa-rocket:before {
- content: "\f135";
-}
-.fa-maxcdn:before {
- content: "\f136";
-}
-.fa-chevron-circle-left:before {
- content: "\f137";
-}
-.fa-chevron-circle-right:before {
- content: "\f138";
-}
-.fa-chevron-circle-up:before {
- content: "\f139";
-}
-.fa-chevron-circle-down:before {
- content: "\f13a";
-}
-.fa-html5:before {
- content: "\f13b";
-}
-.fa-css3:before {
- content: "\f13c";
-}
-.fa-anchor:before {
- content: "\f13d";
-}
-.fa-unlock-alt:before {
- content: "\f13e";
-}
-.fa-bullseye:before {
- content: "\f140";
-}
-.fa-ellipsis-h:before {
- content: "\f141";
-}
-.fa-ellipsis-v:before {
- content: "\f142";
-}
-.fa-rss-square:before {
- content: "\f143";
-}
-.fa-play-circle:before {
- content: "\f144";
-}
-.fa-ticket:before {
- content: "\f145";
-}
-.fa-minus-square:before {
- content: "\f146";
-}
-.fa-minus-square-o:before {
- content: "\f147";
-}
-.fa-level-up:before {
- content: "\f148";
-}
-.fa-level-down:before {
- content: "\f149";
-}
-.fa-check-square:before {
- content: "\f14a";
-}
-.fa-pencil-square:before {
- content: "\f14b";
-}
-.fa-external-link-square:before {
- content: "\f14c";
-}
-.fa-share-square:before {
- content: "\f14d";
-}
-.fa-compass:before {
- content: "\f14e";
-}
-.fa-toggle-down:before,
-.fa-caret-square-o-down:before {
- content: "\f150";
-}
-.fa-toggle-up:before,
-.fa-caret-square-o-up:before {
- content: "\f151";
-}
-.fa-toggle-right:before,
-.fa-caret-square-o-right:before {
- content: "\f152";
-}
-.fa-euro:before,
-.fa-eur:before {
- content: "\f153";
-}
-.fa-gbp:before {
- content: "\f154";
-}
-.fa-dollar:before,
-.fa-usd:before {
- content: "\f155";
-}
-.fa-rupee:before,
-.fa-inr:before {
- content: "\f156";
-}
-.fa-cny:before,
-.fa-rmb:before,
-.fa-yen:before,
-.fa-jpy:before {
- content: "\f157";
-}
-.fa-ruble:before,
-.fa-rouble:before,
-.fa-rub:before {
- content: "\f158";
-}
-.fa-won:before,
-.fa-krw:before {
- content: "\f159";
-}
-.fa-bitcoin:before,
-.fa-btc:before {
- content: "\f15a";
-}
-.fa-file:before {
- content: "\f15b";
-}
-.fa-file-text:before {
- content: "\f15c";
-}
-.fa-sort-alpha-asc:before {
- content: "\f15d";
-}
-.fa-sort-alpha-desc:before {
- content: "\f15e";
-}
-.fa-sort-amount-asc:before {
- content: "\f160";
-}
-.fa-sort-amount-desc:before {
- content: "\f161";
-}
-.fa-sort-numeric-asc:before {
- content: "\f162";
-}
-.fa-sort-numeric-desc:before {
- content: "\f163";
-}
-.fa-thumbs-up:before {
- content: "\f164";
-}
-.fa-thumbs-down:before {
- content: "\f165";
-}
-.fa-youtube-square:before {
- content: "\f166";
-}
-.fa-youtube:before {
- content: "\f167";
-}
-.fa-xing:before {
- content: "\f168";
-}
-.fa-xing-square:before {
- content: "\f169";
-}
-.fa-youtube-play:before {
- content: "\f16a";
-}
-.fa-dropbox:before {
- content: "\f16b";
-}
-.fa-stack-overflow:before {
- content: "\f16c";
-}
-.fa-instagram:before {
- content: "\f16d";
-}
-.fa-flickr:before {
- content: "\f16e";
-}
-.fa-adn:before {
- content: "\f170";
-}
-.fa-bitbucket:before {
- content: "\f171";
-}
-.fa-bitbucket-square:before {
- content: "\f172";
-}
-.fa-tumblr:before {
- content: "\f173";
-}
-.fa-tumblr-square:before {
- content: "\f174";
-}
-.fa-long-arrow-down:before {
- content: "\f175";
-}
-.fa-long-arrow-up:before {
- content: "\f176";
-}
-.fa-long-arrow-left:before {
- content: "\f177";
-}
-.fa-long-arrow-right:before {
- content: "\f178";
-}
-.fa-apple:before {
- content: "\f179";
-}
-.fa-windows:before {
- content: "\f17a";
-}
-.fa-android:before {
- content: "\f17b";
-}
-.fa-linux:before {
- content: "\f17c";
-}
-.fa-dribbble:before {
- content: "\f17d";
-}
-.fa-skype:before {
- content: "\f17e";
-}
-.fa-foursquare:before {
- content: "\f180";
-}
-.fa-trello:before {
- content: "\f181";
-}
-.fa-female:before {
- content: "\f182";
-}
-.fa-male:before {
- content: "\f183";
-}
-.fa-gittip:before,
-.fa-gratipay:before {
- content: "\f184";
-}
-.fa-sun-o:before {
- content: "\f185";
-}
-.fa-moon-o:before {
- content: "\f186";
-}
-.fa-archive:before {
- content: "\f187";
-}
-.fa-bug:before {
- content: "\f188";
-}
-.fa-vk:before {
- content: "\f189";
-}
-.fa-weibo:before {
- content: "\f18a";
-}
-.fa-renren:before {
- content: "\f18b";
-}
-.fa-pagelines:before {
- content: "\f18c";
-}
-.fa-stack-exchange:before {
- content: "\f18d";
-}
-.fa-arrow-circle-o-right:before {
- content: "\f18e";
-}
-.fa-arrow-circle-o-left:before {
- content: "\f190";
-}
-.fa-toggle-left:before,
-.fa-caret-square-o-left:before {
- content: "\f191";
-}
-.fa-dot-circle-o:before {
- content: "\f192";
-}
-.fa-wheelchair:before {
- content: "\f193";
-}
-.fa-vimeo-square:before {
- content: "\f194";
-}
-.fa-turkish-lira:before,
-.fa-try:before {
- content: "\f195";
-}
-.fa-plus-square-o:before {
- content: "\f196";
-}
-.fa-space-shuttle:before {
- content: "\f197";
-}
-.fa-slack:before {
- content: "\f198";
-}
-.fa-envelope-square:before {
- content: "\f199";
-}
-.fa-wordpress:before {
- content: "\f19a";
-}
-.fa-openid:before {
- content: "\f19b";
-}
-.fa-institution:before,
-.fa-bank:before,
-.fa-university:before {
- content: "\f19c";
-}
-.fa-mortar-board:before,
-.fa-graduation-cap:before {
- content: "\f19d";
-}
-.fa-yahoo:before {
- content: "\f19e";
-}
-.fa-google:before {
- content: "\f1a0";
-}
-.fa-reddit:before {
- content: "\f1a1";
-}
-.fa-reddit-square:before {
- content: "\f1a2";
-}
-.fa-stumbleupon-circle:before {
- content: "\f1a3";
-}
-.fa-stumbleupon:before {
- content: "\f1a4";
-}
-.fa-delicious:before {
- content: "\f1a5";
-}
-.fa-digg:before {
- content: "\f1a6";
-}
-.fa-pied-piper-pp:before {
- content: "\f1a7";
-}
-.fa-pied-piper-alt:before {
- content: "\f1a8";
-}
-.fa-drupal:before {
- content: "\f1a9";
-}
-.fa-joomla:before {
- content: "\f1aa";
-}
-.fa-language:before {
- content: "\f1ab";
-}
-.fa-fax:before {
- content: "\f1ac";
-}
-.fa-building:before {
- content: "\f1ad";
-}
-.fa-child:before {
- content: "\f1ae";
-}
-.fa-paw:before {
- content: "\f1b0";
-}
-.fa-spoon:before {
- content: "\f1b1";
-}
-.fa-cube:before {
- content: "\f1b2";
-}
-.fa-cubes:before {
- content: "\f1b3";
-}
-.fa-behance:before {
- content: "\f1b4";
-}
-.fa-behance-square:before {
- content: "\f1b5";
-}
-.fa-steam:before {
- content: "\f1b6";
-}
-.fa-steam-square:before {
- content: "\f1b7";
-}
-.fa-recycle:before {
- content: "\f1b8";
-}
-.fa-automobile:before,
-.fa-car:before {
- content: "\f1b9";
-}
-.fa-cab:before,
-.fa-taxi:before {
- content: "\f1ba";
-}
-.fa-tree:before {
- content: "\f1bb";
-}
-.fa-spotify:before {
- content: "\f1bc";
-}
-.fa-deviantart:before {
- content: "\f1bd";
-}
-.fa-soundcloud:before {
- content: "\f1be";
-}
-.fa-database:before {
- content: "\f1c0";
-}
-.fa-file-pdf-o:before {
- content: "\f1c1";
-}
-.fa-file-word-o:before {
- content: "\f1c2";
-}
-.fa-file-excel-o:before {
- content: "\f1c3";
-}
-.fa-file-powerpoint-o:before {
- content: "\f1c4";
-}
-.fa-file-photo-o:before,
-.fa-file-picture-o:before,
-.fa-file-image-o:before {
- content: "\f1c5";
-}
-.fa-file-zip-o:before,
-.fa-file-archive-o:before {
- content: "\f1c6";
-}
-.fa-file-sound-o:before,
-.fa-file-audio-o:before {
- content: "\f1c7";
-}
-.fa-file-movie-o:before,
-.fa-file-video-o:before {
- content: "\f1c8";
-}
-.fa-file-code-o:before {
- content: "\f1c9";
-}
-.fa-vine:before {
- content: "\f1ca";
-}
-.fa-codepen:before {
- content: "\f1cb";
-}
-.fa-jsfiddle:before {
- content: "\f1cc";
-}
-.fa-life-bouy:before,
-.fa-life-buoy:before,
-.fa-life-saver:before,
-.fa-support:before,
-.fa-life-ring:before {
- content: "\f1cd";
-}
-.fa-circle-o-notch:before {
- content: "\f1ce";
-}
-.fa-ra:before,
-.fa-resistance:before,
-.fa-rebel:before {
- content: "\f1d0";
-}
-.fa-ge:before,
-.fa-empire:before {
- content: "\f1d1";
-}
-.fa-git-square:before {
- content: "\f1d2";
-}
-.fa-git:before {
- content: "\f1d3";
-}
-.fa-y-combinator-square:before,
-.fa-yc-square:before,
-.fa-hacker-news:before {
- content: "\f1d4";
-}
-.fa-tencent-weibo:before {
- content: "\f1d5";
-}
-.fa-qq:before {
- content: "\f1d6";
-}
-.fa-wechat:before,
-.fa-weixin:before {
- content: "\f1d7";
-}
-.fa-send:before,
-.fa-paper-plane:before {
- content: "\f1d8";
-}
-.fa-send-o:before,
-.fa-paper-plane-o:before {
- content: "\f1d9";
-}
-.fa-history:before {
- content: "\f1da";
-}
-.fa-circle-thin:before {
- content: "\f1db";
-}
-.fa-header:before {
- content: "\f1dc";
-}
-.fa-paragraph:before {
- content: "\f1dd";
-}
-.fa-sliders:before {
- content: "\f1de";
-}
-.fa-share-alt:before {
- content: "\f1e0";
-}
-.fa-share-alt-square:before {
- content: "\f1e1";
-}
-.fa-bomb:before {
- content: "\f1e2";
-}
-.fa-soccer-ball-o:before,
-.fa-futbol-o:before {
- content: "\f1e3";
-}
-.fa-tty:before {
- content: "\f1e4";
-}
-.fa-binoculars:before {
- content: "\f1e5";
-}
-.fa-plug:before {
- content: "\f1e6";
-}
-.fa-slideshare:before {
- content: "\f1e7";
-}
-.fa-twitch:before {
- content: "\f1e8";
-}
-.fa-yelp:before {
- content: "\f1e9";
-}
-.fa-newspaper-o:before {
- content: "\f1ea";
-}
-.fa-wifi:before {
- content: "\f1eb";
-}
-.fa-calculator:before {
- content: "\f1ec";
-}
-.fa-paypal:before {
- content: "\f1ed";
-}
-.fa-google-wallet:before {
- content: "\f1ee";
-}
-.fa-cc-visa:before {
- content: "\f1f0";
-}
-.fa-cc-mastercard:before {
- content: "\f1f1";
-}
-.fa-cc-discover:before {
- content: "\f1f2";
-}
-.fa-cc-amex:before {
- content: "\f1f3";
-}
-.fa-cc-paypal:before {
- content: "\f1f4";
-}
-.fa-cc-stripe:before {
- content: "\f1f5";
-}
-.fa-bell-slash:before {
- content: "\f1f6";
-}
-.fa-bell-slash-o:before {
- content: "\f1f7";
-}
-.fa-trash:before {
- content: "\f1f8";
-}
-.fa-copyright:before {
- content: "\f1f9";
-}
-.fa-at:before {
- content: "\f1fa";
-}
-.fa-eyedropper:before {
- content: "\f1fb";
-}
-.fa-paint-brush:before {
- content: "\f1fc";
-}
-.fa-birthday-cake:before {
- content: "\f1fd";
-}
-.fa-area-chart:before {
- content: "\f1fe";
-}
-.fa-pie-chart:before {
- content: "\f200";
-}
-.fa-line-chart:before {
- content: "\f201";
-}
-.fa-lastfm:before {
- content: "\f202";
-}
-.fa-lastfm-square:before {
- content: "\f203";
-}
-.fa-toggle-off:before {
- content: "\f204";
-}
-.fa-toggle-on:before {
- content: "\f205";
-}
-.fa-bicycle:before {
- content: "\f206";
-}
-.fa-bus:before {
- content: "\f207";
-}
-.fa-ioxhost:before {
- content: "\f208";
-}
-.fa-angellist:before {
- content: "\f209";
-}
-.fa-cc:before {
- content: "\f20a";
-}
-.fa-shekel:before,
-.fa-sheqel:before,
-.fa-ils:before {
- content: "\f20b";
-}
-.fa-meanpath:before {
- content: "\f20c";
-}
-.fa-buysellads:before {
- content: "\f20d";
-}
-.fa-connectdevelop:before {
- content: "\f20e";
-}
-.fa-dashcube:before {
- content: "\f210";
-}
-.fa-forumbee:before {
- content: "\f211";
-}
-.fa-leanpub:before {
- content: "\f212";
-}
-.fa-sellsy:before {
- content: "\f213";
-}
-.fa-shirtsinbulk:before {
- content: "\f214";
-}
-.fa-simplybuilt:before {
- content: "\f215";
-}
-.fa-skyatlas:before {
- content: "\f216";
-}
-.fa-cart-plus:before {
- content: "\f217";
-}
-.fa-cart-arrow-down:before {
- content: "\f218";
-}
-.fa-diamond:before {
- content: "\f219";
-}
-.fa-ship:before {
- content: "\f21a";
-}
-.fa-user-secret:before {
- content: "\f21b";
-}
-.fa-motorcycle:before {
- content: "\f21c";
-}
-.fa-street-view:before {
- content: "\f21d";
-}
-.fa-heartbeat:before {
- content: "\f21e";
-}
-.fa-venus:before {
- content: "\f221";
-}
-.fa-mars:before {
- content: "\f222";
-}
-.fa-mercury:before {
- content: "\f223";
-}
-.fa-intersex:before,
-.fa-transgender:before {
- content: "\f224";
-}
-.fa-transgender-alt:before {
- content: "\f225";
-}
-.fa-venus-double:before {
- content: "\f226";
-}
-.fa-mars-double:before {
- content: "\f227";
-}
-.fa-venus-mars:before {
- content: "\f228";
-}
-.fa-mars-stroke:before {
- content: "\f229";
-}
-.fa-mars-stroke-v:before {
- content: "\f22a";
-}
-.fa-mars-stroke-h:before {
- content: "\f22b";
-}
-.fa-neuter:before {
- content: "\f22c";
-}
-.fa-genderless:before {
- content: "\f22d";
-}
-.fa-facebook-official:before {
- content: "\f230";
-}
-.fa-pinterest-p:before {
- content: "\f231";
-}
-.fa-whatsapp:before {
- content: "\f232";
-}
-.fa-server:before {
- content: "\f233";
-}
-.fa-user-plus:before {
- content: "\f234";
-}
-.fa-user-times:before {
- content: "\f235";
-}
-.fa-hotel:before,
-.fa-bed:before {
- content: "\f236";
-}
-.fa-viacoin:before {
- content: "\f237";
-}
-.fa-train:before {
- content: "\f238";
-}
-.fa-subway:before {
- content: "\f239";
-}
-.fa-medium:before {
- content: "\f23a";
-}
-.fa-yc:before,
-.fa-y-combinator:before {
- content: "\f23b";
-}
-.fa-optin-monster:before {
- content: "\f23c";
-}
-.fa-opencart:before {
- content: "\f23d";
-}
-.fa-expeditedssl:before {
- content: "\f23e";
-}
-.fa-battery-4:before,
-.fa-battery:before,
-.fa-battery-full:before {
- content: "\f240";
-}
-.fa-battery-3:before,
-.fa-battery-three-quarters:before {
- content: "\f241";
-}
-.fa-battery-2:before,
-.fa-battery-half:before {
- content: "\f242";
-}
-.fa-battery-1:before,
-.fa-battery-quarter:before {
- content: "\f243";
-}
-.fa-battery-0:before,
-.fa-battery-empty:before {
- content: "\f244";
-}
-.fa-mouse-pointer:before {
- content: "\f245";
-}
-.fa-i-cursor:before {
- content: "\f246";
-}
-.fa-object-group:before {
- content: "\f247";
-}
-.fa-object-ungroup:before {
- content: "\f248";
-}
-.fa-sticky-note:before {
- content: "\f249";
-}
-.fa-sticky-note-o:before {
- content: "\f24a";
-}
-.fa-cc-jcb:before {
- content: "\f24b";
-}
-.fa-cc-diners-club:before {
- content: "\f24c";
-}
-.fa-clone:before {
- content: "\f24d";
-}
-.fa-balance-scale:before {
- content: "\f24e";
-}
-.fa-hourglass-o:before {
- content: "\f250";
-}
-.fa-hourglass-1:before,
-.fa-hourglass-start:before {
- content: "\f251";
-}
-.fa-hourglass-2:before,
-.fa-hourglass-half:before {
- content: "\f252";
-}
-.fa-hourglass-3:before,
-.fa-hourglass-end:before {
- content: "\f253";
-}
-.fa-hourglass:before {
- content: "\f254";
-}
-.fa-hand-grab-o:before,
-.fa-hand-rock-o:before {
- content: "\f255";
-}
-.fa-hand-stop-o:before,
-.fa-hand-paper-o:before {
- content: "\f256";
-}
-.fa-hand-scissors-o:before {
- content: "\f257";
-}
-.fa-hand-lizard-o:before {
- content: "\f258";
-}
-.fa-hand-spock-o:before {
- content: "\f259";
-}
-.fa-hand-pointer-o:before {
- content: "\f25a";
-}
-.fa-hand-peace-o:before {
- content: "\f25b";
-}
-.fa-trademark:before {
- content: "\f25c";
-}
-.fa-registered:before {
- content: "\f25d";
-}
-.fa-creative-commons:before {
- content: "\f25e";
-}
-.fa-gg:before {
- content: "\f260";
-}
-.fa-gg-circle:before {
- content: "\f261";
-}
-.fa-tripadvisor:before {
- content: "\f262";
-}
-.fa-odnoklassniki:before {
- content: "\f263";
-}
-.fa-odnoklassniki-square:before {
- content: "\f264";
-}
-.fa-get-pocket:before {
- content: "\f265";
-}
-.fa-wikipedia-w:before {
- content: "\f266";
-}
-.fa-safari:before {
- content: "\f267";
-}
-.fa-chrome:before {
- content: "\f268";
-}
-.fa-firefox:before {
- content: "\f269";
-}
-.fa-opera:before {
- content: "\f26a";
-}
-.fa-internet-explorer:before {
- content: "\f26b";
-}
-.fa-tv:before,
-.fa-television:before {
- content: "\f26c";
-}
-.fa-contao:before {
- content: "\f26d";
-}
-.fa-500px:before {
- content: "\f26e";
-}
-.fa-amazon:before {
- content: "\f270";
-}
-.fa-calendar-plus-o:before {
- content: "\f271";
-}
-.fa-calendar-minus-o:before {
- content: "\f272";
-}
-.fa-calendar-times-o:before {
- content: "\f273";
-}
-.fa-calendar-check-o:before {
- content: "\f274";
-}
-.fa-industry:before {
- content: "\f275";
-}
-.fa-map-pin:before {
- content: "\f276";
-}
-.fa-map-signs:before {
- content: "\f277";
-}
-.fa-map-o:before {
- content: "\f278";
-}
-.fa-map:before {
- content: "\f279";
-}
-.fa-commenting:before {
- content: "\f27a";
-}
-.fa-commenting-o:before {
- content: "\f27b";
-}
-.fa-houzz:before {
- content: "\f27c";
-}
-.fa-vimeo:before {
- content: "\f27d";
-}
-.fa-black-tie:before {
- content: "\f27e";
-}
-.fa-fonticons:before {
- content: "\f280";
-}
-.fa-reddit-alien:before {
- content: "\f281";
-}
-.fa-edge:before {
- content: "\f282";
-}
-.fa-credit-card-alt:before {
- content: "\f283";
-}
-.fa-codiepie:before {
- content: "\f284";
-}
-.fa-modx:before {
- content: "\f285";
-}
-.fa-fort-awesome:before {
- content: "\f286";
-}
-.fa-usb:before {
- content: "\f287";
-}
-.fa-product-hunt:before {
- content: "\f288";
-}
-.fa-mixcloud:before {
- content: "\f289";
-}
-.fa-scribd:before {
- content: "\f28a";
-}
-.fa-pause-circle:before {
- content: "\f28b";
-}
-.fa-pause-circle-o:before {
- content: "\f28c";
-}
-.fa-stop-circle:before {
- content: "\f28d";
-}
-.fa-stop-circle-o:before {
- content: "\f28e";
-}
-.fa-shopping-bag:before {
- content: "\f290";
-}
-.fa-shopping-basket:before {
- content: "\f291";
-}
-.fa-hashtag:before {
- content: "\f292";
-}
-.fa-bluetooth:before {
- content: "\f293";
-}
-.fa-bluetooth-b:before {
- content: "\f294";
-}
-.fa-percent:before {
- content: "\f295";
-}
-.fa-gitlab:before {
- content: "\f296";
-}
-.fa-wpbeginner:before {
- content: "\f297";
-}
-.fa-wpforms:before {
- content: "\f298";
-}
-.fa-envira:before {
- content: "\f299";
-}
-.fa-universal-access:before {
- content: "\f29a";
-}
-.fa-wheelchair-alt:before {
- content: "\f29b";
-}
-.fa-question-circle-o:before {
- content: "\f29c";
-}
-.fa-blind:before {
- content: "\f29d";
-}
-.fa-audio-description:before {
- content: "\f29e";
-}
-.fa-volume-control-phone:before {
- content: "\f2a0";
-}
-.fa-braille:before {
- content: "\f2a1";
-}
-.fa-assistive-listening-systems:before {
- content: "\f2a2";
-}
-.fa-asl-interpreting:before,
-.fa-american-sign-language-interpreting:before {
- content: "\f2a3";
-}
-.fa-deafness:before,
-.fa-hard-of-hearing:before,
-.fa-deaf:before {
- content: "\f2a4";
-}
-.fa-glide:before {
- content: "\f2a5";
-}
-.fa-glide-g:before {
- content: "\f2a6";
-}
-.fa-signing:before,
-.fa-sign-language:before {
- content: "\f2a7";
-}
-.fa-low-vision:before {
- content: "\f2a8";
-}
-.fa-viadeo:before {
- content: "\f2a9";
-}
-.fa-viadeo-square:before {
- content: "\f2aa";
-}
-.fa-snapchat:before {
- content: "\f2ab";
-}
-.fa-snapchat-ghost:before {
- content: "\f2ac";
-}
-.fa-snapchat-square:before {
- content: "\f2ad";
-}
-.fa-pied-piper:before {
- content: "\f2ae";
-}
-.fa-first-order:before {
- content: "\f2b0";
-}
-.fa-yoast:before {
- content: "\f2b1";
-}
-.fa-themeisle:before {
- content: "\f2b2";
-}
-.fa-google-plus-circle:before,
-.fa-google-plus-official:before {
- content: "\f2b3";
-}
-.fa-fa:before,
-.fa-font-awesome:before {
- content: "\f2b4";
-}
-.fa-handshake-o:before {
- content: "\f2b5";
-}
-.fa-envelope-open:before {
- content: "\f2b6";
-}
-.fa-envelope-open-o:before {
- content: "\f2b7";
-}
-.fa-linode:before {
- content: "\f2b8";
-}
-.fa-address-book:before {
- content: "\f2b9";
-}
-.fa-address-book-o:before {
- content: "\f2ba";
-}
-.fa-vcard:before,
-.fa-address-card:before {
- content: "\f2bb";
-}
-.fa-vcard-o:before,
-.fa-address-card-o:before {
- content: "\f2bc";
-}
-.fa-user-circle:before {
- content: "\f2bd";
-}
-.fa-user-circle-o:before {
- content: "\f2be";
-}
-.fa-user-o:before {
- content: "\f2c0";
-}
-.fa-id-badge:before {
- content: "\f2c1";
-}
-.fa-drivers-license:before,
-.fa-id-card:before {
- content: "\f2c2";
-}
-.fa-drivers-license-o:before,
-.fa-id-card-o:before {
- content: "\f2c3";
-}
-.fa-quora:before {
- content: "\f2c4";
-}
-.fa-free-code-camp:before {
- content: "\f2c5";
-}
-.fa-telegram:before {
- content: "\f2c6";
-}
-.fa-thermometer-4:before,
-.fa-thermometer:before,
-.fa-thermometer-full:before {
- content: "\f2c7";
-}
-.fa-thermometer-3:before,
-.fa-thermometer-three-quarters:before {
- content: "\f2c8";
-}
-.fa-thermometer-2:before,
-.fa-thermometer-half:before {
- content: "\f2c9";
-}
-.fa-thermometer-1:before,
-.fa-thermometer-quarter:before {
- content: "\f2ca";
-}
-.fa-thermometer-0:before,
-.fa-thermometer-empty:before {
- content: "\f2cb";
-}
-.fa-shower:before {
- content: "\f2cc";
-}
-.fa-bathtub:before,
-.fa-s15:before,
-.fa-bath:before {
- content: "\f2cd";
-}
-.fa-podcast:before {
- content: "\f2ce";
-}
-.fa-window-maximize:before {
- content: "\f2d0";
-}
-.fa-window-minimize:before {
- content: "\f2d1";
-}
-.fa-window-restore:before {
- content: "\f2d2";
-}
-.fa-times-rectangle:before,
-.fa-window-close:before {
- content: "\f2d3";
-}
-.fa-times-rectangle-o:before,
-.fa-window-close-o:before {
- content: "\f2d4";
-}
-.fa-bandcamp:before {
- content: "\f2d5";
-}
-.fa-grav:before {
- content: "\f2d6";
-}
-.fa-etsy:before {
- content: "\f2d7";
-}
-.fa-imdb:before {
- content: "\f2d8";
-}
-.fa-ravelry:before {
- content: "\f2d9";
-}
-.fa-eercast:before {
- content: "\f2da";
-}
-.fa-microchip:before {
- content: "\f2db";
-}
-.fa-snowflake-o:before {
- content: "\f2dc";
-}
-.fa-superpowers:before {
- content: "\f2dd";
-}
-.fa-wpexplorer:before {
- content: "\f2de";
-}
-.fa-meetup:before {
- content: "\f2e0";
-}