feat: moved json-formatter-js into core grafana to be able to make changes

This commit is contained in:
Torkel Ödegaard 2017-05-19 13:16:05 +02:00
parent f7a6c9a1e6
commit 5513d3c9d1
12 changed files with 698 additions and 573 deletions

View File

@ -68,7 +68,6 @@
"grunt-jscs": "3.0.1",
"grunt-sass-lint": "^0.2.2",
"grunt-sync": "^0.6.2",
"json-formatter-js": "^2.2.0",
"karma-sinon": "^1.0.5",
"lodash": "^4.17.2",
"mousetrap": "^1.6.0",

View File

@ -0,0 +1,113 @@
// Based on work https://github.com/mohsen1/json-formatter-js
// Licence MIT, Copyright (c) 2015 Mohsen Azimi
/*
* Escapes `"` charachters from string
*/
function escapeString(str: string): string {
return str.replace('"', '\"');
}
/*
* Determines if a value is an object
*/
export function isObject(value: any): boolean {
var type = typeof value;
return !!value && (type === 'object');
}
/*
* Gets constructor name of an object.
* From http://stackoverflow.com/a/332429
*
*/
export function getObjectName(object: Object): string {
if (object === undefined) {
return '';
}
if (object === null) {
return 'Object';
}
if (typeof object === 'object' && !object.constructor) {
return 'Object';
}
const funcNameRegex = /function ([^(]*)/;
const results = (funcNameRegex).exec((object).constructor.toString());
if (results && results.length > 1) {
return results[1];
} else {
return '';
}
}
/*
* Gets type of an object. Returns "null" for null objects
*/
export function getType(object: Object): string {
if (object === null) { return 'null'; }
return typeof object;
}
/*
* Generates inline preview for a JavaScript object based on a value
*/
export function getValuePreview (object: Object, value: string): string {
var type = getType(object);
if (type === 'null' || type === 'undefined') { return type; }
if (type === 'string') {
value = '"' + escapeString(value) + '"';
}
if (type === 'function'){
// Remove content of the function
return object.toString()
.replace(/[\r\n]/g, '')
.replace(/\{.*\}/, '') + '{…}';
}
return value;
}
/*
* Generates inline preview for a JavaScript object
*/
export function getPreview(object: string): string {
let value = '';
if (isObject(object)) {
value = getObjectName(object);
if (Array.isArray(object)) {
value += '[' + object.length + ']';
}
} else {
value = getValuePreview(object, object);
}
return value;
}
/*
* Generates a prefixed CSS class name
*/
export function cssClass(className: string): string {
return `json-formatter-${className}`;
}
/*
* Creates a new DOM element wiht given type and class
* TODO: move me to helpers
*/
export function createElement(type: string, className?: string, content?: Element|string): Element {
const el = document.createElement(type);
if (className) {
el.classList.add(cssClass(className));
}
if (content !== undefined) {
if (content instanceof Node) {
el.appendChild(content);
} else {
el.appendChild(document.createTextNode(String(content)));
}
}
return el;
}

View File

@ -0,0 +1,454 @@
// Based on work https://github.com/mohsen1/json-formatter-js
// Licence MIT, Copyright (c) 2015 Mohsen Azimi
import {
isObject,
getObjectName,
getType,
getValuePreview,
getPreview,
cssClass,
createElement
} from './helpers';
const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/;
const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/;
const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
// When toggleing, don't animated removal or addition of more than a few items
const MAX_ANIMATED_TOGGLE_ITEMS = 10;
const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; };
export interface JsonExplorerConfig {
hoverPreviewEnabled?: boolean;
hoverPreviewArrayCount?: number;
hoverPreviewFieldCount?: number;
animateOpen?: boolean;
animateClose?: boolean;
theme?: string;
}
const _defaultConfig: JsonExplorerConfig = {
hoverPreviewEnabled: false,
hoverPreviewArrayCount: 100,
hoverPreviewFieldCount: 5,
animateOpen: true,
animateClose: true,
theme: null
};
/**
* @class JsonExplorer
*
* JsonExplorer allows you to render JSON objects in HTML with a
* **collapsible** navigation.
*/
export default class JsonExplorer {
// Hold the open state after the toggler is used
private _isOpen: boolean = null;
// A reference to the element that we render to
private element: Element;
/**
* @param {object} json The JSON object you want to render. It has to be an
* object or array. Do NOT pass raw JSON string.
*
* @param {number} [open=1] his number indicates up to how many levels the
* rendered tree should expand. Set it to `0` to make the whole tree collapsed
* or set it to `Infinity` to expand the tree deeply
*
* @param {object} [config=defaultConfig] -
* defaultConfig = {
* hoverPreviewEnabled: false,
* hoverPreviewArrayCount: 100,
* hoverPreviewFieldCount: 5
* }
*
* Available configurations:
* #####Hover Preview
* * `hoverPreviewEnabled`: enable preview on hover
* * `hoverPreviewArrayCount`: number of array items to show in preview Any
* array larger than this number will be shown as `Array[XXX]` where `XXX`
* is length of the array.
* * `hoverPreviewFieldCount`: number of object properties to show for object
* preview. Any object with more properties that thin number will be
* truncated.
*
* @param {string} [key=undefined] The key that this object in it's parent
* context
*/
constructor(public json: any, private open = 1, private config: JsonExplorerConfig = _defaultConfig, private key?: string) {
// Setting default values for config object
if (this.config.hoverPreviewEnabled === undefined) {
this.config.hoverPreviewEnabled = _defaultConfig.hoverPreviewEnabled;
}
if (this.config.hoverPreviewArrayCount === undefined) {
this.config.hoverPreviewArrayCount = _defaultConfig.hoverPreviewArrayCount;
}
if (this.config.hoverPreviewFieldCount === undefined) {
this.config.hoverPreviewFieldCount = _defaultConfig.hoverPreviewFieldCount;
}
}
/*
* is formatter open?
*/
private get isOpen(): boolean {
if (this._isOpen !== null) {
return this._isOpen;
} else {
return this.open > 0;
}
}
/*
* set open state (from toggler)
*/
private set isOpen(value: boolean) {
this._isOpen = value;
}
/*
* is this a date string?
*/
private get isDate(): boolean {
return (this.type === 'string') &&
(DATE_STRING_REGEX.test(this.json) ||
JSON_DATE_REGEX.test(this.json) ||
PARTIAL_DATE_REGEX.test(this.json));
}
/*
* is this a URL string?
*/
private get isUrl(): boolean {
return this.type === 'string' && (this.json.indexOf('http') === 0);
}
/*
* is this an array?
*/
private get isArray(): boolean {
return Array.isArray(this.json);
}
/*
* is this an object?
* Note: In this context arrays are object as well
*/
private get isObject(): boolean {
return isObject(this.json);
}
/*
* is this an empty object with no properties?
*/
private get isEmptyObject(): boolean {
return !this.keys.length && !this.isArray;
}
/*
* is this an empty object or array?
*/
private get isEmpty(): boolean {
return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
}
/*
* did we recieve a key argument?
* This means that the formatter was called as a sub formatter of a parent formatter
*/
private get hasKey(): boolean {
return typeof this.key !== 'undefined';
}
/*
* if this is an object, get constructor function name
*/
private get constructorName(): string {
return getObjectName(this.json);
}
/*
* get type of this value
* Possible values: all JavaScript primitive types plus "array" and "null"
*/
private get type(): string {
return getType(this.json);
}
/*
* get object keys
* If there is an empty key we pad it wit quotes to make it visible
*/
private get keys(): string[] {
if (this.isObject) {
return Object.keys(this.json).map((key)=> key ? key : '""');
} else {
return [];
}
}
/**
* Toggles `isOpen` state
*
*/
toggleOpen() {
this.isOpen = !this.isOpen;
if (this.element) {
if (this.isOpen) {
this.appendChildren(this.config.animateOpen);
} else{
this.removeChildren(this.config.animateClose);
}
this.element.classList.toggle(cssClass('open'));
}
}
/**
* Open all children up to a certain depth.
* Allows actions such as expand all/collapse all
*
*/
openAtDepth(depth = 1) {
if (depth < 0) {
return;
}
this.open = depth;
this.isOpen = (depth !== 0);
if (this.element) {
this.removeChildren(false);
if (depth === 0) {
this.element.classList.remove(cssClass('open'));
} else {
this.appendChildren(this.config.animateOpen);
this.element.classList.add(cssClass('open'));
}
}
}
/**
* Generates inline preview
*
* @returns {string}
*/
getInlinepreview() {
if (this.isArray) {
// if array length is greater then 100 it shows "Array[101]"
if (this.json.length > this.config.hoverPreviewArrayCount) {
return `Array[${this.json.length}]`;
} else {
return `[${this.json.map(getPreview).join(', ')}]`;
}
} else {
const keys = this.keys;
// the first five keys (like Chrome Developer Tool)
const narrowKeys = keys.slice(0, this.config.hoverPreviewFieldCount);
// json value schematic information
const kvs = narrowKeys.map(key => `${key}:${getPreview(this.json[key])}`);
// if keys count greater then 5 then show ellipsis
const ellipsis = keys.length >= this.config.hoverPreviewFieldCount ? '…' : '';
return `{${kvs.join(', ')}${ellipsis}}`;
}
}
/**
* Renders an HTML element and installs event listeners
*
* @returns {HTMLDivElement}
*/
render(): HTMLDivElement {
// construct the root element and assign it to this.element
this.element = createElement('div', 'row');
// construct the toggler link
const togglerLink = createElement('a', 'toggler-link');
// if this is an object we need a wrapper span (toggler)
if (this.isObject) {
togglerLink.appendChild(createElement('span', 'toggler'));
}
// if this is child of a parent formatter we need to append the key
if (this.hasKey) {
togglerLink.appendChild(createElement('span', 'key', `${this.key}:`));
}
// Value for objects and arrays
if (this.isObject) {
// construct the value holder element
const value = createElement('span', 'value');
// we need a wrapper span for objects
const objectWrapperSpan = createElement('span');
// get constructor name and append it to wrapper span
var constructorName = createElement('span', 'constructor-name', this.constructorName);
objectWrapperSpan.appendChild(constructorName);
// if it's an array append the array specific elements like brackets and length
if (this.isArray) {
const arrayWrapperSpan = createElement('span');
arrayWrapperSpan.appendChild(createElement('span', 'bracket', '['));
arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length)));
arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
objectWrapperSpan.appendChild(arrayWrapperSpan);
}
// append object wrapper span to toggler link
value.appendChild(objectWrapperSpan);
togglerLink.appendChild(value);
// Primitive values
} else {
// make a value holder element
const value = this.isUrl ? createElement('a') : createElement('span');
// add type and other type related CSS classes
value.classList.add(cssClass(this.type));
if (this.isDate) {
value.classList.add(cssClass('date'));
}
if (this.isUrl) {
value.classList.add(cssClass('url'));
value.setAttribute('href', this.json);
}
// Append value content to value element
const valuePreview = getValuePreview(this.json, this.json);
value.appendChild(document.createTextNode(valuePreview));
// append the value element to toggler link
togglerLink.appendChild(value);
}
// if hover preview is enabled, append the inline preview element
if (this.isObject && this.config.hoverPreviewEnabled) {
const preview = createElement('span', 'preview-text');
preview.appendChild(document.createTextNode(this.getInlinepreview()));
togglerLink.appendChild(preview);
}
// construct a children element
const children = createElement('div', 'children');
// set CSS classes for children
if (this.isObject) {
children.classList.add(cssClass('object'));
}
if (this.isArray) {
children.classList.add(cssClass('array'));
}
if (this.isEmpty) {
children.classList.add(cssClass('empty'));
}
// set CSS classes for root element
if (this.config && this.config.theme) {
this.element.classList.add(cssClass(this.config.theme));
}
if (this.isOpen) {
this.element.classList.add(cssClass('open'));
}
// append toggler and children elements to root element
this.element.appendChild(togglerLink);
this.element.appendChild(children);
// if formatter is set to be open call appendChildren
if (this.isObject && this.isOpen) {
this.appendChildren();
}
// add event listener for toggling
if (this.isObject) {
togglerLink.addEventListener('click', this.toggleOpen.bind(this));
}
return this.element as HTMLDivElement;
}
/**
* Appends all the children to children element
* Animated option is used when user triggers this via a click
*/
appendChildren(animated = false) {
const children = this.element.querySelector(`div.${cssClass('children')}`);
if (!children || this.isEmpty) { return; }
if (animated) {
let index = 0;
const addAChild = ()=> {
const key = this.keys[index];
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
children.appendChild(formatter.render());
index += 1;
if (index < this.keys.length) {
if (index > MAX_ANIMATED_TOGGLE_ITEMS) {
addAChild();
} else {
requestAnimationFrame(addAChild);
}
}
};
requestAnimationFrame(addAChild);
} else {
this.keys.forEach(key => {
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
children.appendChild(formatter.render());
});
}
}
/**
* Removes all the children from children element
* Animated option is used when user triggers this via a click
*/
removeChildren(animated = false) {
const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement;
if (animated) {
let childrenRemoved = 0;
const removeAChild = ()=> {
if (childrenElement && childrenElement.children.length) {
childrenElement.removeChild(childrenElement.children[0]);
childrenRemoved += 1;
if (childrenRemoved > MAX_ANIMATED_TOGGLE_ITEMS) {
removeAChild();
} else {
requestAnimationFrame(removeAChild);
}
}
};
requestAnimationFrame(removeAChild);
} else {
if (childrenElement) {
childrenElement.innerHTML = '';
}
}
}
}

