app.ts 7.01 KB
Newer Older
1
import 'symbol-observable';
2 3 4
import 'core-js/stable';
import 'regenerator-runtime/runtime';

5 6
import 'whatwg-fetch'; // fetch polyfill needed for PhantomJs rendering
import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'; // fetch polyfill needed for PhantomJs rendering
7 8 9
// @ts-ignore
import ttiPolyfill from 'tti-polyfill';

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
import 'file-saver';
import 'lodash';
import 'jquery';
import 'angular';
import 'angular-route';
import 'angular-sanitize';
import 'angular-native-dragdrop';
import 'angular-bindonce';
import 'react';
import 'react-dom';

import 'vendor/bootstrap/bootstrap';
import 'vendor/angular-other/angular-strap';

import $ from 'jquery';
import angular from 'angular';
import config from 'app/core/config';
27
// @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move
28
import _ from 'lodash';
29 30 31 32 33 34
import {
  AppEvents,
  setLocale,
  setMarkdownOptions,
  standardEditorsRegistry,
  standardFieldConfigEditorRegistry,
35
  standardTransformersRegistry,
36
  setTimeZoneResolver,
37
} from '@grafana/data';
38 39
import appEvents from 'app/core/app_events';
import { checkBrowserCompatibility } from 'app/core/utils/browser';
40
import { importPluginModule } from 'app/features/plugins/plugin_loader';
41
import { angularModules, coreModule } from 'app/core/core_module';
42 43
import { registerAngularDirectives } from 'app/core/core';
import { setupAngularRoutes } from 'app/routes/routes';
44
import { registerEchoBackend, setEchoSrv } from '@grafana/runtime';
45 46 47
import { Echo } from './core/services/echo/Echo';
import { reportPerformance } from './core/services/echo/EchoSrv';
import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend';
48 49
import 'app/routes/GrafanaCtrl';
import 'app/features/all';
50
import { getStandardFieldConfigs, getStandardOptionEditors, getScrollbarWidth } from '@grafana/ui';
51
import { getDefaultVariableAdapters, variableAdapters } from './features/variables/adapters';
52
import { initDevFeatures } from './dev';
53
import { getStandardTransformers } from 'app/core/utils/standardTransformers';
54

55 56 57 58 59 60 61
// add move to lodash for backward compatabiltiy
// @ts-ignore
_.move = (array: [], fromIndex: number, toIndex: number) => {
  array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
  return array;
};

62 63
// import symlinked extensions
const extensionsIndex = (require as any).context('.', true, /extensions\/index.ts/);
64
extensionsIndex.keys().forEach((key: any) => {
65
  extensionsIndex(key);
66 67
});

68 69 70 71
if (process.env.NODE_ENV === 'development') {
  initDevFeatures();
}

72 73 74
export class GrafanaApp {
  registerFunctions: any;
  ngModuleDependencies: any[];
75
  preBootModules: any[] | null;
76

77 78 79 80 81 82
  constructor() {
    this.preBootModules = [];
    this.registerFunctions = {};
    this.ngModuleDependencies = [];
  }

83
  useModule(module: angular.IModule) {
84 85 86 87 88 89 90 91 92 93
    if (this.preBootModules) {
      this.preBootModules.push(module);
    } else {
      _.extend(module, this.registerFunctions);
    }
    this.ngModuleDependencies.push(module.name);
    return module;
  }

  init() {
94
    const app = angular.module('grafana', []);
95

96
    addClassIfNoOverlayScrollbar();
97
    setLocale(config.bootData.user.locale);
98
    setTimeZoneResolver(() => config.bootData.user.timezone);
99

100
    setMarkdownOptions({ sanitize: !config.disableSanitizeHtml });
101 102

    standardEditorsRegistry.setInit(getStandardOptionEditors);
103
    standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
104
    standardTransformersRegistry.setInit(getStandardTransformers);
105
    variableAdapters.setInit(getDefaultVariableAdapters);
106

107 108 109 110 111 112 113 114 115
    app.config(
      (
        $locationProvider: angular.ILocationProvider,
        $controllerProvider: angular.IControllerProvider,
        $compileProvider: angular.ICompileProvider,
        $filterProvider: angular.IFilterProvider,
        $httpProvider: angular.IHttpProvider,
        $provide: angular.auto.IProvideService
      ) => {
kevin.xu committed
116
        // pre assign bindings before constructor calls
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
        $compileProvider.preAssignBindingsEnabled(true);

        if (config.buildInfo.env !== 'development') {
          $compileProvider.debugInfoEnabled(false);
        }

        $httpProvider.useApplyAsync(true);

        this.registerFunctions.controller = $controllerProvider.register;
        this.registerFunctions.directive = $compileProvider.directive;
        this.registerFunctions.factory = $provide.factory;
        this.registerFunctions.service = $provide.service;
        this.registerFunctions.filter = $filterProvider.register;

        $provide.decorator('$http', [
          '$delegate',
          '$templateCache',
          ($delegate: any, $templateCache: any) => {
            const get = $delegate.get;
            $delegate.get = (url: string, config: any) => {
              if (url.match(/\.html$/)) {
                // some template's already exist in the cache
                if (!$templateCache.get(url)) {
                  url += '?v=' + new Date().getTime();
                }
142
              }
143 144 145 146 147 148 149
              return get(url, config);
            };
            return $delegate;
          },
        ]);
      }
    );
150 151

    this.ngModuleDependencies = [
152 153 154 155 156 157 158 159
      'grafana.core',
      'ngRoute',
      'ngSanitize',
      '$strap.directives',
      'ang-drag-drop',
      'grafana',
      'pasvaz.bindonce',
      'react',
160 161
    ];

162
    // makes it possible to add dynamic stuff
163
    _.each(angularModules, (m: angular.IModule) => {
164 165
      this.useModule(m);
    });
166

167
    // register react angular wrappers
168
    coreModule.config(setupAngularRoutes);
169 170
    registerAngularDirectives();

171 172
    // disable tool tip animation
    $.fn.tooltip.defaults.animation = false;
173

174 175
    // bootstrap the app
    angular.bootstrap(document, this.ngModuleDependencies).invoke(() => {
176
      _.each(this.preBootModules, (module: angular.IModule) => {
177
        _.extend(module, this.registerFunctions);
178
      });
179 180

      this.preBootModules = null;
181 182 183 184 185 186 187 188 189

      if (!checkBrowserCompatibility()) {
        setTimeout(() => {
          appEvents.emit(AppEvents.alertWarning, [
            'Your browser is not fully supported',
            'A newer browser version is recommended',
          ]);
        }, 1000);
      }
190
    });
191 192 193 194 195

    // Preload selected app plugins
    for (const modulePath of config.pluginsToPreload) {
      importPluginModule(modulePath);
    }
196
  }
197 198 199 200 201 202

  initEchoSrv() {
    setEchoSrv(new Echo({ debug: process.env.NODE_ENV === 'development' }));

    ttiPolyfill.getFirstConsistentlyInteractive().then((tti: any) => {
      // Collecting paint metrics first
203
      const paintMetrics = performance && performance.getEntriesByType ? performance.getEntriesByType('paint') : [];
204 205 206 207 208 209 210 211 212 213 214 215 216

      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()));
    });
  }
217
}
218

219 220 221 222 223 224
function addClassIfNoOverlayScrollbar() {
  if (getScrollbarWidth() > 0) {
    document.body.classList.add('no-overlay-scrollbar');
  }
}

225
export default new GrafanaApp();