Merge pull request #247 from CatalystCode/ibex-version-1.0
Ibex version 1.0
This commit is contained in:
Коммит
a2222b503a
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"main.css": "static/css/main.43e63d1e.css",
|
"main.css": "static/css/main.43e63d1e.css",
|
||||||
"main.css.map": "static/css/main.43e63d1e.css.map",
|
"main.css.map": "static/css/main.43e63d1e.css.map",
|
||||||
"main.js": "static/js/main.7f2eae21.js",
|
"main.js": "static/js/main.203f58e7.js",
|
||||||
"main.js.map": "static/js/main.7f2eae21.js.map"
|
"main.js.map": "static/js/main.203f58e7.js.map"
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="shortcut icon" href="/favicon.ico"><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"/><title>React App</title><link href="/static/css/main.43e63d1e.css" rel="stylesheet"></head><body><div id="root"></div><script type="text/javascript" src="/static/js/main.7f2eae21.js"></script></body></html>
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="shortcut icon" href="/favicon.ico"><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"/><title>React App</title><link href="/static/css/main.43e63d1e.css" rel="stylesheet"></head><body><div id="root"></div><script type="text/javascript" src="/static/js/main.203f58e7.js"></script></body></html>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,5 +1,6 @@
|
||||||
import alt, { AbstractActions } from '../alt';
|
import alt, { AbstractActions } from '../alt';
|
||||||
import * as request from 'xhr-request';
|
import * as request from 'xhr-request';
|
||||||
|
import utils from '../utils';
|
||||||
|
|
||||||
interface IConfigurationsActions {
|
interface IConfigurationsActions {
|
||||||
loadConfiguration(): any;
|
loadConfiguration(): any;
|
||||||
|
@ -85,7 +86,7 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
createDashboard(dashboard: IDashboardConfig) {
|
createDashboard(dashboard: IDashboardConfig) {
|
||||||
return (dispatcher: (dashboard: IDashboardConfig) => void) => {
|
return (dispatcher: (dashboard: IDashboardConfig) => void) => {
|
||||||
|
|
||||||
let script = this.objectToString(dashboard);
|
let script = utils.objectToString(dashboard);
|
||||||
request('/api/dashboards/' + dashboard.id, {
|
request('/api/dashboards/' + dashboard.id, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
json: true,
|
json: true,
|
||||||
|
@ -122,7 +123,7 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
saveAsTemplate(template: IDashboardConfig) {
|
saveAsTemplate(template: IDashboardConfig) {
|
||||||
|
|
||||||
return (dispatcher: (result: { template: IDashboardConfig }) => void) => {
|
return (dispatcher: (result: { template: IDashboardConfig }) => void) => {
|
||||||
let script = this.objectToString(template);
|
let script = utils.objectToString(template);
|
||||||
|
|
||||||
script = '/// <reference path="../../../client/@types/types.d.ts"/>\n' +
|
script = '/// <reference path="../../../client/@types/types.d.ts"/>\n' +
|
||||||
'import * as _ from \'lodash\';\n\n' +
|
'import * as _ from \'lodash\';\n\n' +
|
||||||
|
@ -149,7 +150,7 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
saveConfiguration(dashboard: IDashboardConfig) {
|
saveConfiguration(dashboard: IDashboardConfig) {
|
||||||
return (dispatcher: (dashboard: IDashboardConfig) => void) => {
|
return (dispatcher: (dashboard: IDashboardConfig) => void) => {
|
||||||
|
|
||||||
let stringDashboard = this.objectToString(dashboard);
|
let stringDashboard = utils.objectToString(dashboard);
|
||||||
|
|
||||||
request('/api/dashboards/' + dashboard.id, {
|
request('/api/dashboards/' + dashboard.id, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -171,10 +172,6 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
convertDashboardToString(dashboard: IDashboardConfig) {
|
|
||||||
return this.objectToString(dashboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
failure(error: any) {
|
failure(error: any) {
|
||||||
return { error };
|
return { error };
|
||||||
}
|
}
|
||||||
|
@ -196,6 +193,10 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertDashboardToString(dashboard: IDashboardConfig) {
|
||||||
|
return utils.convertDashboardToString(dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
private getScript(source: string, callback?: () => void): boolean {
|
private getScript(source: string, callback?: () => void): boolean {
|
||||||
let script: any = document.createElement('script');
|
let script: any = document.createElement('script');
|
||||||
let prior = document.getElementsByTagName('script')[0];
|
let prior = document.getElementsByTagName('script')[0];
|
||||||
|
@ -219,141 +220,6 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
|
||||||
script.src = source;
|
script.src = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convret a json object with functions to string
|
|
||||||
* @param obj an object with functions to convert to string
|
|
||||||
*/
|
|
||||||
private objectToString(obj: Object, indent: number = 0, lf: boolean = false): string {
|
|
||||||
|
|
||||||
let result = ''; // (lf ? '\n' : '') + '\t'.repeat(indent);
|
|
||||||
let sind = '\t'.repeat(indent);
|
|
||||||
let objectType = (Array.isArray(obj) && 'array') || typeof obj;
|
|
||||||
|
|
||||||
switch (objectType) {
|
|
||||||
case 'object': {
|
|
||||||
|
|
||||||
if (obj === null) { return result = 'null'; }
|
|
||||||
|
|
||||||
// Iterating through all values in object
|
|
||||||
let objectValue = '';
|
|
||||||
let objectValues = [];
|
|
||||||
let valuesStringLength = 0;
|
|
||||||
Object.keys(obj).forEach((key: string, idx: number) => {
|
|
||||||
|
|
||||||
let value = this.objectToString(obj[key], indent + 1, true);
|
|
||||||
|
|
||||||
// if key contains '.' or '-'
|
|
||||||
let skey = key.search(/\.|\-/g) >= 0 ? `"${key}"` : `${key}`;
|
|
||||||
let mapping = `${skey}: ${value}`;
|
|
||||||
valuesStringLength += mapping.length;
|
|
||||||
|
|
||||||
objectValues.push(mapping);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (valuesStringLength <= 120) {
|
|
||||||
result += `{ ${objectValues.join()} }`;
|
|
||||||
} else {
|
|
||||||
result += `{\n${sind}\t${objectValues.join(',\n' + sind + '\t')}\n${sind}}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
let stringValue = obj.toString();
|
|
||||||
let htmlString = stringValue.replace(/^\s+|\s+$/g, ''); // trim any leading and trailing whitespace
|
|
||||||
if ( htmlString.startsWith('<') && htmlString.endsWith('>') ) {
|
|
||||||
result += '`' + htmlString + '`'; // html needs to be wrapped in back ticks
|
|
||||||
} else {
|
|
||||||
stringValue = stringValue.replace(/\"/g, '\\"');
|
|
||||||
result += `"${stringValue}"`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'function': {
|
|
||||||
result += obj.toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'number':
|
|
||||||
case 'boolean': {
|
|
||||||
result += `${obj}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'array': {
|
|
||||||
let arrayStringLength = 0;
|
|
||||||
let mappedValues = (obj as any[]).map(value => {
|
|
||||||
let res = this.objectToString(value, indent + 1, true);
|
|
||||||
arrayStringLength += res.length;
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (arrayStringLength <= 120) {
|
|
||||||
result += `[${mappedValues.join()}]`;
|
|
||||||
} else {
|
|
||||||
result += `[\n${sind}\t${mappedValues.join(',\n' + sind + '\t')}\n${sind}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'undefined': {
|
|
||||||
result += `undefined`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error('An unhandled type was found: ' + typeof objectType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* convert a string to object (with strings)
|
|
||||||
* @param str a string to turn to object with functions
|
|
||||||
*/
|
|
||||||
private stringToObject(str: string): Object {
|
|
||||||
// we doing this recursively so after the first one it will be an object
|
|
||||||
let parsedString: Object;
|
|
||||||
try {
|
|
||||||
parsedString = JSON.parse(`{${str}}`);
|
|
||||||
} catch (e) {
|
|
||||||
parsedString = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = {};
|
|
||||||
for (var i in parsedString) {
|
|
||||||
if (typeof parsedString[i] === 'string') {
|
|
||||||
if (parsedString[i].substring(0, 8) === 'function') {
|
|
||||||
global['eval']('obj[i] = ' + parsedString[i] );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
obj[i] = parsedString[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (typeof parsedString[i] === 'object') {
|
|
||||||
obj[i] = this.stringToObject(parsedString[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private fixCalculatedProperties(dashboard: IDashboardConfig): void {
|
|
||||||
dashboard.dataSources.forEach(dataSource => {
|
|
||||||
let calculated: string = dataSource.calculated as any;
|
|
||||||
if (calculated) {
|
|
||||||
if (!calculated.startsWith('function(){return')) {
|
|
||||||
throw new Error('calculated function format is not recognized: ' + calculated);
|
|
||||||
}
|
|
||||||
|
|
||||||
calculated = calculated.substr('function(){return'.length, calculated.length - 'function(){return'.length - 1);
|
|
||||||
global['eval']('dataSource.calculated = ' + calculated);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const configurationsActions = alt.createActions<IConfigurationsActions>(ConfigurationsActions);
|
const configurationsActions = alt.createActions<IConfigurationsActions>(ConfigurationsActions);
|
||||||
|
|
|
@ -201,6 +201,10 @@ export default class Dashboard extends React.Component<IDashboardProps, IDashboa
|
||||||
template.description = this.state.newTemplateDescription;
|
template.description = this.state.newTemplateDescription;
|
||||||
template.category = 'Custom Templates';
|
template.category = 'Custom Templates';
|
||||||
template.id = template.url = dashboard.id + (Math.floor(Math.random() * 1000) + 1); // generate random id
|
template.id = template.url = dashboard.id + (Math.floor(Math.random() * 1000) + 1); // generate random id
|
||||||
|
|
||||||
|
// Removing connections so private info will not be included
|
||||||
|
template.config.connections = {};
|
||||||
|
|
||||||
ConfigurationsActions.saveAsTemplate(template);
|
ConfigurationsActions.saveAsTemplate(template);
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
this.setState({ askSaveAsTemplate: false });
|
this.setState({ askSaveAsTemplate: false });
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import Toolbar from 'react-md/lib/Toolbars';
|
||||||
import Button from 'react-md/lib/Buttons/Button';
|
import Button from 'react-md/lib/Buttons/Button';
|
||||||
import CircularProgress from 'react-md/lib/Progress/CircularProgress';
|
import CircularProgress from 'react-md/lib/Progress/CircularProgress';
|
||||||
import { Card, CardTitle, CardActions, CardText } from 'react-md/lib/Cards';
|
import { Card, CardTitle, CardActions, CardText } from 'react-md/lib/Cards';
|
||||||
|
@ -13,9 +14,13 @@ import SetupStore from '../../stores/SetupStore';
|
||||||
|
|
||||||
import ConfigurationStore from '../../stores/ConfigurationsStore';
|
import ConfigurationStore from '../../stores/ConfigurationsStore';
|
||||||
import ConfigurationsActions from '../../actions/ConfigurationsActions';
|
import ConfigurationsActions from '../../actions/ConfigurationsActions';
|
||||||
|
import utils from '../../utils';
|
||||||
|
|
||||||
|
import IDownloadFile, { exportDataSources, createDownloadFiles, downloadBlob } from '../Dashboard/DownloadFile';
|
||||||
|
|
||||||
const renderHTML = require('react-render-html');
|
const renderHTML = require('react-render-html');
|
||||||
|
|
||||||
|
const MultipleSpacesRegex = / +/g;
|
||||||
const styles = {
|
const styles = {
|
||||||
card: {
|
card: {
|
||||||
minWidth: 400,
|
minWidth: 400,
|
||||||
|
@ -97,6 +102,8 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
this.onLoad = this.onLoad.bind(this);
|
this.onLoad = this.onLoad.bind(this);
|
||||||
this.setFile = this.setFile.bind(this);
|
this.setFile = this.setFile.bind(this);
|
||||||
this.updateFileName = this.updateFileName.bind(this);
|
this.updateFileName = this.updateFileName.bind(this);
|
||||||
|
this.onExportTemplate = this.onExportTemplate.bind(this);
|
||||||
|
this.downloadTemplate = this.downloadTemplate.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConfiguration(state: {templates: IDashboardConfig[], template: IDashboardConfig, creationState: string}) {
|
updateConfiguration(state: {templates: IDashboardConfig[], template: IDashboardConfig, creationState: string}) {
|
||||||
|
@ -105,6 +112,9 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
template: state.template,
|
template: state.template,
|
||||||
creationState: state.creationState
|
creationState: state.creationState
|
||||||
});
|
});
|
||||||
|
if (this.state.stage === 'requestDownloadTemplate') {
|
||||||
|
this.downloadTemplate(this.state.template);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSetup(state: IHomeState) {
|
updateSetup(state: IHomeState) {
|
||||||
|
@ -211,6 +221,19 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
this.setState({ importedFileContent });
|
this.setState({ importedFileContent });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onExportTemplate(templateId: string) {
|
||||||
|
this.setState({ stage: 'requestDownloadTemplate' });
|
||||||
|
ConfigurationsActions.loadTemplate(templateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadTemplate(template: IDashboardConfig) {
|
||||||
|
template.layouts = template.layouts || {};
|
||||||
|
let stringDashboard = utils.convertDashboardToString(template);
|
||||||
|
var dashboardName = template.id.replace(MultipleSpacesRegex, ' ');
|
||||||
|
dashboardName = template.id.replace(MultipleSpacesRegex, '_');
|
||||||
|
downloadBlob('return ' + stringDashboard, 'application/json', dashboardName + '.private.ts');
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { loaded, redirectUrl, templates, selectedTemplateId, template } = this.state;
|
let { loaded, redirectUrl, templates, selectedTemplateId, template } = this.state;
|
||||||
let { importVisible } = this.state;
|
let { importVisible } = this.state;
|
||||||
|
@ -241,6 +264,14 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
</MediaOverlay>
|
</MediaOverlay>
|
||||||
</Media>
|
</Media>
|
||||||
<CardActions style={styles.fabs}>
|
<CardActions style={styles.fabs}>
|
||||||
|
<Button
|
||||||
|
floating
|
||||||
|
secondary
|
||||||
|
style={{ backgroundColor: '#959ba5', marginRight: '2px' }}
|
||||||
|
onClick={this.onExportTemplate.bind(this, tmpl.id)}
|
||||||
|
>
|
||||||
|
file_download
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
floating
|
floating
|
||||||
secondary
|
secondary
|
||||||
|
@ -268,17 +299,37 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
categories[category].push(createCard(tmpl, index));
|
categories[category].push(createCard(tmpl, index));
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
let toolbarActions = [];
|
||||||
<div>
|
toolbarActions.push(
|
||||||
<div style={{ textAlign: 'right' }}>
|
(
|
||||||
<Button
|
<Button
|
||||||
flat
|
flat
|
||||||
tooltipLabel="Import dashboard"
|
tooltipLabel="Import dashboard"
|
||||||
onClick={this.onOpenImport.bind(this)}
|
onClick={this.onOpenImport.bind(this)}
|
||||||
label="Import dashboard"
|
label="Import dashboard"
|
||||||
>file_upload
|
>file_upload
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="md-cell md-cell--12">
|
||||||
|
<Toolbar actions={toolbarActions} />
|
||||||
|
{
|
||||||
|
Object.keys(categories).map((category, index) => {
|
||||||
|
if (!categories[category].length) { return null; }
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<h1>{category}</h1>
|
||||||
|
<div className="md-grid">
|
||||||
|
{categories[category]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
<Dialog
|
||||||
id="ImportDashboard"
|
id="ImportDashboard"
|
||||||
visible={importVisible || false}
|
visible={importVisible || false}
|
||||||
title="Import dashboard"
|
title="Import dashboard"
|
||||||
|
@ -304,22 +355,7 @@ export default class Home extends React.Component<any, IHomeState> {
|
||||||
lineDirection="center"
|
lineDirection="center"
|
||||||
placeholder="Choose an ID for the imported dashboard"
|
placeholder="Choose an ID for the imported dashboard"
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
Object.keys(categories).map((category, index) => {
|
|
||||||
if (!categories[category].length) { return null; }
|
|
||||||
return (
|
|
||||||
<div key={index}>
|
|
||||||
<h1>{category}</h1>
|
|
||||||
<div className="md-grid">
|
|
||||||
{categories[category]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
id="templateInfoDialog"
|
id="templateInfoDialog"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
export default {
|
export default class Utils {
|
||||||
kmNumber: (num: number): string => {
|
static kmNumber(num: number): string {
|
||||||
if (isNaN(num)) { return ''; }
|
if (isNaN(num)) { return ''; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -11,9 +11,148 @@ export default {
|
||||||
(num / 1000).toFixed(1) + 'K' :
|
(num / 1000).toFixed(1) + 'K' :
|
||||||
(num % 1 * 10) !== 0 ?
|
(num % 1 * 10) !== 0 ?
|
||||||
num.toFixed(1).toString() : num.toString());
|
num.toFixed(1).toString() : num.toString());
|
||||||
},
|
}
|
||||||
|
|
||||||
ago: (date: Date): string => {
|
static ago(date: Date): string {
|
||||||
return moment(date).fromNow();
|
return moment(date).fromNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static convertDashboardToString(dashboard: IDashboardConfig) {
|
||||||
|
return Utils.objectToString(dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convret a json object with functions to string
|
||||||
|
* @param obj an object with functions to convert to string
|
||||||
|
*/
|
||||||
|
static objectToString(obj: Object, indent: number = 0, lf: boolean = false): string {
|
||||||
|
|
||||||
|
let result = ''; // (lf ? '\n' : '') + '\t'.repeat(indent);
|
||||||
|
let sind = '\t'.repeat(indent);
|
||||||
|
let objectType = (Array.isArray(obj) && 'array') || typeof obj;
|
||||||
|
|
||||||
|
switch (objectType) {
|
||||||
|
case 'object': {
|
||||||
|
|
||||||
|
if (obj === null) { return result = 'null'; }
|
||||||
|
|
||||||
|
// Iterating through all values in object
|
||||||
|
let objectValue = '';
|
||||||
|
let objectValues = [];
|
||||||
|
let valuesStringLength = 0;
|
||||||
|
Object.keys(obj).forEach((key: string, idx: number) => {
|
||||||
|
|
||||||
|
let value = Utils.objectToString(obj[key], indent + 1, true);
|
||||||
|
|
||||||
|
// if key contains '.' or '-'
|
||||||
|
let skey = key.search(/\.|\-/g) >= 0 ? `"${key}"` : `${key}`;
|
||||||
|
let mapping = `${skey}: ${value}`;
|
||||||
|
valuesStringLength += mapping.length;
|
||||||
|
|
||||||
|
objectValues.push(mapping);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (valuesStringLength <= 120) {
|
||||||
|
result += `{ ${objectValues.join()} }`;
|
||||||
|
} else {
|
||||||
|
result += `{\n${sind}\t${objectValues.join(',\n' + sind + '\t')}\n${sind}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'string':
|
||||||
|
let stringValue = obj.toString();
|
||||||
|
let htmlString = stringValue.replace(/^\s+|\s+$/g, ''); // trim any leading and trailing whitespace
|
||||||
|
if ( htmlString.startsWith('<') && htmlString.endsWith('>') ) {
|
||||||
|
result += '`' + htmlString + '`'; // html needs to be wrapped in back ticks
|
||||||
|
} else {
|
||||||
|
stringValue = stringValue.replace(/\"/g, '\\"');
|
||||||
|
result += `"${stringValue}"`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'function': {
|
||||||
|
result += obj.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
case 'boolean': {
|
||||||
|
result += `${obj}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'array': {
|
||||||
|
let arrayStringLength = 0;
|
||||||
|
let mappedValues = (obj as any[]).map(value => {
|
||||||
|
let res = Utils.objectToString(value, indent + 1, true);
|
||||||
|
arrayStringLength += res.length;
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (arrayStringLength <= 120) {
|
||||||
|
result += `[${mappedValues.join()}]`;
|
||||||
|
} else {
|
||||||
|
result += `[\n${sind}\t${mappedValues.join(',\n' + sind + '\t')}\n${sind}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'undefined': {
|
||||||
|
result += `undefined`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('An unhandled type was found: ' + typeof objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert a string to object (with strings)
|
||||||
|
* @param str a string to turn to object with functions
|
||||||
|
*/
|
||||||
|
static stringToObject(str: string): Object {
|
||||||
|
// we doing this recursively so after the first one it will be an object
|
||||||
|
let parsedString: Object;
|
||||||
|
try {
|
||||||
|
parsedString = JSON.parse(`{${str}}`);
|
||||||
|
} catch (e) {
|
||||||
|
parsedString = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = {};
|
||||||
|
for (var i in parsedString) {
|
||||||
|
if (typeof parsedString[i] === 'string') {
|
||||||
|
if (parsedString[i].substring(0, 8) === 'function') {
|
||||||
|
global['eval']('obj[i] = ' + parsedString[i] );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
obj[i] = parsedString[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (typeof parsedString[i] === 'object') {
|
||||||
|
obj[i] = Utils.stringToObject(parsedString[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static fixCalculatedProperties(dashboard: IDashboardConfig): void {
|
||||||
|
dashboard.dataSources.forEach(dataSource => {
|
||||||
|
let calculated: string = dataSource.calculated as any;
|
||||||
|
if (calculated) {
|
||||||
|
if (!calculated.startsWith('function(){return')) {
|
||||||
|
throw new Error('calculated function format is not recognized: ' + calculated);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculated = calculated.substr('function(){return'.length, calculated.length - 'function(){return'.length - 1);
|
||||||
|
global['eval']('dataSource.calculated = ' + calculated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
|
@ -80,7 +80,7 @@ router.get('/dashboards', (req, res) => {
|
||||||
const jsonDefinition = getMetadata(fileContents);
|
const jsonDefinition = getMetadata(fileContents);
|
||||||
let content = 'return ' + JSON.stringify(jsonDefinition);
|
let content = 'return ' + JSON.stringify(jsonDefinition);
|
||||||
|
|
||||||
// Ensuing this dashboard is loaded into the dashboards array on the page
|
// Ensuring this dashboard is loaded into the dashboards array on the page
|
||||||
script += `
|
script += `
|
||||||
(function (window) {
|
(function (window) {
|
||||||
var dashboard = (function () {
|
var dashboard = (function () {
|
||||||
|
@ -229,8 +229,12 @@ router.get('/templates/:id', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/templates/:id', (req, res) => {
|
router.put('/templates/:id', (req, res) => {
|
||||||
let { id } = req.params;
|
let { id } = req.params || {};
|
||||||
let { script } = req.body || '';
|
let { script } = req.body || {};
|
||||||
|
|
||||||
|
if (!id || !script) {
|
||||||
|
return res.end({ error: 'No id or scripts were supplied for saving the template' });
|
||||||
|
}
|
||||||
|
|
||||||
const { privateTemplate } = paths();
|
const { privateTemplate } = paths();
|
||||||
|
|
||||||
|
@ -255,8 +259,12 @@ router.put('/templates/:id', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/dashboards/:id', (req, res) => {
|
router.put('/dashboards/:id', (req, res) => {
|
||||||
let { id } = req.params;
|
let { id } = req.params || {};
|
||||||
let { script } = req.body || '';
|
let { script } = req.body || {};
|
||||||
|
|
||||||
|
if (!id || !script) {
|
||||||
|
return res.end({ error: 'No id or script were supplied for the new dashboard' });
|
||||||
|
}
|
||||||
|
|
||||||
const { privateDashboard } = paths();
|
const { privateDashboard } = paths();
|
||||||
let dashboardPath = path.join(privateDashboard, id + '.private.js');
|
let dashboardPath = path.join(privateDashboard, id + '.private.js');
|
||||||
|
|
Загрузка…
Ссылка в новой задаче