View File

@ -1,110 +0,0 @@
// #<{(|
// * Escapes `"` charachters from string
// |)}>#
// function escapeString(str: string): string {
// return str.replace('"', '\"');
// }
//
// #<{(|
// * Determines if a value is an object
// |)}>#
// export function isObject(value: any): boolean {
// var type = typeof value;
// return !!value && (type === 'object');
// }
//
// #<{(|
// * Gets constructor name of an object.
// * From http://stackoverflow.com/a/332429
// *
// |)}>#
// export function getObjectName(object: Object): string {
// if (object === undefined) {
// return '';
// }
// if (object === null) {
// return 'Object';
// }
// if (typeof object === 'object' && !object.constructor) {
// return 'Object';
// }
//
// const funcNameRegex = /function ([^(]*)/;
// const results = (funcNameRegex).exec((object).constructor.toString());
// if (results && results.length > 1) {
// return results[1];
// } else {
// return '';
// }
// }
//
// #<{(|
// * Gets type of an object. Returns "null" for null objects
// |)}>#
// export function getType(object: Object): string {
// if (object === null) { return 'null'; }
// return typeof object;
// }
//
// #<{(|
// * Generates inline preview for a JavaScript object based on a value
// |)}>#
// export function getValuePreview (object: Object, value: string): string {
// var type = getType(object);
//
// if (type === 'null' || type === 'undefined') { return type; }
//
// if (type === 'string') {
// value = '"' + escapeString(value) + '"';
// }
// if (type === 'function'){
//
// // Remove content of the function
// return object.toString()
// .replace(/[\r\n]/g, '')
// .replace(/\{.*\}/, '') + '{…}';
// }
// return value;
// }
//
// #<{(|
// * Generates inline preview for a JavaScript object
// |)}>#
// export function getPreview(object: string): string {
// let value = '';
// if (isObject(object)) {
// value = getObjectName(object);
// if (Array.isArray(object)) {
// value += '[' + object.length + ']';
// }
// } else {
// value = getValuePreview(object, object);
// }
// return value;
// }
//
// #<{(|
// * Generates a prefixed CSS class name
// |)}>#
// export function cssClass(className: string): string {
// return `json-formatter-${className}`;
// }
//
// #<{(|
// * Creates a new DOM element wiht given type and class
// * TODO: move me to helpers
// |)}>#
// export function createElement(type: string, className?: string, content?: Element|string): Element {
// const el = document.createElement(type);
// if (className) {
// el.classList.add(cssClass(className));
// }
// if (content !== undefined) {
// if (content instanceof Node) {
// el.appendChild(content);
// } else {
// el.appendChild(document.createTextNode(String(content)));
// }
// }
// return el;
// }

