Last active
September 23, 2016 00:55
-
-
Save luggage66/2719cb55a7d37f325cd48f2d2fb8e05b to your computer and use it in GitHub Desktop.
CFDump-like output for JS objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import './valueInspector.less'; | |
import Immutable from 'immutable'; | |
export default class ValueInspector extends Component | |
{ | |
render() { | |
let referenceTracker = new CircularReferenceTracker(); | |
let path = Immutable.List(); | |
return <div className="ValueInspector_Component"> | |
<Value value={this.props.value} referenceTracker={referenceTracker} path={path} /> | |
</div>; | |
} | |
} | |
function Value({ value, referenceTracker, path }) { | |
let isCircular = referenceTracker.addReference(value, path); | |
if (isCircular) { | |
return <Circular value={value} path={path} referenceTracker={referenceTracker} />; | |
} | |
let Component = determineComponent(value); | |
return <Component value={value} path={path} referenceTracker={referenceTracker} />; | |
} | |
function determineComponent(value) { | |
const typeOfValue = typeof(value); | |
if (value === undefined) { | |
return UndefinedValue; | |
} | |
else if (value === null) { | |
return NullValue; | |
} | |
else if (typeOfValue == 'number') { | |
return NumberValue; | |
} | |
else if (typeOfValue === 'boolean') { | |
return BooleanValue; | |
} | |
else if (typeOfValue === 'string') { | |
return StringValue; | |
} | |
else if (value instanceof Date) { | |
return DateValue; | |
} | |
else if (value instanceof RegExp) { | |
return RegExpValue; | |
} | |
else if (Array.isArray(value)) { | |
return ArrayValue; | |
} | |
else if (typeOfValue === 'object') { | |
return ObjectValue; | |
} | |
else if (typeOfValue === 'function') { | |
return FunctionValue; | |
} | |
else { | |
console.error("Unknown Type", value); | |
} | |
} | |
function ObjectValue({ value, referenceTracker, path }) { | |
let objectName = value.constructor && value.constructor.name; | |
// pre-register our children at their depth | |
Object.keys(value).forEach((key, index) => referenceTracker.addReference(value[key], path.push(key))); | |
if (path.length > 1) return null; | |
return <div className="object"> | |
<div className="typeLabel">{objectName || 'Object'}</div> | |
<div className="properties"> | |
{Object.keys(value).map((key, i) => { | |
let newPath = path.push(key); | |
return <div key={key} className="property"> | |
<div className="key">{key}</div> | |
<div className="value"><Value value={value[key]} referenceTracker={referenceTracker} path={newPath} /></div> | |
</div>; | |
})} | |
</div> | |
</div>; | |
} | |
function ArrayValue({ value, referenceTracker, path }) { | |
// pre-register our children at their depth | |
value.forEach((v, index) => referenceTracker.addReference(v, path.push(index))); | |
return <div className="array"> | |
<div className="typeLabel">Array</div> | |
<div className="items"> | |
{value.map((x, i) => { | |
let newPath = path.push(i); | |
return <div key={i} className="item"> | |
<div className="index">{i}</div> | |
<div className="value"><Value value={x} referenceTracker={referenceTracker} path={newPath} /></div> | |
</div>; | |
})} | |
</div> | |
</div>; | |
} | |
function BooleanValue({ value }) { | |
return <div className="boolean"> | |
<div className="typeLabel">Boolean</div> | |
<div className="value">{value.toString()}</div> | |
</div>; | |
} | |
function NumberValue({ value }) { | |
return <div className="number"> | |
<div className="typeLabel">Number</div> | |
<div className="value">{value}</div> | |
</div>; | |
} | |
function NullValue() { | |
return <div className="null">null</div>; | |
} | |
function UndefinedValue() { | |
return <div className="undefined">undefined</div>; | |
} | |
function DateValue({ value }) { | |
return <div className="date"> | |
<div className="typeLabel">Date</div> | |
<div className="value">{value.toString()}</div> | |
</div>; | |
} | |
function RegExpValue({ value }) { | |
return <div className="regex"> | |
<div className="typeLabel">RegExp</div> | |
<div className="value">/{value.source}/</div> | |
</div>; | |
} | |
function StringValue({ value }) { | |
return <div className="string"> | |
<div className="typeLabel">String</div> | |
<div className="value">{value}</div> | |
</div>; | |
} | |
function FunctionValue({ value, referenceTracker, path }) { | |
return <div className="function"> | |
<div className="typeLabel">Function</div> | |
<div className="value">Function</div> | |
</div>; | |
} | |
function Circular({ value }) { | |
return <div className="circularReference"> | |
<div className="typeLabel">Circular Reference!</div> | |
<div className="value">{(value.constructor && value.constructor.name) || 'Object'}</div> | |
</div>; | |
} | |
class CircularReferenceTracker | |
{ | |
constructor() { | |
// should contain { value: Any, path: Immutable.List } | |
this.references = Immutable.Map(); | |
} | |
addReference(value, path) { | |
// check for circular references on non-date | |
const isObject = | |
!(value === null || value === undefined) //not null or undefined | |
&& !(value instanceof Date) //not a date (which are objects) | |
&& !(value instanceof RegExp) //not a regex (which are objects) | |
&& typeof(value) === 'object'; //all other 'object's | |
if (isObject) { | |
let existingPath = this.references.get(value); | |
if (!existingPath) { | |
this.references = this.references.set(value, path); | |
return false; //not a dupe | |
} | |
else if (existingPath.equals(path)) { | |
return false; //exact match, not a dupe (was pre-registered) | |
} | |
else { | |
return true; // duplicate | |
} | |
} | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@numberColor: orange; | |
@numberBorderColor: @numberColor; | |
@numberBackgroundColor: lighten(@numberColor, 20%); | |
@stringColor: gray; | |
@stringBorderColor: @stringColor; | |
@stringBackgroundColor: lighten(@stringColor, 30%); | |
@booleanColor: brown; | |
@booleanBorderColor: @booleanColor; | |
@booleanBackgroundColor: lighten(@booleanColor, 20%); | |
@dateColor: #9630ff; | |
@dateBorderColor: @dateColor; | |
@dateBackgroundColor: lighten(@dateColor, 20%); | |
@regexColor: #33ccff; | |
@regexBorderColor: @regexColor; | |
@regexBackgroundColor: lighten(@regexColor, 20%); | |
@circularReferenceColor: pink; | |
@circularReferenceBorderColor: @circularReferenceColor; | |
@circularReferenceBackgroundColor: lighten(@circularReferenceColor, 20%); | |
@objectColor: #4444cc; | |
@objectBorderColor: darken(@objectColor, 20%); | |
@objectBackgroundColor: @objectColor; | |
@objectPropertyBackgroundColor: desaturate(lighten(@objectColor, 30%), 10%); | |
@arrayColor: #009900; | |
@arrayBorderColor: darken(@arrayColor, 10%); | |
@arrayBackgroundColor: @arrayColor; | |
@arrayItemBackgroundColor: desaturate(lighten(@arrayColor, 40%), 40%); | |
@padding: 3px; | |
@borderWidth: 2px; | |
.ValueInspector_Component { | |
color: black; | |
font-size: 12px; | |
.key, .index { font-family: monospace; } | |
// block types | |
.object, .array { | |
> .typeLabel, > .value { | |
padding: @padding; | |
} | |
> .typeLabel { | |
display: block; | |
border-bottom-width: @borderWidth; | |
border-bottom-style: solid; | |
border-bottom-color: inherit; | |
} | |
} | |
// inline types | |
.number, .string, .boolean, .date, .circularReference, .regex { | |
border-style: solid; | |
border-width: @borderWidth; | |
> .typeLabel, > .value { | |
display: inline-block; | |
padding: @padding; | |
} | |
> .typeLabel { | |
border-right-width: @borderWidth; | |
border-right-style: solid; | |
border-right-color: inherit; | |
} | |
} | |
// null types | |
.undefined, .null { | |
font-weight: bold; | |
font-style: italic; | |
color: gray; | |
} | |
.number { | |
border-color: @numberBorderColor; | |
> .typeLabel { | |
background-color: @numberBackgroundColor; | |
} | |
} | |
.string { | |
border-color: @stringBorderColor; | |
> .typeLabel { | |
background-color: @stringBackgroundColor; | |
} | |
} | |
.boolean { | |
border-color: @booleanBorderColor; | |
> .typeLabel { | |
background-color: @booleanBackgroundColor; | |
} | |
} | |
.date { | |
border-color: @dateBorderColor; | |
> .typeLabel { | |
background-color: @dateBackgroundColor; | |
} | |
} | |
.regex { | |
border-color: @regexBorderColor; | |
> .typeLabel { | |
background-color: @regexBackgroundColor; | |
} | |
} | |
.circularReference { | |
border-color: @circularReferenceBorderColor; | |
> .typeLabel { | |
background-color: @circularReferenceBackgroundColor; | |
} | |
} | |
.object { | |
> .typeLabel { | |
background-color: @objectBackgroundColor; | |
color: white; | |
border-color: @objectBorderColor; | |
border-style: solid; | |
border-width: @borderWidth @borderWidth 0 @borderWidth; | |
} | |
> .properties { | |
width: 100%; | |
display: table; | |
border-color: @objectBorderColor; | |
border-style: solid; | |
border-width: @borderWidth 0 0 @borderWidth; | |
> .property { | |
display: table-row; | |
> .key, > .value { | |
padding: @padding; | |
display: table-cell; | |
border-color: @objectBorderColor; | |
border-style: solid; | |
border-width: 0 @borderWidth @borderWidth 0; | |
} | |
> .key { | |
width: 1px; //display: table-cell should make this auto-expand to fit the content. probably. | |
background-color: @objectPropertyBackgroundColor; | |
} | |
} | |
} | |
} | |
.array { | |
> .typeLabel { | |
background-color: @arrayBackgroundColor; | |
color: white; | |
border-color: @arrayBorderColor; | |
border-style: solid; | |
border-width: @borderWidth @borderWidth 0 @borderWidth; | |
} | |
> .items { | |
width: 100%; | |
display: table; | |
border-color: @arrayBorderColor; | |
border-style: solid; | |
border-width: @borderWidth 0 0 @borderWidth; | |
> .item { | |
display: table-row; | |
> .index, > .value { | |
padding: @padding; | |
display: table-cell; | |
border-color: @arrayBorderColor; | |
border-style: solid; | |
border-width: 0 @borderWidth @borderWidth 0; | |
} | |
> .index { | |
width: 1px; | |
background-color: @arrayItemBackgroundColor; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment