mirror of https://github.com/grafana/grafana
commit
9358d0c6f7
@ -0,0 +1,74 @@ |
|||||||
|
import {Emitter} from 'app/core/core'; |
||||||
|
|
||||||
|
export interface GridPos { |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
w: number; |
||||||
|
h: number; |
||||||
|
} |
||||||
|
|
||||||
|
const notPersistedProperties: {[str: string]: boolean} = { |
||||||
|
"events": true, |
||||||
|
"fullscreen": true, |
||||||
|
"isEditing": true, |
||||||
|
}; |
||||||
|
|
||||||
|
export class PanelModel { |
||||||
|
id: number; |
||||||
|
gridPos: GridPos; |
||||||
|
type: string; |
||||||
|
title: string; |
||||||
|
alert?: any; |
||||||
|
|
||||||
|
// non persisted
|
||||||
|
fullscreen: boolean; |
||||||
|
isEditing: boolean; |
||||||
|
events: Emitter; |
||||||
|
|
||||||
|
constructor(model) { |
||||||
|
this.events = new Emitter(); |
||||||
|
|
||||||
|
// copy properties from persisted model
|
||||||
|
for (var property in model) { |
||||||
|
this[property] = model[property]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
getSaveModel() { |
||||||
|
const model: any = {}; |
||||||
|
for (var property in this) { |
||||||
|
if (notPersistedProperties[property] || !this.hasOwnProperty(property)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
model[property] = this[property]; |
||||||
|
} |
||||||
|
|
||||||
|
return model; |
||||||
|
} |
||||||
|
|
||||||
|
setViewMode(fullscreen: boolean, isEditing: boolean) { |
||||||
|
this.fullscreen = fullscreen; |
||||||
|
this.isEditing = isEditing; |
||||||
|
this.events.emit('panel-size-changed'); |
||||||
|
} |
||||||
|
|
||||||
|
updateGridPos(newPos: GridPos) { |
||||||
|
let sizeChanged = false; |
||||||
|
|
||||||
|
if (this.gridPos.w !== newPos.w || this.gridPos.h !== newPos.h) { |
||||||
|
sizeChanged = true; |
||||||
|
} |
||||||
|
|
||||||
|
this.gridPos.x = newPos.x; |
||||||
|
this.gridPos.y = newPos.y; |
||||||
|
this.gridPos.w = newPos.w; |
||||||
|
this.gridPos.h = newPos.h; |
||||||
|
|
||||||
|
if (sizeChanged) { |
||||||
|
console.log('PanelModel sizeChanged event and render events fired'); |
||||||
|
this.events.emit('panel-size-changed'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,131 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import coreModule from 'app/core/core_module'; |
||||||
|
import ReactGridLayout from 'react-grid-layout'; |
||||||
|
import {CELL_HEIGHT, CELL_VMARGIN} from '../DashboardModel'; |
||||||
|
import {DashboardPanel} from './DashboardPanel'; |
||||||
|
import {DashboardModel} from '../DashboardModel'; |
||||||
|
import {PanelContainer} from './PanelContainer'; |
||||||
|
import {PanelModel} from '../PanelModel'; |
||||||
|
import classNames from 'classnames'; |
||||||
|
import sizeMe from 'react-sizeme'; |
||||||
|
|
||||||
|
const COLUMN_COUNT = 12; |
||||||
|
|
||||||
|
function GridWrapper({size, layout, onLayoutChange, children, onResize}) { |
||||||
|
if (size.width === 0) { |
||||||
|
console.log('size is zero!'); |
||||||
|
} |
||||||
|
|
||||||
|
const gridWidth = size.width > 0 ? size.width : 1200; |
||||||
|
|
||||||
|
return ( |
||||||
|
<ReactGridLayout |
||||||
|
width={gridWidth} |
||||||
|
className="layout" |
||||||
|
isDraggable={true} |
||||||
|
isResizable={true} |
||||||
|
measureBeforeMount={false} |
||||||
|
containerPadding={[0, 0]} |
||||||
|
useCSSTransforms={true} |
||||||
|
margin={[CELL_VMARGIN, CELL_VMARGIN]} |
||||||
|
cols={COLUMN_COUNT} |
||||||
|
rowHeight={CELL_HEIGHT} |
||||||
|
draggableHandle=".grid-drag-handle" |
||||||
|
layout={layout} |
||||||
|
onResize={onResize} |
||||||
|
onLayoutChange={onLayoutChange}> |
||||||
|
{children} |
||||||
|
</ReactGridLayout> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const SizedReactLayoutGrid = sizeMe({monitorWidth: true})(GridWrapper); |
||||||
|
|
||||||
|
export interface DashboardGridProps { |
||||||
|
getPanelContainer: () => PanelContainer; |
||||||
|
} |
||||||
|
|
||||||
|
export class DashboardGrid extends React.Component<DashboardGridProps, any> { |
||||||
|
gridToPanelMap: any; |
||||||
|
panelContainer: PanelContainer; |
||||||
|
dashboard: DashboardModel; |
||||||
|
panelMap: {[id: string]: PanelModel}; |
||||||
|
|
||||||
|
constructor(props) { |
||||||
|
super(props); |
||||||
|
this.panelContainer = this.props.getPanelContainer(); |
||||||
|
this.onLayoutChange = this.onLayoutChange.bind(this); |
||||||
|
this.onResize = this.onResize.bind(this); |
||||||
|
|
||||||
|
// subscribe to dashboard events
|
||||||
|
this.dashboard = this.panelContainer.getDashboard(); |
||||||
|
this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this)); |
||||||
|
this.dashboard.on('view-mode-changed', this.triggerForceUpdate.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
buildLayout() { |
||||||
|
const layout = []; |
||||||
|
this.panelMap = {}; |
||||||
|
|
||||||
|
for (let panel of this.dashboard.panels) { |
||||||
|
let stringId = panel.id.toString(); |
||||||
|
this.panelMap[stringId] = panel; |
||||||
|
|
||||||
|
if (!panel.gridPos) { |
||||||
|
console.log('panel without gridpos'); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
layout.push({ |
||||||
|
i: stringId, |
||||||
|
x: panel.gridPos.x, |
||||||
|
y: panel.gridPos.y, |
||||||
|
w: panel.gridPos.w, |
||||||
|
h: panel.gridPos.h, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return layout; |
||||||
|
} |
||||||
|
|
||||||
|
onLayoutChange(newLayout) { |
||||||
|
for (const newPos of newLayout) { |
||||||
|
this.panelMap[newPos.i].updateGridPos(newPos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
triggerForceUpdate() { |
||||||
|
this.forceUpdate(); |
||||||
|
} |
||||||
|
|
||||||
|
onResize(layout, oldItem, newItem) { |
||||||
|
this.panelMap[newItem.i].updateGridPos(newItem); |
||||||
|
} |
||||||
|
|
||||||
|
renderPanels() { |
||||||
|
const panelElements = []; |
||||||
|
|
||||||
|
for (let panel of this.dashboard.panels) { |
||||||
|
const panelClasses = classNames({panel: true, 'panel--fullscreen': panel.fullscreen}); |
||||||
|
panelElements.push( |
||||||
|
<div key={panel.id.toString()} className={panelClasses}> |
||||||
|
<DashboardPanel panel={panel} getPanelContainer={this.props.getPanelContainer} /> |
||||||
|
</div>, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return panelElements; |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
return ( |
||||||
|
<SizedReactLayoutGrid layout={this.buildLayout()} onLayoutChange={this.onLayoutChange} onResize={this.onResize}> |
||||||
|
{this.renderPanels()} |
||||||
|
</SizedReactLayoutGrid> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
coreModule.directive('dashboardGrid', function(reactDirective) { |
||||||
|
return reactDirective(DashboardGrid, [['getPanelContainer', {watchDepth: 'reference', wrapApply: false}]]); |
||||||
|
}); |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import {PanelModel} from '../PanelModel'; |
||||||
|
import {PanelContainer} from './PanelContainer'; |
||||||
|
import {AttachedPanel} from './PanelLoader'; |
||||||
|
|
||||||
|
export interface DashboardPanelProps { |
||||||
|
panel: PanelModel; |
||||||
|
getPanelContainer: () => PanelContainer; |
||||||
|
} |
||||||
|
|
||||||
|
export class DashboardPanel extends React.Component<DashboardPanelProps, any> { |
||||||
|
element: any; |
||||||
|
attachedPanel: AttachedPanel; |
||||||
|
|
||||||
|
constructor(props) { |
||||||
|
super(props); |
||||||
|
this.state = {}; |
||||||
|
} |
||||||
|
|
||||||
|
componentDidMount() { |
||||||
|
const panelContainer = this.props.getPanelContainer(); |
||||||
|
const dashboard = panelContainer.getDashboard(); |
||||||
|
const loader = panelContainer.getPanelLoader(); |
||||||
|
|
||||||
|
this.attachedPanel = loader.load(this.element, this.props.panel, dashboard); |
||||||
|
} |
||||||
|
|
||||||
|
componentWillUnmount() { |
||||||
|
if (this.attachedPanel) { |
||||||
|
this.attachedPanel.destroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
return ( |
||||||
|
<div ref={element => this.element = element} /> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,34 @@ |
|||||||
|
import angular from 'angular'; |
||||||
|
import coreModule from 'app/core/core_module'; |
||||||
|
|
||||||
|
export interface AttachedPanel { |
||||||
|
destroy(); |
||||||
|
} |
||||||
|
|
||||||
|
export class PanelLoader { |
||||||
|
|
||||||
|
/** @ngInject */ |
||||||
|
constructor(private $compile, private $rootScope) { |
||||||
|
} |
||||||
|
|
||||||
|
load(elem, panel, dashboard): AttachedPanel { |
||||||
|
var template = '<plugin-component type="panel"></plugin-component>'; |
||||||
|
var panelScope = this.$rootScope.$new(); |
||||||
|
panelScope.panel = panel; |
||||||
|
panelScope.dashboard = dashboard; |
||||||
|
|
||||||
|
const compiledElem = this.$compile(template)(panelScope); |
||||||
|
const rootNode = angular.element(elem); |
||||||
|
rootNode.append(compiledElem); |
||||||
|
|
||||||
|
return { |
||||||
|
destroy: () => { |
||||||
|
console.log('AttachedPanel:Destroy, id' + panel.id); |
||||||
|
panelScope.$destroy(); |
||||||
|
compiledElem.remove(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
coreModule.service('panelLoader', PanelLoader); |
||||||
@ -1,211 +0,0 @@ |
|||||||
// ///<reference path="../../../headers/common.d.ts" />
|
|
||||||
//
|
|
||||||
// import coreModule from 'app/core/core_module';
|
|
||||||
// import {CELL_HEIGHT, CELL_VMARGIN} from '../model';
|
|
||||||
//
|
|
||||||
// import 'jquery-ui';
|
|
||||||
// import 'gridstack/dist/jquery.jQueryUI';
|
|
||||||
// import 'gridstack';
|
|
||||||
//
|
|
||||||
// const template = `
|
|
||||||
// <div class="grid-stack">
|
|
||||||
// <dash-grid-item ng-repeat="panel in ctrl.dashboard.panels track by panel.id"
|
|
||||||
// class="grid-stack-item"
|
|
||||||
// grid-ctrl="ctrl"
|
|
||||||
// panel="panel">
|
|
||||||
// <plugin-component type="panel" class="grid-stack-item-content">
|
|
||||||
// </plugin-component>
|
|
||||||
// </dash-grid-item>
|
|
||||||
// </div>
|
|
||||||
// `;
|
|
||||||
//
|
|
||||||
// var rowIndex = 0;
|
|
||||||
//
|
|
||||||
// export class GridCtrl {
|
|
||||||
// options: any;
|
|
||||||
// dashboard: any;
|
|
||||||
// panels: any;
|
|
||||||
// gridstack: any;
|
|
||||||
// gridElem: any;
|
|
||||||
// isInitialized: boolean;
|
|
||||||
// isDestroyed: boolean;
|
|
||||||
// index: number;
|
|
||||||
// changeRenderPromise: any;
|
|
||||||
//
|
|
||||||
// #<{(|* @ngInject |)}>#
|
|
||||||
// constructor(private $scope, private $element, private $timeout) {
|
|
||||||
// console.log(this.dashboard);
|
|
||||||
// this.index = rowIndex;
|
|
||||||
// rowIndex += 1;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// init() {
|
|
||||||
// this.gridElem = this.$element.find('.grid-stack');
|
|
||||||
//
|
|
||||||
// this.gridstack = this.gridElem.gridstack({
|
|
||||||
// animate: true,
|
|
||||||
// cellHeight: CELL_HEIGHT,
|
|
||||||
// verticalMargin: CELL_VMARGIN,
|
|
||||||
// acceptWidgets: '.grid-stack-item',
|
|
||||||
// handle: '.grid-drag-handle'
|
|
||||||
// }).data('gridstack');
|
|
||||||
//
|
|
||||||
// this.isInitialized = true;
|
|
||||||
//
|
|
||||||
// this.gridElem.on('added', (e, items) => {
|
|
||||||
// for (let item of items) {
|
|
||||||
// this.onGridStackItemAdded(item);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// this.gridElem.on('removed', (e, items) => {
|
|
||||||
// for (let item of items) {
|
|
||||||
// this.onGridStackItemRemoved(item);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// this.gridElem.on('change', (e, items) => {
|
|
||||||
// this.$timeout(() => this.onGridStackItemsChanged(items), 50);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onGridStackItemAdded(item) {
|
|
||||||
// console.log('row: ' + this.index + ' item added', item);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onGridStackItemRemoved(item) {
|
|
||||||
// console.log('row: ' + this.index + ' item removed', item.id, item);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// onGridStackItemsChanged(items) {
|
|
||||||
// console.log('onGridStackItemsChanged');
|
|
||||||
//
|
|
||||||
// for (let item of items) {
|
|
||||||
// // find panel
|
|
||||||
// var panel = this.dashboard.getPanelById(parseInt(item.id));
|
|
||||||
//
|
|
||||||
// if (!panel) {
|
|
||||||
// console.log('item change but no panel found for item', item);
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // update panel model position
|
|
||||||
// panel.x = item.x;
|
|
||||||
// panel.y = item.y;
|
|
||||||
// panel.width = item.width;
|
|
||||||
// panel.height = item.height;
|
|
||||||
//
|
|
||||||
// console.log('updating panel: ' + panel.id + ' x: ' + panel.x + ' y: ' + panel.y);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.dashboard.panels.sort(function (a, b) {
|
|
||||||
// let aScore = a.x + (a.y * 12);
|
|
||||||
// let bScore = b.x + (b.y * 12);
|
|
||||||
// if (aScore < bScore) { return -1; }
|
|
||||||
// if (aScore > bScore) { return 1; }
|
|
||||||
// return 0;
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if (this.changeRenderPromise) {
|
|
||||||
// this.$timeout.cancel(this.changeRenderPromise);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.changeRenderPromise = this.$timeout(() => {
|
|
||||||
// console.log('broadcasting render');
|
|
||||||
// this.$scope.$broadcast('render');
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// destroy() {
|
|
||||||
// this.gridstack.destroy();
|
|
||||||
// this.gridstack = null;
|
|
||||||
// this.isDestroyed = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #<{(|* @ngInject *|)}>#
|
|
||||||
// export function dashGrid($timeout) {
|
|
||||||
// return {
|
|
||||||
// restrict: 'E',
|
|
||||||
// template: template,
|
|
||||||
// controller: GridCtrl,
|
|
||||||
// bindToController: true,
|
|
||||||
// controllerAs: 'ctrl',
|
|
||||||
// scope: {
|
|
||||||
// dashboard: "=",
|
|
||||||
// },
|
|
||||||
// link: function(scope, elem, attrs, ctrl) {
|
|
||||||
// $timeout(function() {
|
|
||||||
// ctrl.init();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// scope.$on('$destroy', () => {
|
|
||||||
// ctrl.destroy();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #<{(|* @ngInject *|)}>#
|
|
||||||
// export function dashGridItem($timeout, $rootScope) {
|
|
||||||
// return {
|
|
||||||
// restrict: "E",
|
|
||||||
// scope: {
|
|
||||||
// panel: '=',
|
|
||||||
// gridCtrl: '='
|
|
||||||
// },
|
|
||||||
// link: function (scope, element, attrs) {
|
|
||||||
// let gridCtrl = scope.gridCtrl;
|
|
||||||
// let panel = scope.panel;
|
|
||||||
// let gridStackNode = null;
|
|
||||||
//
|
|
||||||
// element.attr({
|
|
||||||
// 'data-gs-id': panel.id,
|
|
||||||
// 'data-gs-x': panel.x,
|
|
||||||
// 'data-gs-y': panel.y,
|
|
||||||
// 'data-gs-width': panel.width,
|
|
||||||
// 'data-gs-height': panel.height,
|
|
||||||
// 'data-gs-no-resize': panel.type === 'row',
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// $rootScope.onAppEvent('panel-fullscreen-exit', (evt, payload) => {
|
|
||||||
// if (panel.id !== payload.panelId) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// gridCtrl.gridstack.locked(element, false);
|
|
||||||
// element.removeClass('panel-fullscreen');
|
|
||||||
// }, scope);
|
|
||||||
//
|
|
||||||
// $rootScope.onAppEvent('panel-fullscreen-enter', (evt, payload) => {
|
|
||||||
// if (panel.id !== payload.panelId) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// element.addClass('panel-fullscreen');
|
|
||||||
// }, scope);
|
|
||||||
//
|
|
||||||
// scope.$on('$destroy', () => {
|
|
||||||
// console.log('grid-item scope $destroy');
|
|
||||||
// if (gridCtrl.isDestroyed) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (gridStackNode) {
|
|
||||||
// console.log('grid-item scope $destroy removeWidget');
|
|
||||||
// gridStackNode._grid.removeWidget(element);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if (gridCtrl.isInitialized) {
|
|
||||||
// gridCtrl.gridstack.makeWidget(element);
|
|
||||||
// gridStackNode = element.data('_gridstack_node');
|
|
||||||
// } else {
|
|
||||||
// setTimeout(function() {
|
|
||||||
// gridStackNode = element.data('_gridstack_node');
|
|
||||||
// }, 500);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// coreModule.directive('dashGrid', dashGrid);
|
|
||||||
// coreModule.directive('dashGridItem', dashGridItem);
|
|
||||||
@ -1,232 +0,0 @@ |
|||||||
Skip to content |
|
||||||
This repository |
|
||||||
Search |
|
||||||
Pull requests |
|
||||||
Issues |
|
||||||
Marketplace |
|
||||||
Gist |
|
||||||
@torkelo |
|
||||||
Sign out |
|
||||||
Unwatch 946 |
|
||||||
Unstar 17,021 |
|
||||||
Fork 2,862 grafana/grafana |
|
||||||
Code Issues 1,079 Pull requests 46 Projects 1 Wiki Settings Insights |
|
||||||
Branch: gridstack Find file Copy pathgrafana/public/app/core/components/dashgrid/dashgrid.ts |
|
||||||
a6bbcb8 on Jun 13 |
|
||||||
@torkelo torkelo ux: gridstack poc |
|
||||||
1 contributor |
|
||||||
RawBlameHistory |
|
||||||
213 lines (181 sloc) 5.45 KB |
|
||||||
///<reference path="../../../headers/common.d.ts" /> |
|
||||||
|
|
||||||
import $ from 'jquery'; |
|
||||||
import coreModule from '../../core_module'; |
|
||||||
|
|
||||||
import 'jquery-ui'; |
|
||||||
import 'gridstack'; |
|
||||||
import 'gridstack.jquery-ui'; |
|
||||||
|
|
||||||
const template = ` |
|
||||||
<div gridstack gridstack-handler="ctrl.gridstack" class="grid-stack" |
|
||||||
options="ctrl.options" |
|
||||||
on-change="ctrl.onChange(event,items)" |
|
||||||
on-drag-start="ctrl.onDragStart(event,ui)" |
|
||||||
on-drag-stop="ctrl.onDragStop(event, ui)" |
|
||||||
on-resize-start="ctrl.onResizeStart(event, ui)" |
|
||||||
on-resize-stop="ctrl.onResizeStop(event, ui)"> |
|
||||||
<div gridstack-item ng-repeat="panel in ctrl.panels" |
|
||||||
class="grid-stack-item" |
|
||||||
gs-item-id="panel.id" |
|
||||||
gs-item-x="panel.x" |
|
||||||
gs-item-y="panel.y" |
|
||||||
gs-item-width="panel.width" |
|
||||||
gs-item-height="panel.height" |
|
||||||
gs-item-autopos="1" |
|
||||||
on-item-added="ctrl.onItemAdded(item)" |
|
||||||
on-item-removed="ctrl.onItemRemoved(item)"> |
|
||||||
<plugin-component type="panel" class="panel-margin grid-stack-item-content"> |
|
||||||
</plugin-component> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
`; |
|
||||||
|
|
||||||
export class DashGridCtrl { |
|
||||||
options: any; |
|
||||||
|
|
||||||
/** @ngInject */ |
|
||||||
constructor(private $rootScope) { |
|
||||||
this.options = { |
|
||||||
animate: true, |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
onResizeStop() { |
|
||||||
this.$rootScope.$broadcast('render'); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export function dashGrid($timeout) { |
|
||||||
return { |
|
||||||
restrict: 'E', |
|
||||||
template: template, |
|
||||||
controller: DashGridCtrl, |
|
||||||
bindToController: true, |
|
||||||
controllerAs: 'ctrl', |
|
||||||
scope: { |
|
||||||
dashboard: "=" |
|
||||||
}, |
|
||||||
link: function(scope, elem, attrs, ctrl) { |
|
||||||
|
|
||||||
ctrl.panels = []; |
|
||||||
ctrl.dashboard.forEachPanel((panel, panelIndex, row, rowIndex) => { |
|
||||||
panel.width = 4; |
|
||||||
panel.height = 4; |
|
||||||
panel.x = panelIndex * 4; |
|
||||||
panel.y = rowIndex * 4; |
|
||||||
ctrl.panels.push(panel); |
|
||||||
}); |
|
||||||
|
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
/** @ngInject */ |
|
||||||
coreModule.controller('GridstackController', ['$scope', function($scope) { |
|
||||||
|
|
||||||
var gridstack = null; |
|
||||||
|
|
||||||
this.init = function(element, options) { |
|
||||||
gridstack = element.gridstack(options).data('gridstack'); |
|
||||||
return gridstack; |
|
||||||
}; |
|
||||||
|
|
||||||
this.removeItem = function(element) { |
|
||||||
if (gridstack) { |
|
||||||
return gridstack.removeWidget(element, false); |
|
||||||
} |
|
||||||
return null; |
|
||||||
}; |
|
||||||
|
|
||||||
this.addItem = function(element) { |
|
||||||
if (gridstack) { |
|
||||||
gridstack.makeWidget(element); |
|
||||||
return element; |
|
||||||
} |
|
||||||
return null; |
|
||||||
}; |
|
||||||
|
|
||||||
}]); |
|
||||||
|
|
||||||
/** @ngInject */ |
|
||||||
coreModule.directive('gridstack', ['$timeout', function($timeout) { |
|
||||||
return { |
|
||||||
restrict: "A", |
|
||||||
controller: 'GridstackController', |
|
||||||
scope: { |
|
||||||
onChange: '&', |
|
||||||
onDragStart: '&', |
|
||||||
onDragStop: '&', |
|
||||||
onResizeStart: '&', |
|
||||||
onResizeStop: '&', |
|
||||||
gridstackHandler: '=', |
|
||||||
options: '=' |
|
||||||
}, |
|
||||||
link: function (scope, element, attrs, controller, ngModel) { |
|
||||||
|
|
||||||
var gridstack = controller.init(element, scope.options); |
|
||||||
scope.gridstackHandler = gridstack; |
|
||||||
|
|
||||||
element.on('change', function (e, items) { |
|
||||||
$timeout(function() { |
|
||||||
scope.$apply(); |
|
||||||
scope.onChange({event: e, items: items}); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
element.on('dragstart', function(e, ui) { |
|
||||||
scope.onDragStart({event: e, ui: ui}); |
|
||||||
}); |
|
||||||
|
|
||||||
element.on('dragstop', function(e, ui) { |
|
||||||
$timeout(function() { |
|
||||||
scope.$apply(); |
|
||||||
scope.onDragStop({event: e, ui: ui}); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
element.on('resizestart', function(e, ui) { |
|
||||||
scope.onResizeStart({event: e, ui: ui}); |
|
||||||
}); |
|
||||||
|
|
||||||
element.on('resizestop', function(e, ui) { |
|
||||||
$timeout(function() { |
|
||||||
scope.$apply(); |
|
||||||
scope.onResizeStop({event: e, ui: ui}); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
} |
|
||||||
}; |
|
||||||
}]); |
|
||||||
|
|
||||||
/** @ngInject */ |
|
||||||
coreModule.directive('gridstackItem', ['$timeout', function($timeout) { |
|
||||||
|
|
||||||
return { |
|
||||||
restrict: "A", |
|
||||||
controller: 'GridstackController', |
|
||||||
require: '^gridstack', |
|
||||||
scope: { |
|
||||||
gridstackItem: '=', |
|
||||||
onItemAdded: '&', |
|
||||||
onItemRemoved: '&', |
|
||||||
gsItemId: '=', |
|
||||||
gsItemX: '=', |
|
||||||
gsItemY: '=', |
|
||||||
gsItemWidth: '=', |
|
||||||
gsItemHeight: '=', |
|
||||||
gsItemAutopos: '=' |
|
||||||
}, |
|
||||||
link: function (scope, element, attrs, controller) { |
|
||||||
$(element).attr('data-gs-id', scope.gsItemId); |
|
||||||
$(element).attr('data-gs-x', scope.gsItemX); |
|
||||||
$(element).attr('data-gs-y', scope.gsItemY); |
|
||||||
$(element).attr('data-gs-width', scope.gsItemWidth); |
|
||||||
$(element).attr('data-gs-height', scope.gsItemHeight); |
|
||||||
$(element).attr('data-gs-auto-position', scope.gsItemAutopos); |
|
||||||
var widget = controller.addItem(element); |
|
||||||
var item = element.data('_gridstack_node'); |
|
||||||
$timeout(function() { |
|
||||||
scope.onItemAdded({item: item}); |
|
||||||
}); |
|
||||||
scope.$watch(function () { return $(element).attr('data-gs-id'); }, function (val) { |
|
||||||
scope.gsItemId = val; |
|
||||||
}); |
|
||||||
scope.$watch(function(){ return $(element).attr('data-gs-x'); }, function(val) { |
|
||||||
scope.gsItemX = val; |
|
||||||
}); |
|
||||||
|
|
||||||
scope.$watch(function(){ return $(element).attr('data-gs-y'); }, function(val) { |
|
||||||
scope.gsItemY = val; |
|
||||||
}); |
|
||||||
|
|
||||||
scope.$watch(function(){ return $(element).attr('data-gs-width'); }, function(val) { |
|
||||||
scope.gsItemWidth = val; |
|
||||||
}); |
|
||||||
|
|
||||||
scope.$watch(function(){ return $(element).attr('data-gs-height'); }, function(val) { |
|
||||||
scope.gsItemHeight = val; |
|
||||||
}); |
|
||||||
|
|
||||||
element.bind('$destroy', function() { |
|
||||||
var item = element.data('_gridstack_node'); |
|
||||||
scope.onItemRemoved({item: item}); |
|
||||||
controller.removeItem(element); |
|
||||||
}); |
|
||||||
} |
|
||||||
}; |
|
||||||
}]); |
|
||||||
|
|
||||||
coreModule.directive('dashGrid', dashGrid); |
|
||||||
Contact GitHub API Training Shop Blog About |
|
||||||
© 2017 GitHub, Inc. Terms Privacy Security Status Help |
|
||||||
@ -1,95 +0,0 @@ |
|||||||
define([ |
|
||||||
'angular', |
|
||||||
'lodash', |
|
||||||
'app/core/utils/kbn' |
|
||||||
], |
|
||||||
function (angular, _, kbn) { |
|
||||||
'use strict'; |
|
||||||
|
|
||||||
var module = angular.module('grafana.controllers'); |
|
||||||
|
|
||||||
module.controller('GraphiteImportCtrl', function($scope, datasourceSrv, dashboardSrv, $location) { |
|
||||||
$scope.options = {}; |
|
||||||
|
|
||||||
$scope.init = function() { |
|
||||||
$scope.datasources = []; |
|
||||||
_.each(datasourceSrv.getAll(), function(ds) { |
|
||||||
if (ds.type === 'graphite') { |
|
||||||
$scope.options.sourceName = ds.name; |
|
||||||
$scope.datasources.push(ds.name); |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.listAll = function() { |
|
||||||
datasourceSrv.get($scope.options.sourceName).then(function(datasource) { |
|
||||||
$scope.datasource = datasource; |
|
||||||
$scope.datasource.listDashboards('').then(function(results) { |
|
||||||
$scope.dashboards = results; |
|
||||||
}, function(err) { |
|
||||||
var message = err.message || err.statusText || 'Error'; |
|
||||||
$scope.appEvent('alert-error', ['Failed to load dashboard list from graphite', message]); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.import = function(dashName) { |
|
||||||
$scope.datasource.loadDashboard(dashName).then(function(results) { |
|
||||||
if (!results.data || !results.data.state) { |
|
||||||
throw { message: 'no dashboard state received from graphite' }; |
|
||||||
} |
|
||||||
|
|
||||||
graphiteToGrafanaTranslator(results.data.state, $scope.datasource.name); |
|
||||||
}, function(err) { |
|
||||||
var message = err.message || err.statusText || 'Error'; |
|
||||||
$scope.appEvent('alert-error', ['Failed to load dashboard from graphite', message]); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
function graphiteToGrafanaTranslator(state, datasource) { |
|
||||||
var graphsPerRow = 2; |
|
||||||
var rowHeight = 300; |
|
||||||
var rowTemplate; |
|
||||||
var currentRow; |
|
||||||
var panel; |
|
||||||
|
|
||||||
rowTemplate = { |
|
||||||
title: '', |
|
||||||
panels: [], |
|
||||||
height: rowHeight |
|
||||||
}; |
|
||||||
|
|
||||||
currentRow = angular.copy(rowTemplate); |
|
||||||
|
|
||||||
var newDashboard = dashboardSrv.create({}); |
|
||||||
newDashboard.rows = []; |
|
||||||
newDashboard.title = state.name; |
|
||||||
newDashboard.rows.push(currentRow); |
|
||||||
|
|
||||||
_.each(state.graphs, function(graph, index) { |
|
||||||
if (currentRow.panels.length === graphsPerRow) { |
|
||||||
currentRow = angular.copy(rowTemplate); |
|
||||||
newDashboard.rows.push(currentRow); |
|
||||||
} |
|
||||||
|
|
||||||
panel = { |
|
||||||
type: 'graph', |
|
||||||
span: 12 / graphsPerRow, |
|
||||||
title: graph[1].title, |
|
||||||
targets: [], |
|
||||||
datasource: datasource, |
|
||||||
id: index + 1 |
|
||||||
}; |
|
||||||
|
|
||||||
_.each(graph[1].target, function(target) { |
|
||||||
panel.targets.push({ target: target }); |
|
||||||
}); |
|
||||||
|
|
||||||
currentRow.panels.push(panel); |
|
||||||
}); |
|
||||||
|
|
||||||
window.grafanaImportDashboard = newDashboard; |
|
||||||
$location.path('/dashboard-import/' + kbn.slugifyForUrl(newDashboard.title)); |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
@ -0,0 +1,24 @@ |
|||||||
|
@import "~react-grid-layout/css/styles.css"; |
||||||
|
@import "~react-resizable/css/styles.css"; |
||||||
|
|
||||||
|
.panel-in-fullscreen { |
||||||
|
|
||||||
|
.react-grid-layout { |
||||||
|
height: 100% !important; |
||||||
|
} |
||||||
|
|
||||||
|
.react-grid-item { |
||||||
|
display: none; |
||||||
|
transition-property: none !important; |
||||||
|
} |
||||||
|
|
||||||
|
.panel--fullscreen { |
||||||
|
display: block !important; |
||||||
|
position: unset !important; |
||||||
|
width: 100% !important; |
||||||
|
height: 100% !important; |
||||||
|
transform: translate(0px, 0px) !important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
@ -1,325 +0,0 @@ |
|||||||
.grid-stack-item > .ui-resizable-handle { |
|
||||||
filter: none; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack { |
|
||||||
position: relative; |
|
||||||
min-height: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-rtl { |
|
||||||
direction: ltr; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-rtl > .grid-stack-item { |
|
||||||
direction: rtl; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack .grid-stack-placeholder > .placeholder-content { |
|
||||||
background: $input-label-bg; |
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236,10.8); |
|
||||||
margin: 0; |
|
||||||
position: absolute; |
|
||||||
top: 0; |
|
||||||
left: 5px; |
|
||||||
right: 5px; |
|
||||||
bottom: 0; |
|
||||||
width: auto; |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item { |
|
||||||
min-width: 8.3333333333%; |
|
||||||
position: absolute; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item > .grid-stack-item-content { |
|
||||||
margin: 0; |
|
||||||
position: absolute; |
|
||||||
top: 0; |
|
||||||
left: 7px; |
|
||||||
right: 7px; |
|
||||||
bottom: 0; |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item > .ui-resizable-handle { |
|
||||||
position: absolute; |
|
||||||
display: block; |
|
||||||
-ms-touch-action: none; |
|
||||||
touch-action: none; |
|
||||||
font-size: 10px; |
|
||||||
color: $text-color-weak; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item.ui-resizable-disabled > .ui-resizable-handle, |
|
||||||
.grid-stack > .grid-stack-item.ui-resizable-autohide > .ui-resizable-handle { |
|
||||||
display: none; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item.ui-draggable-dragging, .grid-stack > .grid-stack-item.ui-resizable-resizing { |
|
||||||
z-index: 100; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item.ui-draggable-dragging > .grid-stack-item-content, |
|
||||||
.grid-stack > .grid-stack-item.ui-draggable-dragging > .grid-stack-item-content, .grid-stack > .grid-stack-item.ui-resizable-resizing > .grid-stack-item-content, |
|
||||||
.grid-stack > .grid-stack-item.ui-resizable-resizing > .grid-stack-item-content { |
|
||||||
box-shadow: 1px 4px 6px rgba(0, 0, 0, 0.2); |
|
||||||
opacity: 0.8; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item > .ui-resizable-se, |
|
||||||
.grid-stack > .grid-stack-item > .ui-resizable-sw { |
|
||||||
font-family: 'grafana-icons' !important; |
|
||||||
speak: none; |
|
||||||
font-style: normal; |
|
||||||
font-weight: normal; |
|
||||||
font-variant: normal; |
|
||||||
text-transform: none; |
|
||||||
line-height: 1; |
|
||||||
-webkit-font-smoothing: antialiased; |
|
||||||
-moz-osx-font-smoothing: grayscale; |
|
||||||
&::before { |
|
||||||
content: "\e90b"; |
|
||||||
} |
|
||||||
} |
|
||||||
.grid-stack > .grid-stack-item > .ui-resizable-se { |
|
||||||
cursor: se-resize; |
|
||||||
width: 16px; |
|
||||||
height: 16px; |
|
||||||
right: 6px; |
|
||||||
bottom: -2px; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item.ui-draggable-dragging > .ui-resizable-handle { |
|
||||||
display: none !important; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='1'] { |
|
||||||
width: 8.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='1'] { |
|
||||||
left: 8.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='1'] { |
|
||||||
min-width: 8.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='1'] { |
|
||||||
max-width: 8.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='2'] { |
|
||||||
width: 16.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='2'] { |
|
||||||
left: 16.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='2'] { |
|
||||||
min-width: 16.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='2'] { |
|
||||||
max-width: 16.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='3'] { |
|
||||||
width: 25%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='3'] { |
|
||||||
left: 25%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='3'] { |
|
||||||
min-width: 25%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='3'] { |
|
||||||
max-width: 25%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='4'] { |
|
||||||
width: 33.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='4'] { |
|
||||||
left: 33.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='4'] { |
|
||||||
min-width: 33.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='4'] { |
|
||||||
max-width: 33.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='5'] { |
|
||||||
width: 41.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='5'] { |
|
||||||
left: 41.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='5'] { |
|
||||||
min-width: 41.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='5'] { |
|
||||||
max-width: 41.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='6'] { |
|
||||||
width: 50%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='6'] { |
|
||||||
left: 50%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='6'] { |
|
||||||
min-width: 50%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='6'] { |
|
||||||
max-width: 50%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='7'] { |
|
||||||
width: 58.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='7'] { |
|
||||||
left: 58.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='7'] { |
|
||||||
min-width: 58.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='7'] { |
|
||||||
max-width: 58.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='8'] { |
|
||||||
width: 66.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='8'] { |
|
||||||
left: 66.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='8'] { |
|
||||||
min-width: 66.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='8'] { |
|
||||||
max-width: 66.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='9'] { |
|
||||||
width: 75%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='9'] { |
|
||||||
left: 75%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='9'] { |
|
||||||
min-width: 75%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='9'] { |
|
||||||
max-width: 75%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='10'] { |
|
||||||
width: 83.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='10'] { |
|
||||||
left: 83.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='10'] { |
|
||||||
min-width: 83.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='10'] { |
|
||||||
max-width: 83.3333333333%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='11'] { |
|
||||||
width: 91.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='11'] { |
|
||||||
left: 91.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='11'] { |
|
||||||
min-width: 91.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='11'] { |
|
||||||
max-width: 91.6666666667%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-width='12'] { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-x='12'] { |
|
||||||
left: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-min-width='12'] { |
|
||||||
min-width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item[data-gs-max-width='12'] { |
|
||||||
max-width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-animate, |
|
||||||
.grid-stack.grid-stack-animate .grid-stack-item { |
|
||||||
-webkit-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s; |
|
||||||
-moz-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s; |
|
||||||
-ms-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s; |
|
||||||
-o-transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s; |
|
||||||
transition: left 0.3s, top 0.3s, height 0.3s, width 0.3s; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-animate .grid-stack-item.ui-draggable-dragging, |
|
||||||
.grid-stack.grid-stack-animate .grid-stack-item.ui-resizable-resizing, |
|
||||||
.grid-stack.grid-stack-animate .grid-stack-item.grid-stack-placeholder { |
|
||||||
-webkit-transition: left 0s, top 0s, height 0s, width 0s; |
|
||||||
-moz-transition: left 0s, top 0s, height 0s, width 0s; |
|
||||||
-ms-transition: left 0s, top 0s, height 0s, width 0s; |
|
||||||
-o-transition: left 0s, top 0s, height 0s, width 0s; |
|
||||||
transition: left 0s, top 0s, height 0s, width 0s; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-one-column-mode { |
|
||||||
height: auto !important; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-one-column-mode > .grid-stack-item { |
|
||||||
position: relative !important; |
|
||||||
width: auto !important; |
|
||||||
left: 0 !important; |
|
||||||
top: auto !important; |
|
||||||
margin-bottom: 20px; |
|
||||||
max-width: none !important; |
|
||||||
} |
|
||||||
|
|
||||||
.grid-stack.grid-stack-one-column-mode > .grid-stack-item > .ui-resizable-handle { |
|
||||||
display: none; |
|
||||||
} |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
declare let helpers: any; |
|
||||||
export default helpers; |
|
||||||
|
|
||||||
|
|
||||||
@ -1,180 +0,0 @@ |
|||||||
define([ |
|
||||||
'lodash', |
|
||||||
'app/core/config', |
|
||||||
'app/core/utils/datemath', |
|
||||||
], function(_, config, dateMath) { |
|
||||||
'use strict'; |
|
||||||
|
|
||||||
function ControllerTestContext() { |
|
||||||
var self = this; |
|
||||||
|
|
||||||
this.datasource = {}; |
|
||||||
this.$element = {}; |
|
||||||
this.annotationsSrv = {}; |
|
||||||
this.timeSrv = new TimeSrvStub(); |
|
||||||
this.templateSrv = new TemplateSrvStub(); |
|
||||||
this.datasourceSrv = { |
|
||||||
getMetricSources: function() {}, |
|
||||||
get: function() { |
|
||||||
return { |
|
||||||
then: function(callback) { |
|
||||||
callback(self.datasource); |
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
this.providePhase = function(mocks) { |
|
||||||
return window.module(function($provide) { |
|
||||||
$provide.value('datasourceSrv', self.datasourceSrv); |
|
||||||
$provide.value('annotationsSrv', self.annotationsSrv); |
|
||||||
$provide.value('timeSrv', self.timeSrv); |
|
||||||
$provide.value('templateSrv', self.templateSrv); |
|
||||||
$provide.value('$element', self.$element); |
|
||||||
_.each(mocks, function(value, key) { |
|
||||||
$provide.value(key, value); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.createPanelController = function(Ctrl) { |
|
||||||
return window.inject(function($controller, $rootScope, $q, $location, $browser) { |
|
||||||
self.scope = $rootScope.$new(); |
|
||||||
self.$location = $location; |
|
||||||
self.$browser = $browser; |
|
||||||
self.$q = $q; |
|
||||||
self.panel = {type: 'test'}; |
|
||||||
self.dashboard = {meta: {}}; |
|
||||||
|
|
||||||
$rootScope.appEvent = sinon.spy(); |
|
||||||
$rootScope.onAppEvent = sinon.spy(); |
|
||||||
$rootScope.colors = []; |
|
||||||
|
|
||||||
for (var i = 0; i < 50; i++) { $rootScope.colors.push('#' + i); } |
|
||||||
|
|
||||||
config.panels['test'] = {info: {}}; |
|
||||||
self.ctrl = $controller(Ctrl, {$scope: self.scope}, { |
|
||||||
panel: self.panel, dashboard: self.dashboard, row: {} |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.createControllerPhase = function(controllerName) { |
|
||||||
return window.inject(function($controller, $rootScope, $q, $location, $browser) { |
|
||||||
self.scope = $rootScope.$new(); |
|
||||||
self.$location = $location; |
|
||||||
self.$browser = $browser; |
|
||||||
self.scope.contextSrv = {}; |
|
||||||
self.scope.panel = {}; |
|
||||||
self.scope.row = { panels:[] }; |
|
||||||
self.scope.dashboard = {meta: {}}; |
|
||||||
self.scope.dashboardMeta = {}; |
|
||||||
self.scope.dashboardViewState = new DashboardViewStateStub(); |
|
||||||
self.scope.appEvent = sinon.spy(); |
|
||||||
self.scope.onAppEvent = sinon.spy(); |
|
||||||
|
|
||||||
$rootScope.colors = []; |
|
||||||
for (var i = 0; i < 50; i++) { $rootScope.colors.push('#' + i); } |
|
||||||
|
|
||||||
self.$q = $q; |
|
||||||
self.scope.skipDataOnInit = true; |
|
||||||
self.scope.skipAutoInit = true; |
|
||||||
self.controller = $controller(controllerName, { |
|
||||||
$scope: self.scope |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function ServiceTestContext() { |
|
||||||
var self = this; |
|
||||||
self.templateSrv = new TemplateSrvStub(); |
|
||||||
self.timeSrv = new TimeSrvStub(); |
|
||||||
self.datasourceSrv = {}; |
|
||||||
self.backendSrv = {}; |
|
||||||
self.$routeParams = {}; |
|
||||||
|
|
||||||
this.providePhase = function(mocks) { |
|
||||||
return window.module(function($provide) { |
|
||||||
_.each(mocks, function(key) { |
|
||||||
$provide.value(key, self[key]); |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.createService = function(name) { |
|
||||||
return window.inject(function($q, $rootScope, $httpBackend, $injector, $location, $timeout) { |
|
||||||
self.$q = $q; |
|
||||||
self.$rootScope = $rootScope; |
|
||||||
self.$httpBackend = $httpBackend; |
|
||||||
self.$location = $location; |
|
||||||
|
|
||||||
self.$rootScope.onAppEvent = function() {}; |
|
||||||
self.$rootScope.appEvent = function() {}; |
|
||||||
self.$timeout = $timeout; |
|
||||||
|
|
||||||
self.service = $injector.get(name); |
|
||||||
}); |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function DashboardViewStateStub() { |
|
||||||
this.registerPanel = function() { |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function TimeSrvStub() { |
|
||||||
this.init = sinon.spy(); |
|
||||||
this.time = { from:'now-1h', to: 'now'}; |
|
||||||
this.timeRange = function(parse) { |
|
||||||
if (parse === false) { |
|
||||||
return this.time; |
|
||||||
} |
|
||||||
return { |
|
||||||
from : dateMath.parse(this.time.from, false), |
|
||||||
to : dateMath.parse(this.time.to, true) |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
this.replace = function(target) { |
|
||||||
return target; |
|
||||||
}; |
|
||||||
|
|
||||||
this.setTime = function(time) { |
|
||||||
this.time = time; |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function ContextSrvStub() { |
|
||||||
this.hasRole = function() { |
|
||||||
return true; |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function TemplateSrvStub() { |
|
||||||
this.variables = []; |
|
||||||
this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g }; |
|
||||||
this.data = {}; |
|
||||||
this.replace = function(text) { |
|
||||||
return _.template(text, this.templateSettings)(this.data); |
|
||||||
}; |
|
||||||
this.init = function() {}; |
|
||||||
this.getAdhocFilters = function() { return []; }; |
|
||||||
this.fillVariableValuesForUrl = function() {}; |
|
||||||
this.updateTemplateData = function() { }; |
|
||||||
this.variableExists = function() { return false; }; |
|
||||||
this.variableInitialized = function() { }; |
|
||||||
this.highlightVariablesAsHtml = function(str) { return str; }; |
|
||||||
this.setGrafanaVariable = function(name, value) { |
|
||||||
this.data[name] = value; |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
ControllerTestContext: ControllerTestContext, |
|
||||||
TimeSrvStub: TimeSrvStub, |
|
||||||
ContextSrvStub: ContextSrvStub, |
|
||||||
ServiceTestContext: ServiceTestContext |
|
||||||
}; |
|
||||||
|
|
||||||
}); |
|
||||||
@ -0,0 +1,195 @@ |
|||||||
|
import _ from 'lodash'; |
||||||
|
import config from 'app/core/config'; |
||||||
|
import * as dateMath from 'app/core/utils/datemath'; |
||||||
|
import {angularMocks, sinon} from '../lib/common'; |
||||||
|
import {PanelModel} from 'app/features/dashboard/PanelModel'; |
||||||
|
|
||||||
|
export function ControllerTestContext() { |
||||||
|
var self = this; |
||||||
|
|
||||||
|
this.datasource = {}; |
||||||
|
this.$element = {}; |
||||||
|
this.annotationsSrv = {}; |
||||||
|
this.timeSrv = new TimeSrvStub(); |
||||||
|
this.templateSrv = new TemplateSrvStub(); |
||||||
|
this.datasourceSrv = { |
||||||
|
getMetricSources: function() {}, |
||||||
|
get: function() { |
||||||
|
return { |
||||||
|
then: function(callback) { |
||||||
|
callback(self.datasource); |
||||||
|
}, |
||||||
|
}; |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
this.providePhase = function(mocks) { |
||||||
|
return angularMocks.module(function($provide) { |
||||||
|
$provide.value('datasourceSrv', self.datasourceSrv); |
||||||
|
$provide.value('annotationsSrv', self.annotationsSrv); |
||||||
|
$provide.value('timeSrv', self.timeSrv); |
||||||
|
$provide.value('templateSrv', self.templateSrv); |
||||||
|
$provide.value('$element', self.$element); |
||||||
|
_.each(mocks, function(value, key) { |
||||||
|
$provide.value(key, value); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.createPanelController = function(Ctrl) { |
||||||
|
return angularMocks.inject(function($controller, $rootScope, $q, $location, $browser) { |
||||||
|
self.scope = $rootScope.$new(); |
||||||
|
self.$location = $location; |
||||||
|
self.$browser = $browser; |
||||||
|
self.$q = $q; |
||||||
|
self.panel = new PanelModel({type: 'test'}); |
||||||
|
self.dashboard = {meta: {}}; |
||||||
|
|
||||||
|
$rootScope.appEvent = sinon.spy(); |
||||||
|
$rootScope.onAppEvent = sinon.spy(); |
||||||
|
$rootScope.colors = []; |
||||||
|
|
||||||
|
for (var i = 0; i < 50; i++) { |
||||||
|
$rootScope.colors.push('#' + i); |
||||||
|
} |
||||||
|
|
||||||
|
config.panels['test'] = {info: {}}; |
||||||
|
self.ctrl = $controller( |
||||||
|
Ctrl, |
||||||
|
{$scope: self.scope}, |
||||||
|
{ |
||||||
|
panel: self.panel, |
||||||
|
dashboard: self.dashboard, |
||||||
|
}, |
||||||
|
); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.createControllerPhase = function(controllerName) { |
||||||
|
return angularMocks.inject(function($controller, $rootScope, $q, $location, $browser) { |
||||||
|
self.scope = $rootScope.$new(); |
||||||
|
self.$location = $location; |
||||||
|
self.$browser = $browser; |
||||||
|
self.scope.contextSrv = {}; |
||||||
|
self.scope.panel = {}; |
||||||
|
self.scope.dashboard = {meta: {}}; |
||||||
|
self.scope.dashboardMeta = {}; |
||||||
|
self.scope.dashboardViewState = new DashboardViewStateStub(); |
||||||
|
self.scope.appEvent = sinon.spy(); |
||||||
|
self.scope.onAppEvent = sinon.spy(); |
||||||
|
|
||||||
|
$rootScope.colors = []; |
||||||
|
for (var i = 0; i < 50; i++) { |
||||||
|
$rootScope.colors.push('#' + i); |
||||||
|
} |
||||||
|
|
||||||
|
self.$q = $q; |
||||||
|
self.scope.skipDataOnInit = true; |
||||||
|
self.scope.skipAutoInit = true; |
||||||
|
self.controller = $controller(controllerName, { |
||||||
|
$scope: self.scope, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function ServiceTestContext() { |
||||||
|
var self = this; |
||||||
|
self.templateSrv = new TemplateSrvStub(); |
||||||
|
self.timeSrv = new TimeSrvStub(); |
||||||
|
self.datasourceSrv = {}; |
||||||
|
self.backendSrv = {}; |
||||||
|
self.$routeParams = {}; |
||||||
|
|
||||||
|
this.providePhase = function(mocks) { |
||||||
|
return angularMocks.module(function($provide) { |
||||||
|
_.each(mocks, function(key) { |
||||||
|
$provide.value(key, self[key]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.createService = function(name) { |
||||||
|
return angularMocks.inject(function($q, $rootScope, $httpBackend, $injector, $location, $timeout) { |
||||||
|
self.$q = $q; |
||||||
|
self.$rootScope = $rootScope; |
||||||
|
self.$httpBackend = $httpBackend; |
||||||
|
self.$location = $location; |
||||||
|
|
||||||
|
self.$rootScope.onAppEvent = function() {}; |
||||||
|
self.$rootScope.appEvent = function() {}; |
||||||
|
self.$timeout = $timeout; |
||||||
|
|
||||||
|
self.service = $injector.get(name); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function DashboardViewStateStub() { |
||||||
|
this.registerPanel = function() {}; |
||||||
|
} |
||||||
|
|
||||||
|
export function TimeSrvStub() { |
||||||
|
this.init = sinon.spy(); |
||||||
|
this.time = {from: 'now-1h', to: 'now'}; |
||||||
|
this.timeRange = function(parse) { |
||||||
|
if (parse === false) { |
||||||
|
return this.time; |
||||||
|
} |
||||||
|
return { |
||||||
|
from: dateMath.parse(this.time.from, false), |
||||||
|
to: dateMath.parse(this.time.to, true), |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
this.replace = function(target) { |
||||||
|
return target; |
||||||
|
}; |
||||||
|
|
||||||
|
this.setTime = function(time) { |
||||||
|
this.time = time; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function ContextSrvStub() { |
||||||
|
this.hasRole = function() { |
||||||
|
return true; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function TemplateSrvStub() { |
||||||
|
this.variables = []; |
||||||
|
this.templateSettings = {interpolate: /\[\[([\s\S]+?)\]\]/g}; |
||||||
|
this.data = {}; |
||||||
|
this.replace = function(text) { |
||||||
|
return _.template(text, this.templateSettings)(this.data); |
||||||
|
}; |
||||||
|
this.init = function() {}; |
||||||
|
this.getAdhocFilters = function() { |
||||||
|
return []; |
||||||
|
}; |
||||||
|
this.fillVariableValuesForUrl = function() {}; |
||||||
|
this.updateTemplateData = function() {}; |
||||||
|
this.variableExists = function() { |
||||||
|
return false; |
||||||
|
}; |
||||||
|
this.variableInitialized = function() {}; |
||||||
|
this.highlightVariablesAsHtml = function(str) { |
||||||
|
return str; |
||||||
|
}; |
||||||
|
this.setGrafanaVariable = function(name, value) { |
||||||
|
this.data[name] = value; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
var allDeps = { |
||||||
|
ContextSrvStub: ContextSrvStub, |
||||||
|
TemplateSrvStub: TemplateSrvStub, |
||||||
|
TimeSrvStub: TimeSrvStub, |
||||||
|
ControllerTestContext: ControllerTestContext, |
||||||
|
ServiceTestContext: ServiceTestContext, |
||||||
|
DashboardViewStateStub: DashboardViewStateStub |
||||||
|
}; |
||||||
|
|
||||||
|
// for legacy
|
||||||
|
export default allDeps; |
||||||
@ -1,130 +0,0 @@ |
|||||||
(function() { |
|
||||||
"use strict"; |
|
||||||
|
|
||||||
// Tun on full stack traces in errors to help debugging
|
|
||||||
Error.stackTraceLimit=Infinity; |
|
||||||
|
|
||||||
window.__karma__.loaded = function() {}; |
|
||||||
|
|
||||||
System.config({ |
|
||||||
baseURL: '/base/', |
|
||||||
defaultJSExtensions: true, |
|
||||||
paths: { |
|
||||||
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js', |
|
||||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js', |
|
||||||
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js', |
|
||||||
'tether': 'vendor/npm/tether/dist/js/tether.js', |
|
||||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js', |
|
||||||
'moment': 'vendor/moment.js', |
|
||||||
"jquery": "vendor/jquery/dist/jquery.js", |
|
||||||
'lodash-src': 'vendor/lodash/dist/lodash.js', |
|
||||||
"lodash": 'app/core/lodash_extended.js', |
|
||||||
"angular": 'vendor/angular/angular.js', |
|
||||||
'angular-mocks': 'vendor/angular-mocks/angular-mocks.js', |
|
||||||
"bootstrap": "vendor/bootstrap/bootstrap.js", |
|
||||||
'angular-route': 'vendor/angular-route/angular-route.js', |
|
||||||
'angular-sanitize': 'vendor/angular-sanitize/angular-sanitize.js', |
|
||||||
"angular-ui": "vendor/angular-ui/ui-bootstrap-tpls.js", |
|
||||||
"angular-strap": "vendor/angular-other/angular-strap.js", |
|
||||||
"angular-dragdrop": "vendor/angular-native-dragdrop/draganddrop.js", |
|
||||||
"angular-bindonce": "vendor/angular-bindonce/bindonce.js", |
|
||||||
"spectrum": "vendor/spectrum.js", |
|
||||||
"bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js", |
|
||||||
"jquery.flot": "vendor/flot/jquery.flot", |
|
||||||
"jquery.flot.pie": "vendor/flot/jquery.flot.pie", |
|
||||||
"jquery.flot.selection": "vendor/flot/jquery.flot.selection", |
|
||||||
"jquery.flot.stack": "vendor/flot/jquery.flot.stack", |
|
||||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent", |
|
||||||
"jquery.flot.time": "vendor/flot/jquery.flot.time", |
|
||||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair", |
|
||||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow", |
|
||||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge", |
|
||||||
"d3": "vendor/d3/d3.js", |
|
||||||
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes", |
|
||||||
"twemoji": "vendor/npm/twemoji/2/twemoji.amd.js", |
|
||||||
"ace": "vendor/npm/ace-builds/src-noconflict/ace", |
|
||||||
}, |
|
||||||
|
|
||||||
packages: { |
|
||||||
app: { |
|
||||||
defaultExtension: 'js', |
|
||||||
}, |
|
||||||
vendor: { |
|
||||||
defaultExtension: 'js', |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
map: { |
|
||||||
}, |
|
||||||
|
|
||||||
meta: { |
|
||||||
'vendor/angular/angular.js': { |
|
||||||
format: 'global', |
|
||||||
deps: ['jquery'], |
|
||||||
exports: 'angular', |
|
||||||
}, |
|
||||||
'vendor/angular-mocks/angular-mocks.js': { |
|
||||||
format: 'global', |
|
||||||
deps: ['angular'], |
|
||||||
}, |
|
||||||
'vendor/npm/eventemitter3/index.js': { |
|
||||||
format: 'cjs', |
|
||||||
exports: 'EventEmitter' |
|
||||||
}, |
|
||||||
'vendor/npm/mousetrap/mousetrap.js': { |
|
||||||
format: 'global', |
|
||||||
exports: 'Mousetrap' |
|
||||||
}, |
|
||||||
'vendor/npm/ace-builds/src-noconflict/ace.js': { |
|
||||||
format: 'global', |
|
||||||
exports: 'ace' |
|
||||||
}, |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
function file2moduleName(filePath) { |
|
||||||
return filePath.replace(/\\/g, '/') |
|
||||||
.replace(/^\/base\//, '') |
|
||||||
.replace(/\.\w*$/, ''); |
|
||||||
} |
|
||||||
|
|
||||||
function onlySpecFiles(path) { |
|
||||||
return /specs.*/.test(path); |
|
||||||
} |
|
||||||
|
|
||||||
window.grafanaBootData = {settings: {}}; |
|
||||||
|
|
||||||
var modules = ['angular', 'angular-mocks', 'app/app']; |
|
||||||
var promises = modules.map(function(name) { |
|
||||||
return System.import(name); |
|
||||||
}); |
|
||||||
|
|
||||||
Promise.all(promises).then(function(deps) { |
|
||||||
var angular = deps[0]; |
|
||||||
|
|
||||||
angular.module('grafana', ['ngRoute']); |
|
||||||
angular.module('grafana.services', ['ngRoute', '$strap.directives']); |
|
||||||
angular.module('grafana.panels', []); |
|
||||||
angular.module('grafana.controllers', []); |
|
||||||
angular.module('grafana.directives', []); |
|
||||||
angular.module('grafana.filters', []); |
|
||||||
angular.module('grafana.routes', ['ngRoute']); |
|
||||||
|
|
||||||
// load specs
|
|
||||||
return Promise.all( |
|
||||||
Object.keys(window.__karma__.files) // All files served by Karma.
|
|
||||||
.filter(onlySpecFiles) |
|
||||||
.map(file2moduleName) |
|
||||||
.map(function(path) { |
|
||||||
// console.log(path);
|
|
||||||
return System.import(path); |
|
||||||
})); |
|
||||||
}).then(function() { |
|
||||||
window.__karma__.start(); |
|
||||||
}, function(error) { |
|
||||||
window.__karma__.error(error.stack || error); |
|
||||||
}).catch(function(error) { |
|
||||||
window.__karma__.error(error.stack || error); |
|
||||||
}); |
|
||||||
|
|
||||||
})(); |
|
||||||
Loading…
Reference in new issue