View File

@ -1,453 +0,0 @@
// import {
// isObject,
// getObjectName,
// getType,
// getValuePreview,
// getPreview,
// cssClass,
// createElement
// } from './helpers';
//
// import './style.less';
//
// const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/;
// const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/;
// const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
//
// // When toggleing, don't animated removal or addition of more than a few items
// const MAX_ANIMATED_TOGGLE_ITEMS = 10;
//
// const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; };
//
// export interface JSONFormatterConfiguration {
// hoverPreviewEnabled?: boolean;
// hoverPreviewArrayCount?: number;
// hoverPreviewFieldCount?: number;
// animateOpen?: boolean;
// animateClose?: boolean;
// theme?: string;
// };
//
// const _defaultConfig: JSONFormatterConfiguration = {
// hoverPreviewEnabled: false,
// hoverPreviewArrayCount: 100,
// hoverPreviewFieldCount: 5,
// animateOpen: true,
// animateClose: true,
// theme: null
// };
//
//
// #<{(|*
// * @class JSONFormatter
// *
// * JSONFormatter allows you to render JSON objects in HTML with a
// * **collapsible** navigation.
// |)}>#
// export default class JSONFormatter {
//
// // Hold the open state after the toggler is used
// private _isOpen: boolean = null;
//
// // A reference to the element that we render to
// private element: Element;
//
// #<{(|*
// * @param {object} json The JSON object you want to render. It has to be an
// * object or array. Do NOT pass raw JSON string.
// *
// * @param {number} [open=1] his number indicates up to how many levels the
// * rendered tree should expand. Set it to `0` to make the whole tree collapsed
// * or set it to `Infinity` to expand the tree deeply
// *
// * @param {object} [config=defaultConfig] -
// * defaultConfig = {
// * hoverPreviewEnabled: false,
// * hoverPreviewArrayCount: 100,
// * hoverPreviewFieldCount: 5
// * }
// *
// * Available configurations:
// * #####Hover Preview
// * * `hoverPreviewEnabled`: enable preview on hover
// * * `hoverPreviewArrayCount`: number of array items to show in preview Any
// * array larger than this number will be shown as `Array[XXX]` where `XXX`
// * is length of the array.
// * * `hoverPreviewFieldCount`: number of object properties to show for object
// * preview. Any object with more properties that thin number will be
// * truncated.
// *
// * @param {string} [key=undefined] The key that this object in it's parent
// * context
// |)}>#
// constructor(public json: any, private open = 1, private config: JSONFormatterConfiguration = _defaultConfig, private key?: string) {
//
// // Setting default values for config object
// if (this.config.hoverPreviewEnabled === undefined) {
// this.config.hoverPreviewEnabled = _defaultConfig.hoverPreviewEnabled;
// }
// if (this.config.hoverPreviewArrayCount === undefined) {
// this.config.hoverPreviewArrayCount = _defaultConfig.hoverPreviewArrayCount;
// }
// if (this.config.hoverPreviewFieldCount === undefined) {
// this.config.hoverPreviewFieldCount = _defaultConfig.hoverPreviewFieldCount;
// }
// }
//
// #<{(|
// * is formatter open?
// |)}>#
// private get isOpen(): boolean {
// if (this._isOpen !== null) {
// return this._isOpen;
// } else {
// return this.open > 0;
// }
// }
//
// #<{(|
// * set open state (from toggler)
// |)}>#
// private set isOpen(value: boolean) {
// this._isOpen = value;
// }
//
// #<{(|
// * is this a date string?
// |)}>#
// private get isDate(): boolean {
// return (this.type === 'string') &&
// (DATE_STRING_REGEX.test(this.json) ||
// JSON_DATE_REGEX.test(this.json) ||
// PARTIAL_DATE_REGEX.test(this.json));
// }
//
// #<{(|
// * is this a URL string?
// |)}>#
// private get isUrl(): boolean {
// return this.type === 'string' && (this.json.indexOf('http') === 0);
// }
//
// #<{(|
// * is this an array?
// |)}>#
// private get isArray(): boolean {
// return Array.isArray(this.json);
// }
//
// #<{(|
// * is this an object?
// * Note: In this context arrays are object as well
// |)}>#
// private get isObject(): boolean {
// return isObject(this.json);
// }
//
// #<{(|
// * is this an empty object with no properties?
// |)}>#
// private get isEmptyObject(): boolean {
// return !this.keys.length && !this.isArray;
// }
//
// #<{(|
// * is this an empty object or array?
// |)}>#
// private get isEmpty(): boolean {
// return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
// }
//
// #<{(|
// * did we recieve a key argument?
// * This means that the formatter was called as a sub formatter of a parent formatter
// |)}>#
// private get hasKey(): boolean {
// return typeof this.key !== 'undefined';
// }
//
// #<{(|
// * if this is an object, get constructor function name
// |)}>#
// private get constructorName(): string {
// return getObjectName(this.json);
// }
//
// #<{(|
// * get type of this value
// * Possible values: all JavaScript primitive types plus "array" and "null"
// |)}>#
// private get type(): string {
// return getType(this.json);
// }
//
// #<{(|
// * get object keys
// * If there is an empty key we pad it wit quotes to make it visible
// |)}>#
// private get keys(): string[] {
// if (this.isObject) {
// return Object.keys(this.json).map((key)=> key ? key : '""');
// } else {
// return [];
// }
// }
//
// #<{(|*
// * Toggles `isOpen` state
// *
// |)}>#
// toggleOpen() {
// this.isOpen = !this.isOpen;
//
// if (this.element) {
// if (this.isOpen) {
// this.appendChildren(this.config.animateOpen);
// } else{
// this.removeChildren(this.config.animateClose);
// }
// this.element.classList.toggle(cssClass('open'));
// }
// }
//
// #<{(|*
// * Open all children up to a certain depth.
// * Allows actions such as expand all/collapse all
// *
// |)}>#
// openAtDepth(depth = 1) {
// if (depth < 0) {
// return;
// }
//
// this.open = depth;
// this.isOpen = (depth !== 0);
//
// if (this.element) {
// this.removeChildren(false);
//
// if (depth === 0) {
// this.element.classList.remove(cssClass('open'));
// } else {
// this.appendChildren(this.config.animateOpen);
// this.element.classList.add(cssClass('open'));
// }
// }
// }
//
// #<{(|*
// * Generates inline preview
// *
// * @returns {string}
// |)}>#
// getInlinepreview() {
// if (this.isArray) {
//
// // if array length is greater then 100 it shows "Array[101]"
// if (this.json.length > this.config.hoverPreviewArrayCount) {
// return `Array[${this.json.length}]`;
// } else {
// return `[${this.json.map(getPreview).join(', ')}]`;
// }
// } else {
//
// const keys = this.keys;
//
// // the first five keys (like Chrome Developer Tool)
// const narrowKeys = keys.slice(0, this.config.hoverPreviewFieldCount);
//
// // json value schematic information
// const kvs = narrowKeys.map(key => `${key}:${getPreview(this.json[key])}`);
//
// // if keys count greater then 5 then show ellipsis
// const ellipsis = keys.length >= this.config.hoverPreviewFieldCount ? '…' : '';
//
// return `{${kvs.join(', ')}${ellipsis}}`;
// }
// }
//
//
// #<{(|*
// * Renders an HTML element and installs event listeners
// *
// * @returns {HTMLDivElement}
// |)}>#
// render(): HTMLDivElement {
//
// // construct the root element and assign it to this.element
// this.element = createElement('div', 'row');
//
// // construct the toggler link
// const togglerLink = createElement('a', 'toggler-link');
//
// // if this is an object we need a wrapper span (toggler)
// if (this.isObject) {
// togglerLink.appendChild(createElement('span', 'toggler'));
// }
//
// // if this is child of a parent formatter we need to append the key
// if (this.hasKey) {
// togglerLink.appendChild(createElement('span', 'key', `${this.key}:`));
// }
//
// // Value for objects and arrays
// if (this.isObject) {
//
// // construct the value holder element
// const value = createElement('span', 'value');
//
// // we need a wrapper span for objects
// const objectWrapperSpan = createElement('span');
//
// // get constructor name and append it to wrapper span
// var constructorName = createElement('span', 'constructor-name', this.constructorName);
// objectWrapperSpan.appendChild(constructorName);
//
// // if it's an array append the array specific elements like brackets and length
// if (this.isArray) {
// const arrayWrapperSpan = createElement('span');
// arrayWrapperSpan.appendChild(createElement('span', 'bracket', '['));
// arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length)));
// arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
// objectWrapperSpan.appendChild(arrayWrapperSpan);
// }
//
// // append object wrapper span to toggler link
// value.appendChild(objectWrapperSpan);
// togglerLink.appendChild(value);
//
// // Primitive values
// } else {
//
// // make a value holder element
// const value = this.isUrl ? createElement('a') : createElement('span');
//
// // add type and other type related CSS classes
// value.classList.add(cssClass(this.type));
// if (this.isDate) {
// value.classList.add(cssClass('date'));
// }
// if (this.isUrl) {
// value.classList.add(cssClass('url'));
// value.setAttribute('href', this.json);
// }
//
// // Append value content to value element
// const valuePreview = getValuePreview(this.json, this.json);
// value.appendChild(document.createTextNode(valuePreview));
//
// // append the value element to toggler link
// togglerLink.appendChild(value);
// }
//
// // if hover preview is enabled, append the inline preview element
// if (this.isObject && this.config.hoverPreviewEnabled) {
// const preview = createElement('span', 'preview-text');
// preview.appendChild(document.createTextNode(this.getInlinepreview()));
// togglerLink.appendChild(preview);
// }
//
// // construct a children element
// const children = createElement('div', 'children');
//
// // set CSS classes for children
// if (this.isObject) {
// children.classList.add(cssClass('object'));
// }
// if (this.isArray) {
// children.classList.add(cssClass('array'));
// }
// if (this.isEmpty) {
// children.classList.add(cssClass('empty'));
// }
//
// // set CSS classes for root element
// if (this.config && this.config.theme) {
// this.element.classList.add(cssClass(this.config.theme));
// }
// if (this.isOpen) {
// this.element.classList.add(cssClass('open'));
// }
//
// // append toggler and children elements to root element
// this.element.appendChild(togglerLink);
// this.element.appendChild(children);
//
// // if formatter is set to be open call appendChildren
// if (this.isObject && this.isOpen) {
// this.appendChildren();
// }
//
// // add event listener for toggling
// if (this.isObject) {
// togglerLink.addEventListener('click', this.toggleOpen.bind(this));
// }
//
// return this.element as HTMLDivElement;
// }
//
// #<{(|*
// * Appends all the children to children element
// * Animated option is used when user triggers this via a click
// |)}>#
// appendChildren(animated = false) {
// const children = this.element.querySelector(`div.${cssClass('children')}`);
//
// if (!children || this.isEmpty) { return; }
//
// if (animated) {
// let index = 0;
// const addAChild = ()=> {
// const key = this.keys[index];
// const formatter = new JSONFormatter(this.json[key], this.open - 1, this.config, key);
// children.appendChild(formatter.render());
//
// index += 1;
//
// if (index < this.keys.length) {
// if (index > MAX_ANIMATED_TOGGLE_ITEMS) {
// addAChild();
// } else {
// requestAnimationFrame(addAChild);
// }
// }
// };
//
// requestAnimationFrame(addAChild);
//
// } else {
// this.keys.forEach(key => {
// const formatter = new JSONFormatter(this.json[key], this.open - 1, this.config, key);
// children.appendChild(formatter.render());
// });
// }
// }
//
// #<{(|*
// * Removes all the children from children element
// * Animated option is used when user triggers this via a click
// |)}>#
// removeChildren(animated = false) {
// const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement;
//
// if (animated) {
// let childrenRemoved = 0;
// const removeAChild = ()=> {
// if (childrenElement && childrenElement.children.length) {
// childrenElement.removeChild(childrenElement.children[0]);
// childrenRemoved += 1;
// if (childrenRemoved > MAX_ANIMATED_TOGGLE_ITEMS) {
// removeAChild();
// } else {
// requestAnimationFrame(removeAChild);
// }
// }
// };
// requestAnimationFrame(removeAChild);
// } else {
// if (childrenElement) {
// childrenElement.innerHTML = '';
// }
// }
// }
// }

