Skip to content

Instantly share code, notes, and snippets.

@luggage66
Last active September 23, 2016 00:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save luggage66/2719cb55a7d37f325cd48f2d2fb8e05b to your computer and use it in GitHub Desktop.
Save luggage66/2719cb55a7d37f325cd48f2d2fb8e05b to your computer and use it in GitHub Desktop.
CFDump-like output for JS objects
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;
}
}
@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