import config from 'app/core/config'; import _ from 'lodash'; import $ from 'jquery'; import { appEvents, profiler } from 'app/core/core'; import { PanelModel } from 'app/features/dashboard/panel_model'; import Remarkable from 'remarkable'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants'; import store from 'app/core/store'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; import { Emitter } from 'app/core/core'; export class PanelCtrl { panel: any; error: any; dashboard: any; editorTabIndex: number; pluginName: string; pluginId: string; editorTabs: any; $scope: any; $injector: any; $location: any; $timeout: any; inspector: any; editModeInitiated: boolean; height: any; containerHeight: any; events: Emitter; timing: any; loading: boolean; constructor($scope, $injector) { this.$injector = $injector; this.$location = $injector.get('$location'); this.$scope = $scope; this.$timeout = $injector.get('$timeout'); this.editorTabIndex = 0; this.events = this.panel.events; this.timing = {}; const plugin = config.panels[this.panel.type]; if (plugin) { this.pluginId = plugin.id; this.pluginName = plugin.name; } $scope.$on('component-did-mount', () => this.panelDidMount()); } panelDidMount() { this.events.emit('component-did-mount'); this.dashboard.panelInitialized(this.panel); } renderingCompleted() { profiler.renderingCompleted(this.panel.id, this.timing); } refresh() { this.panel.refresh(); } publishAppEvent(evtName, evt) { this.$scope.$root.appEvent(evtName, evt); } changeView(fullscreen, edit) { this.publishAppEvent('panel-change-view', { fullscreen: fullscreen, edit: edit, panelId: this.panel.id, }); } viewPanel() { this.changeView(true, false); } editPanel() { this.changeView(true, true); } exitFullscreen() { this.changeView(false, false); } initEditMode() { this.editorTabs = []; this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); this.editModeInitiated = true; this.events.emit('init-edit-mode', null); const urlTab = (this.$injector.get('$routeParams').tab || '').toLowerCase(); if (urlTab) { this.editorTabs.forEach((tab, i) => { if (tab.title.toLowerCase() === urlTab) { this.editorTabIndex = i; } }); } } changeTab(newIndex) { this.editorTabIndex = newIndex; const route = this.$injector.get('$route'); route.current.params.tab = this.editorTabs[newIndex].title.toLowerCase(); route.updateParams(); } addEditorTab(title, directiveFn, index?, icon?) { const editorTab = { title, directiveFn, icon }; if (_.isString(directiveFn)) { editorTab.directiveFn = () => { return { templateUrl: directiveFn }; }; } if (index) { this.editorTabs.splice(index, 0, editorTab); } else { this.editorTabs.push(editorTab); } } getMenu() { const menu = []; menu.push({ text: 'View', click: 'ctrl.viewPanel();', icon: 'fa fa-fw fa-eye', shortcut: 'v', }); if (this.dashboard.meta.canEdit) { menu.push({ text: 'Edit', click: 'ctrl.editPanel();', role: 'Editor', icon: 'fa fa-fw fa-edit', shortcut: 'e', }); } menu.push({ text: 'Share', click: 'ctrl.sharePanel();', icon: 'fa fa-fw fa-share', shortcut: 'p s', }); // Additional items from sub-class menu.push(...this.getAdditionalMenuItems()); const extendedMenu = this.getExtendedMenu(); menu.push({ text: 'More ...', click: '', icon: 'fa fa-fw fa-cube', submenu: extendedMenu, }); if (this.dashboard.meta.canEdit) { menu.push({ divider: true, role: 'Editor' }); menu.push({ text: 'Remove', click: 'ctrl.removePanel();', role: 'Editor', icon: 'fa fa-fw fa-trash', shortcut: 'p r', }); } return menu; } getExtendedMenu() { const menu = []; if (!this.panel.fullscreen && this.dashboard.meta.canEdit) { menu.push({ text: 'Duplicate', click: 'ctrl.duplicate()', role: 'Editor', shortcut: 'p d', }); menu.push({ text: 'Copy', click: 'ctrl.copyPanel()', role: 'Editor', }); } menu.push({ text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();', }); this.events.emit('init-panel-actions', menu); return menu; } // Override in sub-class to add items before extended menu getAdditionalMenuItems() { return []; } otherPanelInFullscreenMode() { return this.dashboard.meta.fullscreen && !this.panel.fullscreen; } calculatePanelHeight() { if (this.panel.fullscreen) { const docHeight = $('.react-grid-layout').height(); const editHeight = Math.floor(docHeight * 0.35); const fullscreenHeight = Math.floor(docHeight * 0.8); this.containerHeight = this.panel.isEditing ? editHeight : fullscreenHeight; } else { this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN; } if (this.panel.soloMode) { this.containerHeight = $(window).height(); } // hacky solution if (this.panel.isEditing && !this.editModeInitiated) { this.initEditMode(); } this.height = this.containerHeight - (PANEL_BORDER + TITLE_HEIGHT); } render(payload?) { this.timing.renderStart = new Date().getTime(); this.events.emit('render', payload); } duplicate() { this.dashboard.duplicatePanel(this.panel); } removePanel() { this.publishAppEvent('panel-remove', { panelId: this.panel.id, }); } editPanelJson() { const editScope = this.$scope.$root.$new(); editScope.object = this.panel.getSaveModel(); editScope.updateHandler = this.replacePanel.bind(this); editScope.enableCopy = true; this.publishAppEvent('show-modal', { src: 'public/app/partials/edit_json.html', scope: editScope, }); } copyPanel() { store.set(LS_PANEL_COPY_KEY, JSON.stringify(this.panel.getSaveModel())); appEvents.emit('alert-success', ['Panel copied. Open Add Panel to paste']); } replacePanel(newPanel, oldPanel) { const dashboard = this.dashboard; const index = _.findIndex(dashboard.panels, panel => { return panel.id === oldPanel.id; }); const deletedPanel = dashboard.panels.splice(index, 1); this.dashboard.events.emit('panel-removed', deletedPanel); newPanel = new PanelModel(newPanel); newPanel.id = oldPanel.id; dashboard.panels.splice(index, 0, newPanel); dashboard.sortPanelsByGridPos(); dashboard.events.emit('panel-added', newPanel); } sharePanel() { const shareScope = this.$scope.$new(); shareScope.panel = this.panel; shareScope.dashboard = this.dashboard; this.publishAppEvent('show-modal', { src: 'public/app/features/dashboard/partials/shareModal.html', scope: shareScope, }); } getInfoMode() { if (this.error) { return 'error'; } if (!!this.panel.description) { return 'info'; } if (this.panel.links && this.panel.links.length) { return 'links'; } return ''; } getInfoContent(options) { let markdown = this.panel.description; if (options.mode === 'tooltip') { markdown = this.error || this.panel.description; } const linkSrv = this.$injector.get('linkSrv'); const sanitize = this.$injector.get('$sanitize'); const templateSrv = this.$injector.get('templateSrv'); const interpolatedMarkdown = templateSrv.replace(markdown, this.panel.scopedVars); let html = '
'; html += new Remarkable().render(interpolatedMarkdown); if (this.panel.links && this.panel.links.length > 0) { html += ''; } html += '
'; return sanitize(html); } openInspector() { const modalScope = this.$scope.$new(); modalScope.panel = this.panel; modalScope.dashboard = this.dashboard; modalScope.panelInfoHtml = this.getInfoContent({ mode: 'inspector' }); modalScope.inspector = $.extend(true, {}, this.inspector); this.publishAppEvent('show-modal', { src: 'public/app/features/dashboard/partials/inspector.html', scope: modalScope, }); } }