View File

@ -1,7 +1,7 @@
///<reference path="../../headers/common.d.ts" />
import coreModule from 'app/core/core_module';
import JsonFormatter from 'json-formatter-js';
import JsonExplorer from './json_explorer/json_explorer';
const template = `
@ -48,7 +48,7 @@ export function responseViewer() {
}
const formatter = new JsonFormatter(scope.response, 2, {
const formatter = new JsonExplorer(scope.response, 2, {
theme: 'dark',
});

View File

@ -33,7 +33,6 @@ System.config({
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
"d3": "vendor/d3/d3.js",
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
"json-formatter-js": "vendor/npm/json-formatter-js/dist/json-formatter"
},
packages: {

View File

@ -76,6 +76,7 @@
@import "components/edit_sidemenu.scss";
@import "components/row.scss";
@import "components/response_viewer.scss";
@import "components/json_explorer.scss";
// PAGES
@import "pages/login";

View File

@ -0,0 +1,128 @@
@mixin json-explorer-theme(
$default-color: black,
$string-color: green,
$number-color: blue,
$boolean-color: red,
$null-color: #855A00,
$undefined-color: rgb(202, 11, 105),
$function-color: #FF20ED,
$rotate-time: 100ms,
$toggler-opacity: 0.6,
$toggler-color: #45376F,
$bracket-color: blue,
$key-color: #00008B,
$url-color: blue) {
font-family: monospace;
&, a, a:hover {
color: $default-color;
text-decoration: none;
}
.json-formatter-row {
margin-left: 1rem;
}
.json-formatter-children {
&.json-formatter-empty {
opacity: 0.5;
margin-left: 1rem;
&::after { display: none; }
&.json-formatter-object::after { content: "No properties"; }
&.json-formatter-array::after { content: "[]"; }
}
}
.json-formatter-string {
color: $string-color;
white-space: pre;
word-wrap: break-word;
}
.json-formatter-number { color: $number-color; }
.json-formatter-boolean { color: $boolean-color; }
.json-formatter-null { color: $null-color; }
.json-formatter-undefined { color: $undefined-color; }
.json-formatter-function { color: $function-color; }
.json-formatter-date { background-color: fade($default-color, 5%); }
.json-formatter-url {
text-decoration: underline;
color: $url-color;
cursor: pointer;
}
.json-formatter-bracket { color: $bracket-color; }
.json-formatter-key {
color: $key-color;
cursor: pointer;
padding-right: 0.2rem;
}
.json-formatter-constructor-name {
cursor: pointer;
}
.json-formatter-toggler {
line-height: 1.2rem;
font-size: 0.7rem;
vertical-align: middle;
opacity: $toggler-opacity;
cursor: pointer;
padding-right: 0.2rem;
&::after {
display: inline-block;
transition: transform $rotate-time ease-in;
content: "";
}
}
// Inline preview on hover (optional)
> a > .json-formatter-preview-text {
opacity: 0;
transition: opacity .15s ease-in;
font-style: italic;
}
&:hover > a > .json-formatter-preview-text {
opacity: 0.6;
}
// Open state
&.json-formatter-open {
> .json-formatter-toggler-link .json-formatter-toggler::after{
transform: rotate(90deg);
}
> .json-formatter-children::after {
display: inline-block;
}
> a > .json-formatter-preview-text {
display: none;
}
&.json-formatter-empty::after {
display: block;
}
}
}
.json-formatter-row {
@include json-explorer-theme();
}
// Dark theme
.json-formatter-dark.json-formatter-row {
@include json-explorer-theme(
$default-color: white,
$string-color: #31F031,
$number-color: #66C2FF,
$boolean-color: #EC4242,
$null-color: #EEC97D,
$undefined-color: rgb(239, 143, 190),
$function-color: #FD48CB,
$rotate-time: 100ms,
$toggler-opacity: 0.6,
$toggler-color: #45376F,
$bracket-color: #9494FF,
$key-color: #23A0DB,
$url-color: #027BFF);
}

View File

@ -41,7 +41,6 @@
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
"d3": "vendor/d3/d3.js",
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
"json-formatter-js": "vendor/npm/json-formatter-js/dist/json-formatter"
},
packages: {

View File

@ -34,7 +34,6 @@ module.exports = function(config) {
'remarkable/dist/*',
'virtual-scroll/**/*',
'mousetrap/**/*',
'json-formatter-js/dist/*.js',
],
dest: '<%= srcDir %>/vendor/npm'
}

View File

@ -2561,10 +2561,6 @@ jshint@~2.9.4:
shelljs "0.3.x"
strip-json-comments "1.0.x"
json-formatter-js@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/json-formatter-js/-/json-formatter-js-2.2.0.tgz#1ed987223ef2f1d945304597faae78b580a8212b"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"