diff --git a/build/buildConfig.js b/build/buildConfig.js index 08be32482b..6b2aa053a2 100644 --- a/build/buildConfig.js +++ b/build/buildConfig.js @@ -175,7 +175,6 @@ module.exports = (...args) => mapArgumentsToObject(args, ({ new ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), - new NormalModuleReplacementPlugin(/leaflet$/, path.join(paths.framework, "libs", "leaflet")), new NormalModuleReplacementPlugin(/proj4$/, path.join(paths.framework, "libs", "proj4")), // it's not possible to load directly from the module name `cesium/Build/Cesium/Widgets/widgets.css` // see https://github.com/CesiumGS/cesium/issues/9212 diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md index a1979623cd..fe2c483a2a 100644 --- a/docs/developer-guide/mapstore-migration-guide.md +++ b/docs/developer-guide/mapstore-migration-guide.md @@ -22,6 +22,43 @@ This is a list of things to check if you want to update from a previous version ## Migration from 2022.01.02 to 2022.02.00 +### HTML pages optimization + +We removed script and css link to leaflet CDN in favor of a dynamic import of the libraries in the main bundle, now leaflet is only loaded when the library is selected as map type of the viewer. You can update the project HTML files by removing these tags: + +```diff +- +- + + + +- +- +``` + +We also made asynchronous the script to detect valid browser. This should slightly improve the initial requests time. + You can updated the script in your project as following: + + +```html + + +``` + ### Version plugin has been removed We no longer maintain the Version plugin since we have moved its content inside the About plugin (see [here](https://github.com/geosolutions-it/MapStore2/issues/7934#issuecomment-1201433942) for more details) diff --git a/package.json b/package.json index 92e5d8edb7..6e2a8ab14e 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@geosolutions/jsdoc": "3.4.4", "@geosolutions/mocha": "6.2.1-3", "@mapstore/eslint-config-mapstore": "1.0.5", + "@testing-library/react": "12.1.5", "axios-mock-adapter": "1.16.0", "babel-loader": "8.0.5", "babel-plugin-add-module-exports": "0.1.4", @@ -162,7 +163,6 @@ "colorbrewer": "1.0.0", "connected-react-router": "6.3.2", "create-react-class": "15.6.3", - "css-tree": "1.0.0-alpha24", "draft-js": "0.11.0", "draft-js-inline-toolbar-plugin": "3.0.1", "draft-js-plugins-editor": "2.1.1", diff --git a/project/standard/templates/api.html b/project/standard/templates/api.html index 48b2d6f602..c568fcd4f6 100644 --- a/project/standard/templates/api.html +++ b/project/standard/templates/api.html @@ -7,13 +7,9 @@ __PROJECTDESCRIPTION__ - - - + - - - - - - - + - - +
diff --git a/project/standard/templates/embedded.html b/project/standard/templates/embedded.html index 1414c4852a..c59bc561fb 100644 --- a/project/standard/templates/embedded.html +++ b/project/standard/templates/embedded.html @@ -84,13 +84,9 @@ - - - + - -
diff --git a/project/standard/templates/embeddedTemplate.html b/project/standard/templates/embeddedTemplate.html index f6945f160a..d957ca5f12 100644 --- a/project/standard/templates/embeddedTemplate.html +++ b/project/standard/templates/embeddedTemplate.html @@ -84,25 +84,23 @@ - - - + - - - + diff --git a/project/standard/templates/geostory-embedded-template.html b/project/standard/templates/geostory-embedded-template.html index c593c34e87..390bdd0a28 100644 --- a/project/standard/templates/geostory-embedded-template.html +++ b/project/standard/templates/geostory-embedded-template.html @@ -94,24 +94,22 @@ - - - - - - + + diff --git a/project/standard/templates/geostory-embedded.html b/project/standard/templates/geostory-embedded.html index cd4611e827..a702b7efc9 100644 --- a/project/standard/templates/geostory-embedded.html +++ b/project/standard/templates/geostory-embedded.html @@ -94,12 +94,8 @@ - - - - - +
diff --git a/project/standard/templates/index.html b/project/standard/templates/index.html index a6824ddd88..c643e11b3e 100644 --- a/project/standard/templates/index.html +++ b/project/standard/templates/index.html @@ -84,14 +84,10 @@ - - - + - - diff --git a/project/standard/templates/indexTemplate.html b/project/standard/templates/indexTemplate.html index e61563511c..3d12542e2d 100644 --- a/project/standard/templates/indexTemplate.html +++ b/project/standard/templates/indexTemplate.html @@ -84,16 +84,12 @@ - - - - - - - + + + } +
diff --git a/web/client/api.html b/web/client/api.html index 079e0e2326..d4f560113b 100644 --- a/web/client/api.html +++ b/web/client/api.html @@ -7,13 +7,9 @@ Page with MapStore API - - - - - - - - -
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"; -}