dashboard_model.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. import moment from 'moment';
  2. import _ from 'lodash';
  3. import {GRID_COLUMN_COUNT, GRID_CELL_HEIGHT, REPEAT_DIR_VERTICAL} from 'app/core/constants';
  4. import {DEFAULT_ANNOTATION_COLOR} from 'app/core/utils/colors';
  5. import {Emitter, contextSrv} from 'app/core/core';
  6. import sortByKeys from 'app/core/utils/sort_by_keys';
  7. import {DashboardRow} from './row/row_model';
  8. import {PanelModel} from './panel_model';
  9. export class DashboardModel {
  10. id: any;
  11. title: any;
  12. autoUpdate: any;
  13. description: any;
  14. tags: any;
  15. style: any;
  16. timezone: any;
  17. editable: any;
  18. graphTooltip: any;
  19. rows: DashboardRow[];
  20. time: any;
  21. timepicker: any;
  22. hideControls: any;
  23. templating: any;
  24. annotations: any;
  25. refresh: any;
  26. snapshot: any;
  27. schemaVersion: number;
  28. version: number;
  29. revision: number;
  30. links: any;
  31. gnetId: any;
  32. editMode: boolean;
  33. folderId: number;
  34. panels: PanelModel[];
  35. // ------------------
  36. // not persisted
  37. // ------------------
  38. // repeat process cycles
  39. iteration: number;
  40. meta: any;
  41. events: Emitter;
  42. static nonPersistedProperties: {[str: string]: boolean} = {
  43. events: true,
  44. meta: true,
  45. panels: true, // needs special handling
  46. templating: true, // needs special handling
  47. };
  48. constructor(data, meta?) {
  49. if (!data) {
  50. data = {};
  51. }
  52. this.events = new Emitter();
  53. this.id = data.id || null;
  54. this.revision = data.revision;
  55. this.title = data.title || 'No Title';
  56. this.autoUpdate = data.autoUpdate;
  57. this.description = data.description;
  58. this.tags = data.tags || [];
  59. this.style = data.style || 'dark';
  60. this.timezone = data.timezone || '';
  61. this.editable = data.editable !== false;
  62. this.graphTooltip = data.graphTooltip || 0;
  63. this.hideControls = data.hideControls || false;
  64. this.time = data.time || {from: 'now-6h', to: 'now'};
  65. this.timepicker = data.timepicker || {};
  66. this.templating = this.ensureListExist(data.templating);
  67. this.annotations = this.ensureListExist(data.annotations);
  68. this.refresh = data.refresh;
  69. this.snapshot = data.snapshot;
  70. this.schemaVersion = data.schemaVersion || 0;
  71. this.version = data.version || 0;
  72. this.links = data.links || [];
  73. this.gnetId = data.gnetId || null;
  74. this.folderId = data.folderId || null;
  75. this.panels = _.map(data.panels || [], panelData => new PanelModel(panelData));
  76. this.initMeta(meta);
  77. this.updateSchema(data);
  78. this.addBuiltInAnnotationQuery();
  79. this.sortPanelsByGridPos();
  80. }
  81. addBuiltInAnnotationQuery() {
  82. let found = false;
  83. for (let item of this.annotations.list) {
  84. if (item.builtIn === 1) {
  85. found = true;
  86. break;
  87. }
  88. }
  89. if (found) {
  90. return;
  91. }
  92. this.annotations.list.unshift({
  93. datasource: '-- Grafana --',
  94. name: 'Annotations & Alerts',
  95. type: 'dashboard',
  96. iconColor: DEFAULT_ANNOTATION_COLOR,
  97. enable: true,
  98. hide: true,
  99. builtIn: 1,
  100. });
  101. }
  102. private initMeta(meta) {
  103. meta = meta || {};
  104. meta.canShare = meta.canShare !== false;
  105. meta.canSave = meta.canSave !== false;
  106. meta.canStar = meta.canStar !== false;
  107. meta.canEdit = meta.canEdit !== false;
  108. if (!this.editable) {
  109. meta.canEdit = false;
  110. meta.canDelete = false;
  111. meta.canSave = false;
  112. }
  113. this.meta = meta;
  114. }
  115. // cleans meta data and other non peristent state
  116. getSaveModelClone() {
  117. // make clone
  118. var copy: any = {};
  119. for (var property in this) {
  120. if (DashboardModel.nonPersistedProperties[property] || !this.hasOwnProperty(property)) {
  121. continue;
  122. }
  123. copy[property] = _.cloneDeep(this[property]);
  124. }
  125. // get variable save models
  126. copy.templating = {
  127. list: _.map(this.templating.list, variable => (variable.getSaveModel ? variable.getSaveModel() : variable)),
  128. };
  129. // get panel save models
  130. copy.panels = _.map(this.panels, panel => panel.getSaveModel());
  131. // sort by keys
  132. copy = sortByKeys(copy);
  133. return copy;
  134. }
  135. setViewMode(panel: PanelModel, fullscreen: boolean, isEditing: boolean) {
  136. this.meta.fullscreen = fullscreen;
  137. this.meta.isEditing = isEditing && this.meta.canEdit;
  138. panel.setViewMode(fullscreen, this.meta.isEditing);
  139. this.events.emit('view-mode-changed', panel);
  140. }
  141. private ensureListExist(data) {
  142. if (!data) {
  143. data = {};
  144. }
  145. if (!data.list) {
  146. data.list = [];
  147. }
  148. return data;
  149. }
  150. getNextPanelId() {
  151. let max = 0;
  152. for (let panel of this.panels) {
  153. if (panel.id > max) {
  154. max = panel.id;
  155. }
  156. }
  157. return max + 1;
  158. }
  159. forEachPanel(callback) {
  160. for (let i = 0; i < this.panels.length; i++) {
  161. callback(this.panels[i], i);
  162. }
  163. }
  164. getPanelById(id) {
  165. for (let panel of this.panels) {
  166. if (panel.id === id) {
  167. return panel;
  168. }
  169. }
  170. return null;
  171. }
  172. addPanel(panelData) {
  173. panelData.id = this.getNextPanelId();
  174. let panel = new PanelModel(panelData);
  175. this.panels.unshift(panel);
  176. this.sortPanelsByGridPos();
  177. this.events.emit('panel-added', panel);
  178. }
  179. private sortPanelsByGridPos() {
  180. this.panels.sort(function(panelA, panelB) {
  181. if (panelA.gridPos.y === panelB.gridPos.y) {
  182. return panelA.gridPos.x - panelB.gridPos.x;
  183. } else {
  184. return panelA.gridPos.y - panelB.gridPos.y;
  185. }
  186. });
  187. }
  188. cleanUpRepeats() {
  189. this.processRepeats(true);
  190. }
  191. processRepeats(cleanUpOnly?: boolean) {
  192. if (this.snapshot || this.templating.list.length === 0) {
  193. return;
  194. }
  195. this.iteration = (this.iteration || new Date().getTime()) + 1;
  196. let panelsToRemove = [];
  197. // cleanup scopedVars
  198. for (let panel of this.panels) {
  199. delete panel.scopedVars;
  200. }
  201. for (let panel of this.panels) {
  202. if (panel.repeat) {
  203. if (!cleanUpOnly) {
  204. this.repeatPanel(panel);
  205. }
  206. } else if (panel.repeatPanelId && panel.repeatIteration !== this.iteration) {
  207. panelsToRemove.push(panel);
  208. }
  209. }
  210. // remove panels
  211. _.pull(this.panels, ...panelsToRemove);
  212. this.sortPanelsByGridPos();
  213. this.events.emit('repeats-processed');
  214. }
  215. getRepeatClone(sourcePanel, index) {
  216. // if first clone return source
  217. if (index === 0) {
  218. return sourcePanel;
  219. }
  220. var clone = new PanelModel(sourcePanel.getSaveModel());
  221. clone.id = this.getNextPanelId();
  222. this.panels.push(clone);
  223. clone.repeatIteration = this.iteration;
  224. clone.repeatPanelId = sourcePanel.id;
  225. clone.repeat = null;
  226. return clone;
  227. }
  228. repeatPanel(panel: PanelModel) {
  229. var variable = _.find(this.templating.list, {name: panel.repeat});
  230. if (!variable) {
  231. return;
  232. }
  233. var selected;
  234. if (variable.current.text === 'All') {
  235. selected = variable.options.slice(1, variable.options.length);
  236. } else {
  237. selected = _.filter(variable.options, {selected: true});
  238. }
  239. let minWidth = panel.minSpan || 6;
  240. let xIndex = 0;
  241. for (let index = 0; index < selected.length; index++) {
  242. var option = selected[index];
  243. var copy = this.getRepeatClone(panel, index);
  244. copy.scopedVars = {};
  245. copy.scopedVars[variable.name] = option;
  246. if (panel.repeatDirection === REPEAT_DIR_VERTICAL) {
  247. if (index === 0) {
  248. continue;
  249. }
  250. copy.gridPos.y = panel.gridPos.y + panel.gridPos.h * index;
  251. } else {
  252. // set width based on how many are selected
  253. // assumed the repeated panels should take up full row width
  254. copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selected.length, minWidth);
  255. copy.gridPos.x = copy.gridPos.w * xIndex;
  256. // handle overflow by pushing down one row
  257. if (copy.gridPos.x + copy.gridPos.w > GRID_COLUMN_COUNT) {
  258. copy.gridPos.x = 0;
  259. xIndex = 0;
  260. } else {
  261. xIndex += 1;
  262. }
  263. }
  264. }
  265. }
  266. removePanel(panel: PanelModel) {
  267. var index = _.indexOf(this.panels, panel);
  268. this.panels.splice(index, 1);
  269. this.events.emit('panel-removed', panel);
  270. }
  271. setPanelFocus(id) {
  272. this.meta.focusPanelId = id;
  273. }
  274. updateSubmenuVisibility() {
  275. this.meta.submenuEnabled = (() => {
  276. if (this.links.length > 0) {
  277. return true;
  278. }
  279. var visibleVars = _.filter(this.templating.list, variable => variable.hide !== 2);
  280. if (visibleVars.length > 0) {
  281. return true;
  282. }
  283. var visibleAnnotations = _.filter(this.annotations.list, annotation => annotation.hide !== true);
  284. if (visibleAnnotations.length > 0) {
  285. return true;
  286. }
  287. return false;
  288. })();
  289. }
  290. getPanelInfoById(panelId) {
  291. for (let i = 0; i < this.panels.length; i++) {
  292. if (this.panels[i].id === panelId) {
  293. return {
  294. panel: this.panels[i],
  295. index: i,
  296. };
  297. }
  298. }
  299. return null;
  300. }
  301. duplicatePanel(panel) {
  302. const newPanel = panel.getSaveModel();
  303. newPanel.id = this.getNextPanelId();
  304. delete newPanel.repeat;
  305. delete newPanel.repeatIteration;
  306. delete newPanel.repeatPanelId;
  307. delete newPanel.scopedVars;
  308. if (newPanel.alert) {
  309. delete newPanel.thresholds;
  310. }
  311. delete newPanel.alert;
  312. // does it fit to the right?
  313. if (panel.gridPos.x + panel.gridPos.w * 2 <= GRID_COLUMN_COUNT) {
  314. newPanel.gridPos.x += panel.gridPos.w;
  315. } else {
  316. // add bellow
  317. newPanel.gridPos.y += panel.gridPos.h;
  318. }
  319. this.addPanel(newPanel);
  320. return newPanel;
  321. }
  322. formatDate(date, format?) {
  323. date = moment.isMoment(date) ? date : moment(date);
  324. format = format || 'YYYY-MM-DD HH:mm:ss';
  325. let timezone = this.getTimezone();
  326. return timezone === 'browser' ? moment(date).format(format) : moment.utc(date).format(format);
  327. }
  328. destroy() {
  329. this.events.removeAllListeners();
  330. for (let panel of this.panels) {
  331. panel.destroy();
  332. }
  333. }
  334. toggleRow(row: PanelModel) {
  335. let rowIndex = _.indexOf(this.panels, row);
  336. if (row.collapsed) {
  337. row.collapsed = false;
  338. if (row.panels.length > 0) {
  339. // Use first panel to figure out if it was moved or pushed
  340. let firstPanel = row.panels[0];
  341. let yDiff = firstPanel.gridPos.y - (row.gridPos.y + row.gridPos.h);
  342. // start inserting after row
  343. let insertPos = rowIndex+1;
  344. for (let panel of row.panels) {
  345. // make sure y is adjusted (in case row moved while collapsed)
  346. panel.gridPos.y -= yDiff;
  347. // insert after row
  348. this.panels.splice(insertPos, 0, new PanelModel(panel));
  349. insertPos += 1;
  350. }
  351. row.panels = [];
  352. }
  353. // sort panels
  354. this.sortPanelsByGridPos();
  355. // emit change event
  356. this.events.emit('row-expanded');
  357. return;
  358. }
  359. let rowPanels = [];
  360. for (let index = rowIndex+1; index < this.panels.length; index++) {
  361. let panel = this.panels[index];
  362. // break when encountering another row
  363. if (panel.type === 'row') {
  364. break;
  365. }
  366. // this panel must belong to row
  367. rowPanels.push(panel);
  368. }
  369. // remove panels
  370. _.pull(this.panels, ...rowPanels);
  371. // save panel models inside row panel
  372. row.panels = _.map(rowPanels, panel => panel.getSaveModel());
  373. row.collapsed = true;
  374. // emit change event
  375. this.events.emit('row-collapsed');
  376. }
  377. on(eventName, callback) {
  378. this.events.on(eventName, callback);
  379. }
  380. off(eventName, callback?) {
  381. this.events.off(eventName, callback);
  382. }
  383. cycleGraphTooltip() {
  384. this.graphTooltip = (this.graphTooltip + 1) % 3;
  385. }
  386. sharedTooltipModeEnabled() {
  387. return this.graphTooltip > 0;
  388. }
  389. sharedCrosshairModeOnly() {
  390. return this.graphTooltip === 1;
  391. }
  392. getRelativeTime(date) {
  393. date = moment.isMoment(date) ? date : moment(date);
  394. return this.timezone === 'browser' ? moment(date).fromNow() : moment.utc(date).fromNow();
  395. }
  396. getNextQueryLetter(panel) {
  397. var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  398. return _.find(letters, function(refId) {
  399. return _.every(panel.targets, function(other) {
  400. return other.refId !== refId;
  401. });
  402. });
  403. }
  404. isTimezoneUtc() {
  405. return this.getTimezone() === 'utc';
  406. }
  407. getTimezone() {
  408. return this.timezone ? this.timezone : contextSrv.user.timezone;
  409. }
  410. private updateSchema(old) {
  411. var i, j, k;
  412. var oldVersion = this.schemaVersion;
  413. var panelUpgrades = [];
  414. this.schemaVersion = 16;
  415. if (oldVersion === this.schemaVersion) {
  416. return;
  417. }
  418. // version 2 schema changes
  419. if (oldVersion < 2) {
  420. if (old.services) {
  421. if (old.services.filter) {
  422. this.time = old.services.filter.time;
  423. this.templating.list = old.services.filter.list || [];
  424. }
  425. }
  426. panelUpgrades.push(function(panel) {
  427. // rename panel type
  428. if (panel.type === 'graphite') {
  429. panel.type = 'graph';
  430. }
  431. if (panel.type !== 'graph') {
  432. return;
  433. }
  434. if (_.isBoolean(panel.legend)) {
  435. panel.legend = {show: panel.legend};
  436. }
  437. if (panel.grid) {
  438. if (panel.grid.min) {
  439. panel.grid.leftMin = panel.grid.min;
  440. delete panel.grid.min;
  441. }
  442. if (panel.grid.max) {
  443. panel.grid.leftMax = panel.grid.max;
  444. delete panel.grid.max;
  445. }
  446. }
  447. if (panel.y_format) {
  448. panel.y_formats[0] = panel.y_format;
  449. delete panel.y_format;
  450. }
  451. if (panel.y2_format) {
  452. panel.y_formats[1] = panel.y2_format;
  453. delete panel.y2_format;
  454. }
  455. });
  456. }
  457. // schema version 3 changes
  458. if (oldVersion < 3) {
  459. // ensure panel ids
  460. var maxId = this.getNextPanelId();
  461. panelUpgrades.push(function(panel) {
  462. if (!panel.id) {
  463. panel.id = maxId;
  464. maxId += 1;
  465. }
  466. });
  467. }
  468. // schema version 4 changes
  469. if (oldVersion < 4) {
  470. // move aliasYAxis changes
  471. panelUpgrades.push(function(panel) {
  472. if (panel.type !== 'graph') {
  473. return;
  474. }
  475. _.each(panel.aliasYAxis, function(value, key) {
  476. panel.seriesOverrides = [{alias: key, yaxis: value}];
  477. });
  478. delete panel.aliasYAxis;
  479. });
  480. }
  481. if (oldVersion < 6) {
  482. // move pulldowns to new schema
  483. var annotations = _.find(old.pulldowns, {type: 'annotations'});
  484. if (annotations) {
  485. this.annotations = {
  486. list: annotations.annotations || [],
  487. };
  488. }
  489. // update template variables
  490. for (i = 0; i < this.templating.list.length; i++) {
  491. var variable = this.templating.list[i];
  492. if (variable.datasource === void 0) {
  493. variable.datasource = null;
  494. }
  495. if (variable.type === 'filter') {
  496. variable.type = 'query';
  497. }
  498. if (variable.type === void 0) {
  499. variable.type = 'query';
  500. }
  501. if (variable.allFormat === void 0) {
  502. variable.allFormat = 'glob';
  503. }
  504. }
  505. }
  506. if (oldVersion < 7) {
  507. if (old.nav && old.nav.length) {
  508. this.timepicker = old.nav[0];
  509. }
  510. // ensure query refIds
  511. panelUpgrades.push(function(panel) {
  512. _.each(
  513. panel.targets,
  514. function(target) {
  515. if (!target.refId) {
  516. target.refId = this.getNextQueryLetter(panel);
  517. }
  518. }.bind(this),
  519. );
  520. });
  521. }
  522. if (oldVersion < 8) {
  523. panelUpgrades.push(function(panel) {
  524. _.each(panel.targets, function(target) {
  525. // update old influxdb query schema
  526. if (target.fields && target.tags && target.groupBy) {
  527. if (target.rawQuery) {
  528. delete target.fields;
  529. delete target.fill;
  530. } else {
  531. target.select = _.map(target.fields, function(field) {
  532. var parts = [];
  533. parts.push({type: 'field', params: [field.name]});
  534. parts.push({type: field.func, params: []});
  535. if (field.mathExpr) {
  536. parts.push({type: 'math', params: [field.mathExpr]});
  537. }
  538. if (field.asExpr) {
  539. parts.push({type: 'alias', params: [field.asExpr]});
  540. }
  541. return parts;
  542. });
  543. delete target.fields;
  544. _.each(target.groupBy, function(part) {
  545. if (part.type === 'time' && part.interval) {
  546. part.params = [part.interval];
  547. delete part.interval;
  548. }
  549. if (part.type === 'tag' && part.key) {
  550. part.params = [part.key];
  551. delete part.key;
  552. }
  553. });
  554. if (target.fill) {
  555. target.groupBy.push({type: 'fill', params: [target.fill]});
  556. delete target.fill;
  557. }
  558. }
  559. }
  560. });
  561. });
  562. }
  563. // schema version 9 changes
  564. if (oldVersion < 9) {
  565. // move aliasYAxis changes
  566. panelUpgrades.push(function(panel) {
  567. if (panel.type !== 'singlestat' && panel.thresholds !== '') {
  568. return;
  569. }
  570. if (panel.thresholds) {
  571. var k = panel.thresholds.split(',');
  572. if (k.length >= 3) {
  573. k.shift();
  574. panel.thresholds = k.join(',');
  575. }
  576. }
  577. });
  578. }
  579. // schema version 10 changes
  580. if (oldVersion < 10) {
  581. // move aliasYAxis changes
  582. panelUpgrades.push(function(panel) {
  583. if (panel.type !== 'table') {
  584. return;
  585. }
  586. _.each(panel.styles, function(style) {
  587. if (style.thresholds && style.thresholds.length >= 3) {
  588. var k = style.thresholds;
  589. k.shift();
  590. style.thresholds = k;
  591. }
  592. });
  593. });
  594. }
  595. if (oldVersion < 12) {
  596. // update template variables
  597. _.each(this.templating.list, function(templateVariable) {
  598. if (templateVariable.refresh) {
  599. templateVariable.refresh = 1;
  600. }
  601. if (!templateVariable.refresh) {
  602. templateVariable.refresh = 0;
  603. }
  604. if (templateVariable.hideVariable) {
  605. templateVariable.hide = 2;
  606. } else if (templateVariable.hideLabel) {
  607. templateVariable.hide = 1;
  608. }
  609. });
  610. }
  611. if (oldVersion < 12) {
  612. // update graph yaxes changes
  613. panelUpgrades.push(function(panel) {
  614. if (panel.type !== 'graph') {
  615. return;
  616. }
  617. if (!panel.grid) {
  618. return;
  619. }
  620. if (!panel.yaxes) {
  621. panel.yaxes = [
  622. {
  623. show: panel['y-axis'],
  624. min: panel.grid.leftMin,
  625. max: panel.grid.leftMax,
  626. logBase: panel.grid.leftLogBase,
  627. format: panel.y_formats[0],
  628. label: panel.leftYAxisLabel,
  629. },
  630. {
  631. show: panel['y-axis'],
  632. min: panel.grid.rightMin,
  633. max: panel.grid.rightMax,
  634. logBase: panel.grid.rightLogBase,
  635. format: panel.y_formats[1],
  636. label: panel.rightYAxisLabel,
  637. },
  638. ];
  639. panel.xaxis = {
  640. show: panel['x-axis'],
  641. };
  642. delete panel.grid.leftMin;
  643. delete panel.grid.leftMax;
  644. delete panel.grid.leftLogBase;
  645. delete panel.grid.rightMin;
  646. delete panel.grid.rightMax;
  647. delete panel.grid.rightLogBase;
  648. delete panel.y_formats;
  649. delete panel.leftYAxisLabel;
  650. delete panel.rightYAxisLabel;
  651. delete panel['y-axis'];
  652. delete panel['x-axis'];
  653. }
  654. });
  655. }
  656. if (oldVersion < 13) {
  657. // update graph yaxes changes
  658. panelUpgrades.push(function(panel) {
  659. if (panel.type !== 'graph') {
  660. return;
  661. }
  662. if (!panel.grid) {
  663. return;
  664. }
  665. panel.thresholds = [];
  666. var t1: any = {},
  667. t2: any = {};
  668. if (panel.grid.threshold1 !== null) {
  669. t1.value = panel.grid.threshold1;
  670. if (panel.grid.thresholdLine) {
  671. t1.line = true;
  672. t1.lineColor = panel.grid.threshold1Color;
  673. t1.colorMode = 'custom';
  674. } else {
  675. t1.fill = true;
  676. t1.fillColor = panel.grid.threshold1Color;
  677. t1.colorMode = 'custom';
  678. }
  679. }
  680. if (panel.grid.threshold2 !== null) {
  681. t2.value = panel.grid.threshold2;
  682. if (panel.grid.thresholdLine) {
  683. t2.line = true;
  684. t2.lineColor = panel.grid.threshold2Color;
  685. t2.colorMode = 'custom';
  686. } else {
  687. t2.fill = true;
  688. t2.fillColor = panel.grid.threshold2Color;
  689. t2.colorMode = 'custom';
  690. }
  691. }
  692. if (_.isNumber(t1.value)) {
  693. if (_.isNumber(t2.value)) {
  694. if (t1.value > t2.value) {
  695. t1.op = t2.op = 'lt';
  696. panel.thresholds.push(t1);
  697. panel.thresholds.push(t2);
  698. } else {
  699. t1.op = t2.op = 'gt';
  700. panel.thresholds.push(t1);
  701. panel.thresholds.push(t2);
  702. }
  703. } else {
  704. t1.op = 'gt';
  705. panel.thresholds.push(t1);
  706. }
  707. }
  708. delete panel.grid.threshold1;
  709. delete panel.grid.threshold1Color;
  710. delete panel.grid.threshold2;
  711. delete panel.grid.threshold2Color;
  712. delete panel.grid.thresholdLine;
  713. });
  714. }
  715. if (oldVersion < 14) {
  716. this.graphTooltip = old.sharedCrosshair ? 1 : 0;
  717. }
  718. if (oldVersion < 16) {
  719. this.upgradeToGridLayout(old);
  720. }
  721. if (panelUpgrades.length === 0) {
  722. return;
  723. }
  724. for (j = 0; j < this.panels.length; j++) {
  725. for (k = 0; k < panelUpgrades.length; k++) {
  726. panelUpgrades[k].call(this, this.panels[j]);
  727. }
  728. }
  729. }
  730. upgradeToGridLayout(old) {
  731. let yPos = 0;
  732. let widthFactor = GRID_COLUMN_COUNT / 12;
  733. //let rowIds = 1000;
  734. //
  735. if (!old.rows) {
  736. return;
  737. }
  738. for (let row of old.rows) {
  739. let xPos = 0;
  740. let height: any = row.height || 250;
  741. // if (this.meta.keepRows) {
  742. // this.panels.push({
  743. // id: rowIds++,
  744. // type: 'row',
  745. // title: row.title,
  746. // x: 0,
  747. // y: yPos,
  748. // height: 1,
  749. // width: 12
  750. // });
  751. //
  752. // yPos += 1;
  753. // }
  754. if (_.isString(height)) {
  755. height = parseInt(height.replace('px', ''), 10);
  756. }
  757. const rowGridHeight = Math.ceil(height / GRID_CELL_HEIGHT);
  758. for (let panel of row.panels) {
  759. const panelWidth = Math.floor(panel.span) * widthFactor;
  760. // should wrap to next row?
  761. if (xPos + panelWidth >= GRID_COLUMN_COUNT) {
  762. yPos += rowGridHeight;
  763. }
  764. panel.gridPos = {x: xPos, y: yPos, w: panelWidth, h: rowGridHeight};
  765. delete panel.span;
  766. xPos += panel.gridPos.w;
  767. this.panels.push(new PanelModel(panel));
  768. }
  769. yPos += rowGridHeight;
  770. }
  771. }
  772. }