Echo: mechanism for collecting custom events lazily (#20365)
* Introduce Echo for collecting frontend metrics * Update public/app/core/services/echo/Echo.ts Co-Authored-By: Peter Holmberg <peterholmberg@users.noreply.github.com> * Custom meta when adding event * Rename consumer to backend * Remove buffer from Echo * Minor tweaks * Update package.json * Update public/app/app.ts * Update public/app/app.ts * Collect paint metrics when collecting tti. Remove echoBackendFactory * Update yarn.lock * Move Echo interfaces to runtime * progress on meta and echo * Collect meta analytics events * Move MetaanalyticsBackend to enterprise repo * Fixed unit tests * Removed unused type from test * Fixed issues with chunk loading (reverted index-template changes) * Restored changes * Fixed webpack prod
This commit is contained in:
1 parent
4b8a50e70b
commit
178bb1d3ab
24 files changed
+650
-261
No files matched your search
@@ -256,6 +256,7 @@
|
||||
"tether": "1.4.5",
|
||||
"tether-drop": "https://github.com/torkelo/drop/tarball/master",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tti-polyfill": "0.2.2",
|
||||
"xss": "1.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './services';
|
||||
export * from './config';
|
||||
export * from './types';
|
||||
export { loadPluginCss, SystemJS } from './utils/plugin';
|
||||
export { reportMetaAnalytics } from './utils/analytics';
|
||||
@@ -0,0 +1,57 @@
|
||||
interface SizeMeta {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface EchoMeta {
|
||||
screenSize: SizeMeta;
|
||||
windowSize: SizeMeta;
|
||||
userAgent: string;
|
||||
url?: string;
|
||||
/**
|
||||
* A unique browser session
|
||||
*/
|
||||
sessionId: string;
|
||||
userLogin: string;
|
||||
userId: number;
|
||||
userSignedIn: boolean;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export interface EchoBackend<T extends EchoEvent = any, O = any> {
|
||||
options: O;
|
||||
supportedEvents: EchoEventType[];
|
||||
flush: () => void;
|
||||
addEvent: (event: T) => void;
|
||||
}
|
||||
|
||||
export interface EchoEvent<T extends EchoEventType = any, P = any> {
|
||||
type: EchoEventType;
|
||||
payload: P;
|
||||
meta: EchoMeta;
|
||||
}
|
||||
|
||||
export enum EchoEventType {
|
||||
Performance = 'performance',
|
||||
MetaAnalytics = 'meta-analytics',
|
||||
}
|
||||
|
||||
export interface EchoSrv {
|
||||
flush(): void;
|
||||
addBackend(backend: EchoBackend): void;
|
||||
addEvent<T extends EchoEvent>(event: Omit<T, 'meta'>, meta?: {}): void;
|
||||
}
|
||||
|
||||
let singletonInstance: EchoSrv;
|
||||
|
||||
export function setEchoSrv(instance: EchoSrv) {
|
||||
singletonInstance = instance;
|
||||
}
|
||||
|
||||
export function getEchoSrv(): EchoSrv {
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
export const registerEchoBackend = (backend: EchoBackend) => {
|
||||
getEchoSrv().addBackend(backend);
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export * from './backendSrv';
|
||||
export * from './AngularLoader';
|
||||
export * from './dataSourceSrv';
|
||||
export * from './LocationSrv';
|
||||
export * from './EchoSrv';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { EchoEvent, EchoEventType } from '../services/EchoSrv';
|
||||
|
||||
export interface MetaAnalyticsEventPayload {
|
||||
eventName: string;
|
||||
dashboardId?: number;
|
||||
dashboardUid?: string;
|
||||
dashboardName?: string;
|
||||
folderName?: string;
|
||||
panelId?: number;
|
||||
panelName?: string;
|
||||
datasourceName: string;
|
||||
datasourceId?: number;
|
||||
error?: string;
|
||||
duration: number;
|
||||
dataSize?: number;
|
||||
}
|
||||
|
||||
export interface MetaAnalyticsEvent extends EchoEvent<EchoEventType.MetaAnalytics, MetaAnalyticsEventPayload> {}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './analytics';
|
||||
@@ -0,0 +1,9 @@
|
||||
import { getEchoSrv, EchoEventType } from '../services/EchoSrv';
|
||||
import { MetaAnalyticsEvent, MetaAnalyticsEventPayload } from '../types/analytics';
|
||||
|
||||
export const reportMetaAnalytics = (payload: MetaAnalyticsEventPayload) => {
|
||||
getEchoSrv().addEvent<MetaAnalyticsEvent>({
|
||||
type: EchoEventType.MetaAnalytics,
|
||||
payload,
|
||||
});
|
||||
};
|
||||
@@ -187,7 +187,7 @@ $btn-drag-image: '../img/grab_dark.svg';
|
||||
|
||||
$navbar-btn-gicon-brightness: brightness(0.5);
|
||||
|
||||
$btn-active-box-shadow: 0px 0px 4px rgba(255,120,10,0.5);
|
||||
$btn-active-box-shadow: 0px 0px 4px rgba(255, 120, 10, 0.5);
|
||||
|
||||
// Forms
|
||||
// -------------------------
|
||||
|
||||
@@ -16,6 +16,8 @@ import 'vendor/angular-other/angular-strap';
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
import config from 'app/core/config';
|
||||
// @ts-ignore
|
||||
import ttiPolyfill from 'tti-polyfill';
|
||||
// @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move
|
||||
import _ from 'lodash';
|
||||
import { AppEvents, setMarkdownOptions, setLocale } from '@grafana/data';
|
||||
@@ -34,6 +36,10 @@ _.move = (array: [], fromIndex: number, toIndex: number) => {
|
||||
import { coreModule, angularModules } from 'app/core/core_module';
|
||||
import { registerAngularDirectives } from 'app/core/core';
|
||||
import { setupAngularRoutes } from 'app/routes/routes';
|
||||
import { setEchoSrv, registerEchoBackend } from '@grafana/runtime';
|
||||
import { Echo } from './core/services/echo/Echo';
|
||||
import { reportPerformance } from './core/services/echo/EchoSrv';
|
||||
import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend';
|
||||
|
||||
import 'app/routes/GrafanaCtrl';
|
||||
import 'app/features/all';
|
||||
@@ -163,6 +169,26 @@ export class GrafanaApp {
|
||||
importPluginModule(modulePath);
|
||||
}
|
||||
}
|
||||
|
||||
initEchoSrv() {
|
||||
setEchoSrv(new Echo({ debug: process.env.NODE_ENV === 'development' }));
|
||||
|
||||
ttiPolyfill.getFirstConsistentlyInteractive().then((tti: any) => {
|
||||
// Collecting paint metrics first
|
||||
const paintMetrics = performance.getEntriesByType('paint');
|
||||
|
||||
for (const metric of paintMetrics) {
|
||||
reportPerformance(metric.name, Math.round(metric.startTime + metric.duration));
|
||||
}
|
||||
reportPerformance('tti', tti);
|
||||
});
|
||||
|
||||
registerEchoBackend(new PerformanceBackend({}));
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
reportPerformance('dcl', Math.round(performance.now()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new GrafanaApp();
|
||||
@@ -21,6 +21,7 @@ const setup = (propOverrides?: object) => {
|
||||
orgCount: 2,
|
||||
orgRole: '',
|
||||
orgId: 1,
|
||||
login: 'hello',
|
||||
orgName: 'Grafana',
|
||||
timezone: 'UTC',
|
||||
helpFlags1: 1,
|
||||
|
||||
@@ -9,6 +9,7 @@ export class User {
|
||||
orgRole: any;
|
||||
orgId: number;
|
||||
orgName: string;
|
||||
login: string;
|
||||
orgCount: number;
|
||||
timezone: string;
|
||||
helpFlags1: number;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { EchoBackend, EchoMeta, EchoEvent, EchoSrv } from '@grafana/runtime';
|
||||
import { contextSrv } from '../context_srv';
|
||||
|
||||
interface EchoConfig {
|
||||
// How often should metrics be reported
|
||||
flushInterval: number;
|
||||
// Enables debug mode
|
||||
debug: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo is a service for collecting events from Grafana client-app
|
||||
* It collects events, distributes them across registered backend and flushes once per configured interval
|
||||
* It's up to the registered backend to decide what to do with a given type of metric
|
||||
*/
|
||||
export class Echo implements EchoSrv {
|
||||
private config: EchoConfig = {
|
||||
flushInterval: 10000, // By default Echo flushes every 10s
|
||||
debug: false,
|
||||
};
|
||||
|
||||
private backends: EchoBackend[] = [];
|
||||
// meta data added to every event collected
|
||||
|
||||
constructor(config?: Partial<EchoConfig>) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
...config,
|
||||
};
|
||||
setInterval(this.flush, this.config.flushInterval);
|
||||
}
|
||||
|
||||
logDebug = (...msg: any) => {
|
||||
if (this.config.debug) {
|
||||
// tslint:disable-next-line
|
||||
// console.debug('ECHO:', ...msg);
|
||||
}
|
||||
};
|
||||
|
||||
flush = () => {
|
||||
for (const backend of this.backends) {
|
||||
backend.flush();
|
||||
}
|
||||
};
|
||||
|
||||
addBackend = (backend: EchoBackend) => {
|
||||
this.logDebug('Adding backend', backend);
|
||||
this.backends.push(backend);
|
||||
};
|
||||
|
||||
addEvent = <T extends EchoEvent>(event: Omit<T, 'meta'>, _meta?: {}) => {
|
||||
const meta = this.getMeta();
|
||||
const _event = {
|
||||
...event,
|
||||
meta: {
|
||||
...meta,
|
||||
..._meta,
|
||||
},
|
||||
};
|
||||
|
||||
for (const backend of this.backends) {
|
||||
if (backend.supportedEvents.length === 0 || backend.supportedEvents.indexOf(_event.type) > -1) {
|
||||
backend.addEvent(_event);
|
||||
}
|
||||
}
|
||||
|
||||
this.logDebug('Adding event', _event);
|
||||
};
|
||||
|
||||
getMeta = (): EchoMeta => {
|
||||
return {
|
||||
sessionId: '',
|
||||
userId: contextSrv.user.id,
|
||||
userLogin: contextSrv.user.login,
|
||||
userSignedIn: contextSrv.user.isSignedIn,
|
||||
screenSize: {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
},
|
||||
windowSize: {
|
||||
width: window.screen.width,
|
||||
height: window.screen.height,
|
||||
},
|
||||
userAgent: window.navigator.userAgent,
|
||||
ts: performance.now(),
|
||||
url: window.location.href,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getEchoSrv, EchoEventType } from '@grafana/runtime';
|
||||
import { PerformanceEvent } from './backends/PerformanceBackend';
|
||||
|
||||
export const reportPerformance = (metric: string, value: number) => {
|
||||
getEchoSrv().addEvent<PerformanceEvent>({
|
||||
type: EchoEventType.Performance,
|
||||
payload: {
|
||||
metricName: metric,
|
||||
duration: value,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime';
|
||||
|
||||
export interface PerformanceEventPayload {
|
||||
metricName: string;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export interface PerformanceEvent extends EchoEvent<EchoEventType.Performance, PerformanceEventPayload> {}
|
||||
|
||||
export interface PerformanceBackendOptions {
|
||||
url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo's performance metrics consumer
|
||||
* Reports performance metrics to given url (TODO)
|
||||
*/
|
||||
export class PerformanceBackend implements EchoBackend<PerformanceEvent, PerformanceBackendOptions> {
|
||||
private buffer: PerformanceEvent[] = [];
|
||||
supportedEvents = [EchoEventType.Performance];
|
||||
|
||||
constructor(public options: PerformanceBackendOptions) {}
|
||||
|
||||
addEvent = (e: EchoEvent) => {
|
||||
this.buffer.push(e);
|
||||
};
|
||||
|
||||
flush = () => {
|
||||
if (this.buffer.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = {
|
||||
metrics: this.buffer,
|
||||
};
|
||||
|
||||
// Currently we don have API for sending the metrics hence loging to console in dev environment
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('PerformanceBackend flushing:', result);
|
||||
}
|
||||
|
||||
this.buffer = [];
|
||||
|
||||
// TODO: Enable backend request when we have metrics API
|
||||
// if (this.options.url) {
|
||||
// getBackendSrv().post(this.options.url, result);
|
||||
// }
|
||||
};
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
import { PanelQueryRunner } from './PanelQueryRunner';
|
||||
import { PanelData, DataQueryRequest, dateTime, ScopedVars } from '@grafana/data';
|
||||
import { PanelModel } from './PanelModel';
|
||||
import { DashboardModel } from './index';
|
||||
import { setEchoSrv } from '@grafana/runtime';
|
||||
import { Echo } from '../../../core/services/echo/Echo';
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
|
||||
// Defined within setup functions
|
||||
const panelsForCurrentDashboardMock: { [key: number]: PanelModel } = {};
|
||||
const dashboardModel = new DashboardModel({
|
||||
panels: [{ id: 1, type: 'graph' }],
|
||||
});
|
||||
|
||||
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
|
||||
getDashboardSrv: () => {
|
||||
return {
|
||||
getCurrent: () => {
|
||||
return {
|
||||
getPanelById: (id: number) => {
|
||||
return panelsForCurrentDashboardMock[id];
|
||||
},
|
||||
};
|
||||
},
|
||||
getCurrent: () => dashboardModel,
|
||||
};
|
||||
},
|
||||
}));
|
||||
@@ -68,6 +65,7 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
setEchoSrv(new Echo());
|
||||
setupFn();
|
||||
|
||||
const datasource: any = {
|
||||
@@ -103,13 +101,6 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn
|
||||
},
|
||||
});
|
||||
|
||||
panelsForCurrentDashboardMock[1] = {
|
||||
id: 1,
|
||||
getQueryRunner: () => {
|
||||
return ctx.runner;
|
||||
},
|
||||
} as PanelModel;
|
||||
|
||||
ctx.events = [];
|
||||
ctx.runner.run(args);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { getDashboardSrv } from '../services/DashboardSrv';
|
||||
|
||||
import { PanelData, LoadingState, DataSourceApi } from '@grafana/data';
|
||||
|
||||
import { reportMetaAnalytics, MetaAnalyticsEventPayload } from '@grafana/runtime';
|
||||
|
||||
export function getAnalyticsProcessor(datasource: DataSourceApi) {
|
||||
let done = false;
|
||||
|
||||
return (data: PanelData) => {
|
||||
if (!data.request || done) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.state !== LoadingState.Done && data.state !== LoadingState.Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eventData: MetaAnalyticsEventPayload = {
|
||||
datasourceName: datasource.name,
|
||||
datasourceId: datasource.id,
|
||||
panelId: data.request.panelId,
|
||||
dashboardId: data.request.dashboardId,
|
||||
// app: 'dashboard',
|
||||
// count: 1,
|
||||
dataSize: 0,
|
||||
duration: data.request.endTime - data.request.startTime,
|
||||
eventName: 'data-request',
|
||||
// sessionId: '',
|
||||
};
|
||||
|
||||
// enrich with dashboard info
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
if (dashboard) {
|
||||
eventData.dashboardId = dashboard.id;
|
||||
eventData.dashboardName = dashboard.title;
|
||||
eventData.dashboardUid = dashboard.uid;
|
||||
eventData.folderName = dashboard.meta.folderTitle;
|
||||
}
|
||||
|
||||
if (data.series.length > 0) {
|
||||
// estimate size
|
||||
eventData.dataSize = data.series.length * data.series[0].length;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
eventData.error = data.error.message;
|
||||
}
|
||||
|
||||
reportMetaAnalytics(eventData);
|
||||
|
||||
// this done check is to make sure we do not double emit events in case
|
||||
// there are multiple responses with done state
|
||||
done = true;
|
||||
};
|
||||
}
|
||||
@@ -10,9 +10,24 @@ import {
|
||||
import { Subscriber, Observable, Subscription } from 'rxjs';
|
||||
import { runRequest } from './runRequest';
|
||||
import { deepFreeze } from '../../../../test/core/redux/reducerTester';
|
||||
import { DashboardModel } from './DashboardModel';
|
||||
import { setEchoSrv } from '@grafana/runtime';
|
||||
import { Echo } from '../../../core/services/echo/Echo';
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
|
||||
const dashboardModel = new DashboardModel({
|
||||
panels: [{ id: 1, type: 'graph' }],
|
||||
});
|
||||
|
||||
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
|
||||
getDashboardSrv: () => {
|
||||
return {
|
||||
getCurrent: () => dashboardModel,
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
class ScenarioCtx {
|
||||
ds: DataSourceApi;
|
||||
request: DataQueryRequest;
|
||||
@@ -84,6 +99,7 @@ function runRequestScenario(desc: string, fn: (ctx: ScenarioCtx) => void) {
|
||||
const ctx = new ScenarioCtx();
|
||||
|
||||
beforeEach(() => {
|
||||
setEchoSrv(new Echo());
|
||||
ctx.reset();
|
||||
return ctx.setupFn();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Libraries
|
||||
import { Observable, of, timer, merge, from } from 'rxjs';
|
||||
import { flatten, map as lodashMap, isArray, isString } from 'lodash';
|
||||
import { map, catchError, takeUntil, mapTo, share, finalize } from 'rxjs/operators';
|
||||
import { map, catchError, takeUntil, mapTo, share, finalize, tap } from 'rxjs/operators';
|
||||
// Utils & Services
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
// Types
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
DataFrame,
|
||||
guessFieldTypes,
|
||||
} from '@grafana/data';
|
||||
import { getAnalyticsProcessor } from './analyticsProcessor';
|
||||
import { ExpressionDatasourceID, expressionDatasource } from 'app/features/expressions/ExpressionDatasource';
|
||||
|
||||
type MapOfResponsePackets = { [str: string]: DataQueryResponse };
|
||||
@@ -119,6 +120,7 @@ export function runRequest(datasource: DataSourceApi, request: DataQueryRequest)
|
||||
error: processQueryError(err),
|
||||
})
|
||||
),
|
||||
tap(getAnalyticsProcessor(datasource)),
|
||||
// finalize is triggered when subscriber unsubscribes
|
||||
// This makes sure any still running network requests are cancelled
|
||||
finalize(cancelNetworkRequestsOnUnsubscribe(request)),
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
import app from './app';
|
||||
|
||||
app.initEchoSrv();
|
||||
app.init();
|
||||
@@ -141,7 +141,6 @@ export function grafanaAppDirective(
|
||||
controller: GrafanaCtrl,
|
||||
link: (scope: IRootScopeService & AppEventEmitter, elem: JQuery) => {
|
||||
const body = $('body');
|
||||
|
||||
// see https://github.com/zenorocha/clipboard.js/issues/155
|
||||
$.fn.modal.Constructor.prototype.enforceFocus = () => {};
|
||||
|
||||
|
||||
+290
-239
@@ -1,277 +1,328 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
// https://github.com/GoogleChromeLabs/tti-polyfill
|
||||
!(function() {
|
||||
if ('PerformanceLongTaskTiming' in window) {
|
||||
var g = (window.__tti = { e: [] });
|
||||
g.o = new PerformanceObserver(function(l) {
|
||||
g.e = g.e.concat(l.getEntries());
|
||||
});
|
||||
g.o.observe({ entryTypes: ['longtask'] });
|
||||
}
|
||||
})();
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="theme-color" content="#000">
|
||||
</script>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="theme-color" content="#000" />
|
||||
|
||||
<title>Grafana</title>
|
||||
<title>Grafana</title>
|
||||
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
|
||||
<link rel="preload" href="public/fonts/roboto/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2" as="font" crossorigin />
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="public/img/apple-touch-icon.png">
|
||||
<link
|
||||
rel="preload"
|
||||
href="public/fonts/roboto/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2"
|
||||
as="font"
|
||||
crossorigin
|
||||
/>
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png" />
|
||||
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="public/img/apple-touch-icon.png" />
|
||||
|
||||
<link rel="stylesheet" href="public/build/grafana.[[ .Theme ]].<%= webpack.hash %>.css">
|
||||
<link rel="stylesheet" href="public/build/grafana.[[ .Theme ]].<%= webpack.hash %>.css" />
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
|
||||
<meta name="msapplication-TileColor" content="#2b5797">
|
||||
<meta name="msapplication-config" content="public/img/browserconfig.xml">
|
||||
</head>
|
||||
<script>
|
||||
performance.mark('css done blocking');
|
||||
</script>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="msapplication-TileColor" content="#2b5797" />
|
||||
<meta name="msapplication-config" content="public/img/browserconfig.xml" />
|
||||
</head>
|
||||
|
||||
<body class="theme-[[ .Theme ]] [[.AppNameBodyClass]]">
|
||||
<body class="theme-[[ .Theme ]] [[.AppNameBodyClass]]">
|
||||
<style>
|
||||
.preloader {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
<style>
|
||||
.preloader {
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preloader__enter {
|
||||
opacity: 0;
|
||||
animation-name: preloader-fade-in;
|
||||
animation-iteration-count: 1;
|
||||
animation-duration: .9s;
|
||||
animation-delay: 1.35s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.preloader__bounce {
|
||||
text-align: center;
|
||||
animation-name: preloader-bounce;
|
||||
animation-duration: .9s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.preloader__logo {
|
||||
display: inline-block;
|
||||
animation-name: preloader-squash;
|
||||
animation-duration: .9s;
|
||||
animation-iteration-count: infinite;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-image: url("data:image/svg+xml,%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='351px' height='365px' viewBox='0 0 351 365' style='enable-background:new 0 0 351 365%3b' xml:space='preserve'%3e %3cstyle type='text/css'%3e .st0%7bfill:url(%23SVGID_1_)%3b%7d %3c/style%3e %3cg id='Layer_1_1_'%3e %3c/g%3e %3clinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='175.5' y1='445.4948' x2='175.5' y2='114.0346'%3e %3cstop offset='0' style='stop-color:%23FFF100'/%3e %3cstop offset='1' style='stop-color:%23F05A28'/%3e %3c/linearGradient%3e %3cpath class='st0' d='M342%2c161.2c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25c-4.4-8.8-10.1-17.9-17.5-26.8 c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9%2c6.1-36.5%2c9.4c-0.8-0.3-1.5-0.7-2.3-1 c-3.3-1.3-6.7-2.6-10.3-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3 c-8.5-27.2-32.9-38.6-32.9-38.6c-27.3%2c17.3-32.4%2c41.5-32.4%2c41.5s-0.1%2c0.5-0.3%2c1.4c-1.5%2c0.4-3%2c0.9-4.5%2c1.3c-2.1%2c0.6-4.2%2c1.4-6.2%2c2.2 c-2.1%2c0.8-4.1%2c1.6-6.2%2c2.5c-4.1%2c1.8-8.2%2c3.8-12.2%2c6c-3.9%2c2.2-7.7%2c4.6-11.4%2c7.1c-0.5-0.2-1-0.4-1-0.4c-37.8-14.4-71.3%2c2.9-71.3%2c2.9 c-3.1%2c40.2%2c15.1%2c65.5%2c18.7%2c70.1c-0.9%2c2.5-1.7%2c5-2.5%2c7.5c-2.8%2c9.1-4.9%2c18.4-6.2%2c28.1c-0.2%2c1.4-0.4%2c2.8-0.5%2c4.2 C18.8%2c192.7%2c8.5%2c228%2c8.5%2c228c29.1%2c33.5%2c63.1%2c35.6%2c63.1%2c35.6c0%2c0%2c0.1-0.1%2c0.1-0.1c4.3%2c7.7%2c9.3%2c15%2c14.9%2c21.9c2.4%2c2.9%2c4.8%2c5.6%2c7.4%2c8.3 c-10.6%2c30.4%2c1.5%2c55.6%2c1.5%2c55.6c32.4%2c1.2%2c53.7-14.2%2c58.2-17.7c3.2%2c1.1%2c6.5%2c2.1%2c9.8%2c2.9c10%2c2.6%2c20.2%2c4.1%2c30.4%2c4.5 c2.5%2c0.1%2c5.1%2c0.2%2c7.6%2c0.1l1.2%2c0l0.8%2c0l1.6%2c0l1.6-0.1l0%2c0.1c15.3%2c21.8%2c42.1%2c24.9%2c42.1%2c24.9c19.1-20.1%2c20.2-40.1%2c20.2-44.4l0%2c0 c0%2c0%2c0-0.1%2c0-0.3c0-0.4%2c0-0.6%2c0-0.6l0%2c0c0-0.3%2c0-0.6%2c0-0.9c4-2.8%2c7.8-5.8%2c11.4-9.1c7.6-6.9%2c14.3-14.8%2c19.9-23.3 c0.5-0.8%2c1-1.6%2c1.5-2.4c21.6%2c1.2%2c36.9-13.4%2c36.9-13.4c-3.6-22.5-16.4-33.5-19.1-35.6l0%2c0c0%2c0-0.1-0.1-0.3-0.2 c-0.2-0.1-0.2-0.2-0.2-0.2c0%2c0%2c0%2c0%2c0%2c0c-0.1-0.1-0.3-0.2-0.5-0.3c0.1-1.4%2c0.2-2.7%2c0.3-4.1c0.2-2.4%2c0.2-4.9%2c0.2-7.3l0-1.8l0-0.9 l0-0.5c0-0.6%2c0-0.4%2c0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9 c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.3-21.8c-7-6.2-14.9-11.2-23.1-14.9 c-8.3-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6%2c0l-0.4%2c0c-0.1%2c0-0.6%2c0-0.5%2c0l-0.7%2c0l-1.6%2c0.1c-0.6%2c0-1.2%2c0.1-1.7%2c0.1 c-2.2%2c0.2-4.4%2c0.5-6.5%2c0.9c-8.6%2c1.6-16.7%2c4.7-23.8%2c9c-7.1%2c4.3-13.3%2c9.6-18.3%2c15.6c-5%2c6-8.9%2c12.7-11.6%2c19.6c-2.7%2c6.9-4.2%2c14.1-4.6%2c21 c-0.1%2c1.7-0.1%2c3.5-0.1%2c5.2c0%2c0.4%2c0%2c0.9%2c0%2c1.3l0.1%2c1.4c0.1%2c0.8%2c0.1%2c1.7%2c0.2%2c2.5c0.3%2c3.5%2c1%2c6.9%2c1.9%2c10.1c1.9%2c6.5%2c4.9%2c12.4%2c8.6%2c17.4 c3.7%2c5%2c8.2%2c9.1%2c12.9%2c12.4c4.7%2c3.2%2c9.8%2c5.5%2c14.8%2c7c5%2c1.5%2c10%2c2.1%2c14.7%2c2.1c0.6%2c0%2c1.2%2c0%2c1.7%2c0c0.3%2c0%2c0.6%2c0%2c0.9%2c0c0.3%2c0%2c0.6%2c0%2c0.9-0.1 c0.5%2c0%2c1-0.1%2c1.5-0.1c0.1%2c0%2c0.3%2c0%2c0.4-0.1l0.5-0.1c0.3%2c0%2c0.6-0.1%2c0.9-0.1c0.6-0.1%2c1.1-0.2%2c1.7-0.3c0.6-0.1%2c1.1-0.2%2c1.6-0.4 c1.1-0.2%2c2.1-0.6%2c3.1-0.9c2-0.7%2c4-1.5%2c5.7-2.4c1.8-0.9%2c3.4-2%2c5-3c0.4-0.3%2c0.9-0.6%2c1.3-1c1.6-1.3%2c1.9-3.7%2c0.6-5.3 c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4%2c0.2-0.8%2c0.4-1.2%2c0.6c-1.4%2c0.7-2.8%2c1.3-4.3%2c1.8c-1.5%2c0.5-3.1%2c0.9-4.7%2c1.2c-0.8%2c0.1-1.6%2c0.2-2.5%2c0.3 c-0.4%2c0-0.8%2c0.1-1.3%2c0.1c-0.4%2c0-0.9%2c0-1.2%2c0c-0.4%2c0-0.8%2c0-1.2%2c0c-0.5%2c0-1%2c0-1.5-0.1c0%2c0-0.3%2c0-0.1%2c0l-0.2%2c0l-0.3%2c0 c-0.2%2c0-0.5%2c0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9 c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6%2c0.1-1.2%2c0.1-1.8c0%2c0.2%2c0-0.1%2c0-0.1l0-0.2l0-0.5c0-0.3%2c0.1-0.6%2c0.1-0.9 c0.1-1.2%2c0.3-2.4%2c0.5-3.6c1.7-9.6%2c6.5-19%2c13.9-26.1c1.9-1.8%2c3.9-3.4%2c6-4.9c2.1-1.5%2c4.4-2.8%2c6.8-3.9c2.4-1.1%2c4.8-2%2c7.4-2.7 c2.5-0.7%2c5.1-1.1%2c7.8-1.4c1.3-0.1%2c2.6-0.2%2c4-0.2c0.4%2c0%2c0.6%2c0%2c0.9%2c0l1.1%2c0l0.7%2c0c0.3%2c0%2c0%2c0%2c0.1%2c0l0.3%2c0l1.1%2c0.1 c2.9%2c0.2%2c5.7%2c0.6%2c8.5%2c1.3c5.6%2c1.2%2c11.1%2c3.3%2c16.2%2c6.1c10.2%2c5.7%2c18.9%2c14.5%2c24.2%2c25.1c2.7%2c5.3%2c4.6%2c11%2c5.5%2c16.9c0.2%2c1.5%2c0.4%2c3%2c0.5%2c4.5 l0.1%2c1.1l0.1%2c1.1c0%2c0.4%2c0%2c0.8%2c0%2c1.1c0%2c0.4%2c0%2c0.8%2c0%2c1.1l0%2c1l0%2c1.1c0%2c0.7-0.1%2c1.9-0.1%2c2.6c-0.1%2c1.6-0.3%2c3.3-0.5%2c4.9 c-0.2%2c1.6-0.5%2c3.2-0.8%2c4.8c-0.3%2c1.6-0.7%2c3.2-1.1%2c4.7c-0.8%2c3.1-1.8%2c6.2-3%2c9.3c-2.4%2c6-5.6%2c11.8-9.4%2c17.1 c-7.7%2c10.6-18.2%2c19.2-30.2%2c24.7c-6%2c2.7-12.3%2c4.7-18.8%2c5.7c-3.2%2c0.6-6.5%2c0.9-9.8%2c1l-0.6%2c0l-0.5%2c0l-1.1%2c0l-1.6%2c0l-0.8%2c0 c0.4%2c0-0.1%2c0-0.1%2c0l-0.3%2c0c-1.8%2c0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8 c-12.3-6.6-23.4-15.6-32-26.5c-4.3-5.4-8.1Line truncated
|
||||
}
|
||||
|
||||
.preloader__text {
|
||||
margin-top: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-family: Sans-serif;
|
||||
opacity: 0;
|
||||
animation-name: preloader-fade-in;
|
||||
animation-duration: .9s;
|
||||
animation-delay: 1.8s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.theme-light .preloader__text {
|
||||
color: #52545c;
|
||||
}
|
||||
|
||||
.theme-dark .preloader__text {
|
||||
color: #d8d9da;
|
||||
}
|
||||
|
||||
@keyframes preloader-fade-in {
|
||||
0% {
|
||||
.preloader__enter {
|
||||
opacity: 0;
|
||||
/*animation-timing-function: linear;*/
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.5, 1)
|
||||
animation-name: preloader-fade-in;
|
||||
animation-iteration-count: 1;
|
||||
animation-duration: 0.9s;
|
||||
animation-delay: 1.35s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes preloader-bounce {
|
||||
from,
|
||||
to {
|
||||
transform: translateY(0px);
|
||||
animation-timing-function: cubic-bezier(0.3, 0.0, 0.1, 1)
|
||||
.preloader__bounce {
|
||||
text-align: center;
|
||||
animation-name: preloader-bounce;
|
||||
animation-duration: 0.9s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-50px);
|
||||
animation-timing-function: cubic-bezier(.9, 0, .7, 1)
|
||||
|
||||
.preloader__logo {
|
||||
display: inline-block;
|
||||
animation-name: preloader-squash;
|
||||
animation-duration: 0.9s;
|
||||
animation-iteration-count: infinite;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-image: url("data:image/svg+xml,%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='351px' height='365px' viewBox='0 0 351 365' style='enable-background:new 0 0 351 365%3b' xml:space='preserve'%3e %3cstyle type='text/css'%3e .st0%7bfill:url(%23SVGID_1_)%3b%7d %3c/style%3e %3cg id='Layer_1_1_'%3e %3c/g%3e %3clinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='175.5' y1='445.4948' x2='175.5' y2='114.0346'%3e %3cstop offset='0' style='stop-color:%23FFF100'/%3e %3cstop offset='1' style='stop-color:%23F05A28'/%3e %3c/linearGradient%3e %3cpath class='st0' d='M342%2c161.2c-0.6-6.1-1.6-13.1-3.6-20.9c-2-7.7-5-16.2-9.4-25c-4.4-8.8-10.1-17.9-17.5-26.8 c-2.9-3.5-6.1-6.9-9.5-10.2c5.1-20.3-6.2-37.9-6.2-37.9c-19.5-1.2-31.9%2c6.1-36.5%2c9.4c-0.8-0.3-1.5-0.7-2.3-1 c-3.3-1.3-6.7-2.6-10.3-3.7c-3.5-1.1-7.1-2.1-10.8-3c-3.7-0.9-7.4-1.6-11.2-2.2c-0.7-0.1-1.3-0.2-2-0.3 c-8.5-27.2-32.9-38.6-32.9-38.6c-27.3%2c17.3-32.4%2c41.5-32.4%2c41.5s-0.1%2c0.5-0.3%2c1.4c-1.5%2c0.4-3%2c0.9-4.5%2c1.3c-2.1%2c0.6-4.2%2c1.4-6.2%2c2.2 c-2.1%2c0.8-4.1%2c1.6-6.2%2c2.5c-4.1%2c1.8-8.2%2c3.8-12.2%2c6c-3.9%2c2.2-7.7%2c4.6-11.4%2c7.1c-0.5-0.2-1-0.4-1-0.4c-37.8-14.4-71.3%2c2.9-71.3%2c2.9 c-3.1%2c40.2%2c15.1%2c65.5%2c18.7%2c70.1c-0.9%2c2.5-1.7%2c5-2.5%2c7.5c-2.8%2c9.1-4.9%2c18.4-6.2%2c28.1c-0.2%2c1.4-0.4%2c2.8-0.5%2c4.2 C18.8%2c192.7%2c8.5%2c228%2c8.5%2c228c29.1%2c33.5%2c63.1%2c35.6%2c63.1%2c35.6c0%2c0%2c0.1-0.1%2c0.1-0.1c4.3%2c7.7%2c9.3%2c15%2c14.9%2c21.9c2.4%2c2.9%2c4.8%2c5.6%2c7.4%2c8.3 c-10.6%2c30.4%2c1.5%2c55.6%2c1.5%2c55.6c32.4%2c1.2%2c53.7-14.2%2c58.2-17.7c3.2%2c1.1%2c6.5%2c2.1%2c9.8%2c2.9c10%2c2.6%2c20.2%2c4.1%2c30.4%2c4.5 c2.5%2c0.1%2c5.1%2c0.2%2c7.6%2c0.1l1.2%2c0l0.8%2c0l1.6%2c0l1.6-0.1l0%2c0.1c15.3%2c21.8%2c42.1%2c24.9%2c42.1%2c24.9c19.1-20.1%2c20.2-40.1%2c20.2-44.4l0%2c0 c0%2c0%2c0-0.1%2c0-0.3c0-0.4%2c0-0.6%2c0-0.6l0%2c0c0-0.3%2c0-0.6%2c0-0.9c4-2.8%2c7.8-5.8%2c11.4-9.1c7.6-6.9%2c14.3-14.8%2c19.9-23.3 c0.5-0.8%2c1-1.6%2c1.5-2.4c21.6%2c1.2%2c36.9-13.4%2c36.9-13.4c-3.6-22.5-16.4-33.5-19.1-35.6l0%2c0c0%2c0-0.1-0.1-0.3-0.2 c-0.2-0.1-0.2-0.2-0.2-0.2c0%2c0%2c0%2c0%2c0%2c0c-0.1-0.1-0.3-0.2-0.5-0.3c0.1-1.4%2c0.2-2.7%2c0.3-4.1c0.2-2.4%2c0.2-4.9%2c0.2-7.3l0-1.8l0-0.9 l0-0.5c0-0.6%2c0-0.4%2c0-0.6l-0.1-1.5l-0.1-2c0-0.7-0.1-1.3-0.2-1.9c-0.1-0.6-0.1-1.3-0.2-1.9l-0.2-1.9l-0.3-1.9 c-0.4-2.5-0.8-4.9-1.4-7.4c-2.3-9.7-6.1-18.9-11-27.2c-5-8.3-11.2-15.6-18.3-21.8c-7-6.2-14.9-11.2-23.1-14.9 c-8.3-3.7-16.9-6.1-25.5-7.2c-4.3-0.6-8.6-0.8-12.9-0.7l-1.6%2c0l-0.4%2c0c-0.1%2c0-0.6%2c0-0.5%2c0l-0.7%2c0l-1.6%2c0.1c-0.6%2c0-1.2%2c0.1-1.7%2c0.1 c-2.2%2c0.2-4.4%2c0.5-6.5%2c0.9c-8.6%2c1.6-16.7%2c4.7-23.8%2c9c-7.1%2c4.3-13.3%2c9.6-18.3%2c15.6c-5%2c6-8.9%2c12.7-11.6%2c19.6c-2.7%2c6.9-4.2%2c14.1-4.6%2c21 c-0.1%2c1.7-0.1%2c3.5-0.1%2c5.2c0%2c0.4%2c0%2c0.9%2c0%2c1.3l0.1%2c1.4c0.1%2c0.8%2c0.1%2c1.7%2c0.2%2c2.5c0.3%2c3.5%2c1%2c6.9%2c1.9%2c10.1c1.9%2c6.5%2c4.9%2c12.4%2c8.6%2c17.4 c3.7%2c5%2c8.2%2c9.1%2c12.9%2c12.4c4.7%2c3.2%2c9.8%2c5.5%2c14.8%2c7c5%2c1.5%2c10%2c2.1%2c14.7%2c2.1c0.6%2c0%2c1.2%2c0%2c1.7%2c0c0.3%2c0%2c0.6%2c0%2c0.9%2c0c0.3%2c0%2c0.6%2c0%2c0.9-0.1 c0.5%2c0%2c1-0.1%2c1.5-0.1c0.1%2c0%2c0.3%2c0%2c0.4-0.1l0.5-0.1c0.3%2c0%2c0.6-0.1%2c0.9-0.1c0.6-0.1%2c1.1-0.2%2c1.7-0.3c0.6-0.1%2c1.1-0.2%2c1.6-0.4 c1.1-0.2%2c2.1-0.6%2c3.1-0.9c2-0.7%2c4-1.5%2c5.7-2.4c1.8-0.9%2c3.4-2%2c5-3c0.4-0.3%2c0.9-0.6%2c1.3-1c1.6-1.3%2c1.9-3.7%2c0.6-5.3 c-1.1-1.4-3.1-1.8-4.7-0.9c-0.4%2c0.2-0.8%2c0.4-1.2%2c0.6c-1.4%2c0.7-2.8%2c1.3-4.3%2c1.8c-1.5%2c0.5-3.1%2c0.9-4.7%2c1.2c-0.8%2c0.1-1.6%2c0.2-2.5%2c0.3 c-0.4%2c0-0.8%2c0.1-1.3%2c0.1c-0.4%2c0-0.9%2c0-1.2%2c0c-0.4%2c0-0.8%2c0-1.2%2c0c-0.5%2c0-1%2c0-1.5-0.1c0%2c0-0.3%2c0-0.1%2c0l-0.2%2c0l-0.3%2c0 c-0.2%2c0-0.5%2c0-0.7-0.1c-0.5-0.1-0.9-0.1-1.4-0.2c-3.7-0.5-7.4-1.6-10.9-3.2c-3.6-1.6-7-3.8-10.1-6.6c-3.1-2.8-5.8-6.1-7.9-9.9 c-2.1-3.8-3.6-8-4.3-12.4c-0.3-2.2-0.5-4.5-0.4-6.7c0-0.6%2c0.1-1.2%2c0.1-1.8c0%2c0.2%2c0-0.1%2c0-0.1l0-0.2l0-0.5c0-0.3%2c0.1-0.6%2c0.1-0.9 c0.1-1.2%2c0.3-2.4%2c0.5-3.6c1.7-9.6%2c6.5-19%2c13.9-26.1c1.9-1.8%2c3.9-3.4%2c6-4.9c2.1-1.5%2c4.4-2.8%2c6.8-3.9c2.4-1.1%2c4.8-2%2c7.4-2.7 c2.5-0.7%2c5.1-1.1%2c7.8-1.4c1.3-0.1%2c2.6-0.2%2c4-0.2c0.4%2c0%2c0.6%2c0%2c0.9%2c0l1.1%2c0l0.7%2c0c0.3%2c0%2c0%2c0%2c0.1%2c0l0.3%2c0l1.1%2c0.1 c2.9%2c0.2%2c5.7%2c0.6%2c8.5%2c1.3c5.6%2c1.2%2c11.1%2c3.3%2c16.2%2c6.1c10.2%2c5.7%2c18.9%2c14.5%2c24.2%2c25.1c2.7%2c5.3%2c4.6%2c11%2c5.5%2c16.9c0.2%2c1.5%2c0.4%2c3%2c0.5%2c4.5 l0.1%2c1.1l0.1%2c1.1c0%2c0.4%2c0%2c0.8%2c0%2c1.1c0%2c0.4%2c0%2c0.8%2c0%2c1.1l0%2c1l0%2c1.1c0%2c0.7-0.1%2c1.9-0.1%2c2.6c-0.1%2c1.6-0.3%2c3.3-0.5%2c4.9 c-0.2%2c1.6-0.5%2c3.2-0.8%2c4.8c-0.3%2c1.6-0.7%2c3.2-1.1%2c4.7c-0.8%2c3.1-1.8%2c6.2-3%2c9.3c-2.4%2c6-5.6%2c11.8-9.4%2c17.1 c-7.7%2c10.6-18.2%2c19.2-30.2%2c24.7c-6%2c2.7-12.3%2c4.7-18.8%2c5.7c-3.2%2c0.6-6.5%2c0.9-9.8%2c1l-0.6%2c0l-0.5%2c0l-1.1%2c0l-1.6%2c0l-0.8%2c0 c0.4%2c0-0.1%2c0-0.1%2c0l-0.3%2c0c-1.8%2c0-3.5-0.1-5.3-0.3c-7-0.5-13.9-1.8-20.7-3.7c-6.7-1.9-13.2-4.6-19.4-7.8 c-12.3-6.6-23.4-15.6-32-26.5c-4.3-5.4-8Line truncated
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes preloader-squash {
|
||||
0% {
|
||||
transform: scaleX(1.3) scaleY(.8);
|
||||
animation-timing-function: cubic-bezier(.3, 0, .1, 1);
|
||||
transform-origin: bottom center;
|
||||
.preloader__text {
|
||||
margin-top: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-family: Sans-serif;
|
||||
opacity: 0;
|
||||
animation-name: preloader-fade-in;
|
||||
animation-duration: 0.9s;
|
||||
animation-delay: 1.8s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
15% {
|
||||
transform: scaleX(.75) scaleY(1.25);
|
||||
animation-timing-function: cubic-bezier(0, 0, .7, .75);
|
||||
transform-origin: bottom center;
|
||||
|
||||
.theme-light .preloader__text {
|
||||
color: #52545c;
|
||||
}
|
||||
55% {
|
||||
transform: scaleX(1.05) scaleY(.95);
|
||||
animation-timing-function: cubic-bezier(.9, 0, 1, 1);
|
||||
transform-origin: top center;
|
||||
|
||||
.theme-dark .preloader__text {
|
||||
color: #d8d9da;
|
||||
}
|
||||
95% {
|
||||
transform: scaleX(.75) scaleY(1.25);
|
||||
animation-timing-function: cubic-bezier(0, 0, 0, 1);
|
||||
transform-origin: bottom center;
|
||||
|
||||
@keyframes preloader-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
/*animation-timing-function: linear;*/
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.5, 1);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
100% {
|
||||
transform: scaleX(1.3) scaleY(.8);
|
||||
transform-origin: bottom center;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.7, 1);
|
||||
|
||||
@keyframes preloader-bounce {
|
||||
from,
|
||||
to {
|
||||
transform: translateY(0px);
|
||||
animation-timing-function: cubic-bezier(0.3, 0, 0.1, 1);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-50px);
|
||||
animation-timing-function: cubic-bezier(0.9, 0, 0.7, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fail info */
|
||||
.preloader__text--fail {
|
||||
display: none;
|
||||
}
|
||||
@keyframes preloader-squash {
|
||||
0% {
|
||||
transform: scaleX(1.3) scaleY(0.8);
|
||||
animation-timing-function: cubic-bezier(0.3, 0, 0.1, 1);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
15% {
|
||||
transform: scaleX(0.75) scaleY(1.25);
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.7, 0.75);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
55% {
|
||||
transform: scaleX(1.05) scaleY(0.95);
|
||||
animation-timing-function: cubic-bezier(0.9, 0, 1, 1);
|
||||
transform-origin: top center;
|
||||
}
|
||||
95% {
|
||||
transform: scaleX(0.75) scaleY(1.25);
|
||||
animation-timing-function: cubic-bezier(0, 0, 0, 1);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
100% {
|
||||
transform: scaleX(1.3) scaleY(0.8);
|
||||
transform-origin: bottom center;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.7, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* stop logo animation */
|
||||
.preloader--done .preloader__bounce,
|
||||
.preloader--done .preloader__logo {
|
||||
animation-name: none;
|
||||
display: none;
|
||||
}
|
||||
/* Fail info */
|
||||
.preloader__text--fail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preloader--done .preloader__logo,
|
||||
.preloader--done .preloader__text {
|
||||
display: none;
|
||||
color: #ff5705 !important;
|
||||
font-size: 15px;
|
||||
}
|
||||
/* stop logo animation */
|
||||
.preloader--done .preloader__bounce,
|
||||
.preloader--done .preloader__logo {
|
||||
animation-name: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preloader--done .preloader__text--fail {
|
||||
display: block;
|
||||
}
|
||||
.preloader--done .preloader__logo,
|
||||
.preloader--done .preloader__text {
|
||||
display: none;
|
||||
color: #ff5705 !important;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
[ng\:cloak],
|
||||
[ng-cloak],
|
||||
.ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
.preloader--done .preloader__text--fail {
|
||||
display: block;
|
||||
}
|
||||
|
||||
<div class="preloader">
|
||||
<div class="preloader__enter">
|
||||
<div class="preloader__bounce">
|
||||
<div class="preloader__logo"></div>
|
||||
[ng\:cloak],
|
||||
[ng-cloak],
|
||||
.ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="preloader">
|
||||
<div class="preloader__enter">
|
||||
<div class="preloader__bounce">
|
||||
<div class="preloader__logo"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preloader__text">Loading Grafana</div>
|
||||
<div class="preloader__text preloader__text--fail">
|
||||
<p>
|
||||
<strong>If you're seeing this Grafana has failed to load its application files</strong>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
<p>
|
||||
1. This could be caused by your reverse proxy settings.<br /><br />
|
||||
2. If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath<br />
|
||||
<br />
|
||||
3. If you have a local dev build make sure you build frontend using: yarn start, yarn start:hot, or yarn
|
||||
build<br />
|
||||
<br />
|
||||
4. Sometimes restarting grafana-server can help<br />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preloader__text">Loading Grafana</div>
|
||||
<div class="preloader__text preloader__text--fail">
|
||||
<p>
|
||||
<strong>If you're seeing this Grafana has failed to load its application files</strong>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
<p>
|
||||
1. This could be caused by your reverse proxy settings.<br /><br />
|
||||
2. If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath<br /> <br />
|
||||
3. If you have a local dev build make sure you build frontend using: yarn start, yarn start:hot, or yarn build<br /> <br />
|
||||
4. Sometimes restarting grafana-server can help<br />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<grafana-app class="grafana-app" ng-cloak>
|
||||
<sidemenu class="sidemenu"></sidemenu>
|
||||
<app-notifications-list class="page-alert-list"></app-notifications-list>
|
||||
<dashboard-search></dashboard-search>
|
||||
<grafana-app class="grafana-app" ng-cloak>
|
||||
<sidemenu class="sidemenu"></sidemenu>
|
||||
<app-notifications-list class="page-alert-list"></app-notifications-list>
|
||||
<dashboard-search></dashboard-search>
|
||||
|
||||
<div class="main-view">
|
||||
<div class="scroll-canvas">
|
||||
<div ng-view></div>
|
||||
<div class="main-view">
|
||||
<div class="scroll-canvas">
|
||||
<div ng-view></div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="text-center">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://docs.grafana.org" target="_blank">
|
||||
<i class="fa fa-file-code-o"></i>
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://grafana.com/services/support" target="_blank">
|
||||
<i class="fa fa-support"></i>
|
||||
Support Plans
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://community.grafana.com/" target="_blank">
|
||||
<i class="fa fa-comments-o"></i>
|
||||
Community
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://grafana.com" target="_blank">[[.AppName]]</a>
|
||||
<span>v[[.BuildVersion]] (commit: [[.BuildCommit]])</span>
|
||||
</li>
|
||||
[[if .NewGrafanaVersionExists]]
|
||||
<li>
|
||||
<a href="https://grafana.com/get" target="_blank" bs-tooltip="'[[.NewGrafanaVersion]]'">
|
||||
New version available!
|
||||
</a>
|
||||
</li>
|
||||
[[end]]
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
<footer class="footer">
|
||||
<div class="text-center">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://docs.grafana.org" target="_blank">
|
||||
<i class="fa fa-file-code-o"></i>
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://grafana.com/services/support" target="_blank">
|
||||
<i class="fa fa-support"></i>
|
||||
Support Plans
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://community.grafana.com/" target="_blank">
|
||||
<i class="fa fa-comments-o"></i>
|
||||
Community
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://grafana.com" target="_blank">[[.AppName]]</a>
|
||||
<span>v[[.BuildVersion]] (commit: [[.BuildCommit]])</span>
|
||||
</li>
|
||||
[[if .NewGrafanaVersionExists]]
|
||||
<li>
|
||||
<a href="https://grafana.com/get" target="_blank" bs-tooltip="'[[.NewGrafanaVersion]]'">
|
||||
New version available!
|
||||
</a>
|
||||
</li>
|
||||
[[end]]
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</grafana-app>
|
||||
</grafana-app>
|
||||
|
||||
<script>
|
||||
window.grafanaBootData = {
|
||||
user: [[.User]],
|
||||
settings: [[.Settings]],
|
||||
navTree: [[.NavTree]]
|
||||
};
|
||||
<script>
|
||||
window.grafanaBootData = {
|
||||
user: [[.User]],
|
||||
settings: [[.Settings]],
|
||||
navTree: [[.NavTree]]
|
||||
};
|
||||
|
||||
// In case the js files fails to load the code below will show an info message.
|
||||
window.onload = function() {
|
||||
var preloader = document.getElementsByClassName("preloader");
|
||||
if (preloader.length) {
|
||||
preloader[0].className = "preloader preloader--done";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
// In case the js files fails to load the code below will show an info message.
|
||||
window.onload = function() {
|
||||
var preloader = document.getElementsByClassName("preloader");
|
||||
if (preloader.length) {
|
||||
preloader[0].className = "preloader preloader--done";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
[[if .GoogleTagManagerId]]
|
||||
<!-- Google Tag Manager -->
|
||||
<script>
|
||||
dataLayer = [{
|
||||
'IsSignedIn': '[[.User.IsSignedIn]]',
|
||||
'Email': '[[.User.Email]]',
|
||||
'Name': '[[.User.Name]]',
|
||||
'UserId': '[[.User.Id]]',
|
||||
'OrgId': '[[.User.OrgId]]',
|
||||
'OrgName': '[[.User.OrgName]]',
|
||||
}];
|
||||
</script>
|
||||
<noscript>
|
||||
<iframe src="//www.googletagmanager.com/ns.html?id=[[.GoogleTagManagerId]]" height="0" width="0" style="display:none;visibility:hidden"></iframe>
|
||||
</noscript>
|
||||
<script>(function (w, d, s, l, i) {
|
||||
w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); var f = d.getElementsByTagName(s)[0],
|
||||
j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
|
||||
})(window, document, 'script', 'dataLayer', '[[.GoogleTagManagerId]]');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
[[end]]
|
||||
|
||||
</body>
|
||||
[[if .GoogleTagManagerId]]
|
||||
<!-- Google Tag Manager -->
|
||||
<script>
|
||||
dataLayer = [
|
||||
{
|
||||
IsSignedIn: '[[.User.IsSignedIn]]',
|
||||
Email: '[[.User.Email]]',
|
||||
Name: '[[.User.Name]]',
|
||||
UserId: '[[.User.Id]]',
|
||||
OrgId: '[[.User.OrgId]]',
|
||||
OrgName: '[[.User.OrgName]]',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
<noscript>
|
||||
<iframe
|
||||
src="//www.googletagmanager.com/ns.html?id=[[.GoogleTagManagerId]]"
|
||||
height="0"
|
||||
width="0"
|
||||
style="display:none;visibility:hidden"
|
||||
></iframe>
|
||||
</noscript>
|
||||
<script>
|
||||
(function(w, d, s, l, i) {
|
||||
w[l] = w[l] || [];
|
||||
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
|
||||
var f = d.getElementsByTagName(s)[0],
|
||||
j = d.createElement(s),
|
||||
dl = l != 'dataLayer' ? '&l=' + l : '';
|
||||
j.async = true;
|
||||
j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl;
|
||||
f.parentNode.insertBefore(j, f);
|
||||
})(window, document, 'script', 'dataLayer', '[[.GoogleTagManagerId]]');
|
||||
</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
[[end]]
|
||||
|
||||
<%
|
||||
for (key in htmlWebpackPlugin.files.chunks) { %><%
|
||||
if (htmlWebpackPlugin.files.jsIntegrity) { %>
|
||||
<script
|
||||
src="<%= htmlWebpackPlugin.files.chunks[key].entry %>"
|
||||
type="text/javascript"
|
||||
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[htmlWebpackPlugin.files.js.indexOf(htmlWebpackPlugin.files.chunks[key].entry)] %>"
|
||||
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"></script><%
|
||||
} else { %>
|
||||
<script src="<%= htmlWebpackPlugin.files.chunks[key].entry %>" type="text/javascript"></script><%
|
||||
} %><%
|
||||
} %>
|
||||
<script>
|
||||
performance.mark('js done blocking');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -81,7 +81,7 @@ module.exports = (env = {}) =>
|
||||
new HtmlWebpackPlugin({
|
||||
filename: path.resolve(__dirname, '../../public/views/index.html'),
|
||||
template: path.resolve(__dirname, '../../public/views/index-template.html'),
|
||||
inject: 'body',
|
||||
inject: false,
|
||||
chunksSortMode: 'none',
|
||||
excludeChunks: ['dark', 'light']
|
||||
}),
|
||||
|
||||
@@ -77,7 +77,7 @@ module.exports = merge(common, {
|
||||
new HtmlWebpackPlugin({
|
||||
filename: path.resolve(__dirname, '../../public/views/index.html'),
|
||||
template: path.resolve(__dirname, '../../public/views/index-template.html'),
|
||||
inject: 'body',
|
||||
inject: false,
|
||||
excludeChunks: ['manifest', 'dark', 'light'],
|
||||
chunksSortMode: 'none'
|
||||
}),
|
||||
|
||||
@@ -20371,6 +20371,11 @@ tsutils@^3.9.1:
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
tti-polyfill@0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tti-polyfill/-/tti-polyfill-0.2.2.tgz#f7bbf71b13afa9edf60c8bb0d0c05f134e1513b9"
|
||||
integrity sha512-URIoJxvsHThbQEJij29hIBUDHx9UNoBBCQVjy7L8PnzkqY8N6lsAI6h8JrT1Wt2lA0avus/DkuiJxd9qpfCpqw==
|
||||
|
||||
tty-browserify@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
|
||||
Reference in new issue
Block a user