summaryrefslogtreecommitdiffstats
path: root/devtools/client/jsonview/components
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/jsonview/components')
-rw-r--r--devtools/client/jsonview/components/headers-panel.js79
-rw-r--r--devtools/client/jsonview/components/headers.js105
-rw-r--r--devtools/client/jsonview/components/json-panel.js194
-rw-r--r--devtools/client/jsonview/components/main-tabbed-area.js89
-rw-r--r--devtools/client/jsonview/components/moz.build18
-rw-r--r--devtools/client/jsonview/components/reps/moz.build9
-rw-r--r--devtools/client/jsonview/components/reps/toolbar.js58
-rw-r--r--devtools/client/jsonview/components/search-box.js55
-rw-r--r--devtools/client/jsonview/components/text-panel.js95
9 files changed, 702 insertions, 0 deletions
diff --git a/devtools/client/jsonview/components/headers-panel.js b/devtools/client/jsonview/components/headers-panel.js
new file mode 100644
index 000000000..9229aaa01
--- /dev/null
+++ b/devtools/client/jsonview/components/headers-panel.js
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+ const { Headers } = createFactories(require("./headers"));
+ const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
+
+ const { div } = dom;
+
+ /**
+ * This template represents the 'Headers' panel
+ * s responsible for rendering its content.
+ */
+ let HeadersPanel = createClass({
+ displayName: "HeadersPanel",
+
+ propTypes: {
+ actions: PropTypes.object,
+ data: PropTypes.object,
+ },
+
+ getInitialState: function () {
+ return {
+ data: {}
+ };
+ },
+
+ render: function () {
+ let data = this.props.data;
+
+ return (
+ div({className: "headersPanelBox"},
+ HeadersToolbar({actions: this.props.actions}),
+ div({className: "panelContent"},
+ Headers({data: data})
+ )
+ )
+ );
+ }
+ });
+
+ /**
+ * This template is responsible for rendering a toolbar
+ * within the 'Headers' panel.
+ */
+ let HeadersToolbar = createFactory(createClass({
+ displayName: "HeadersToolbar",
+
+ propTypes: {
+ actions: PropTypes.object,
+ },
+
+ // Commands
+
+ onCopy: function (event) {
+ this.props.actions.onCopyHeaders();
+ },
+
+ render: function () {
+ return (
+ Toolbar({},
+ ToolbarButton({className: "btn copy", onClick: this.onCopy},
+ Locale.$STR("jsonViewer.Copy")
+ )
+ )
+ );
+ },
+ }));
+
+ // Exports from this module
+ exports.HeadersPanel = HeadersPanel;
+});
diff --git a/devtools/client/jsonview/components/headers.js b/devtools/client/jsonview/components/headers.js
new file mode 100644
index 000000000..38ac4051c
--- /dev/null
+++ b/devtools/client/jsonview/components/headers.js
@@ -0,0 +1,105 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+
+ const { div, span, table, tbody, tr, td, } = dom;
+
+ /**
+ * This template is responsible for rendering basic layout
+ * of the 'Headers' panel. It displays HTTP headers groups such as
+ * received or response headers.
+ */
+ let Headers = createClass({
+ displayName: "Headers",
+
+ propTypes: {
+ data: PropTypes.object,
+ },
+
+ getInitialState: function () {
+ return {};
+ },
+
+ render: function () {
+ let data = this.props.data;
+
+ return (
+ div({className: "netInfoHeadersTable"},
+ div({className: "netHeadersGroup"},
+ div({className: "netInfoHeadersGroup"},
+ Locale.$STR("jsonViewer.responseHeaders")
+ ),
+ table({cellPadding: 0, cellSpacing: 0},
+ HeaderList({headers: data.response})
+ )
+ ),
+ div({className: "netHeadersGroup"},
+ div({className: "netInfoHeadersGroup"},
+ Locale.$STR("jsonViewer.requestHeaders")
+ ),
+ table({cellPadding: 0, cellSpacing: 0},
+ HeaderList({headers: data.request})
+ )
+ )
+ )
+ );
+ }
+ });
+
+ /**
+ * This template renders headers list,
+ * name + value pairs.
+ */
+ let HeaderList = createFactory(createClass({
+ displayName: "HeaderList",
+
+ propTypes: {
+ headers: PropTypes.arrayOf(PropTypes.shape({
+ name: PropTypes.string,
+ value: PropTypes.string
+ }))
+ },
+
+ getInitialState: function () {
+ return {
+ headers: []
+ };
+ },
+
+ render: function () {
+ let headers = this.props.headers;
+
+ headers.sort(function (a, b) {
+ return a.name > b.name ? 1 : -1;
+ });
+
+ let rows = [];
+ headers.forEach(header => {
+ rows.push(
+ tr({key: header.name},
+ td({className: "netInfoParamName"},
+ span({title: header.name}, header.name)
+ ),
+ td({className: "netInfoParamValue"}, header.value)
+ )
+ );
+ });
+
+ return (
+ tbody({},
+ rows
+ )
+ );
+ }
+ }));
+
+ // Exports from this module
+ exports.Headers = Headers;
+});
diff --git a/devtools/client/jsonview/components/json-panel.js b/devtools/client/jsonview/components/json-panel.js
new file mode 100644
index 000000000..c7280a0b1
--- /dev/null
+++ b/devtools/client/jsonview/components/json-panel.js
@@ -0,0 +1,194 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+ const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
+ const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
+ const { SearchBox } = createFactories(require("./search-box"));
+ const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
+
+ const { div } = dom;
+ const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
+ const AUTO_EXPAND_MAX_LEVEL = 7;
+
+ /**
+ * This template represents the 'JSON' panel. The panel is
+ * responsible for rendering an expandable tree that allows simple
+ * inspection of JSON structure.
+ */
+ let JsonPanel = createClass({
+ displayName: "JsonPanel",
+
+ propTypes: {
+ data: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.array,
+ PropTypes.object
+ ]),
+ jsonTextLength: PropTypes.number,
+ searchFilter: PropTypes.string,
+ actions: PropTypes.object,
+ },
+
+ getInitialState: function () {
+ return {};
+ },
+
+ componentDidMount: function () {
+ document.addEventListener("keypress", this.onKeyPress, true);
+ },
+
+ componentWillUnmount: function () {
+ document.removeEventListener("keypress", this.onKeyPress, true);
+ },
+
+ onKeyPress: function (e) {
+ // XXX shortcut for focusing the Filter field (see Bug 1178771).
+ },
+
+ onFilter: function (object) {
+ if (!this.props.searchFilter) {
+ return true;
+ }
+
+ let json = JSON.stringify(object).toLowerCase();
+ return json.indexOf(this.props.searchFilter.toLowerCase()) >= 0;
+ },
+
+ getExpandedNodes: function (object, path = "", level = 0) {
+ if (typeof object != "object") {
+ return null;
+ }
+
+ if (level > AUTO_EXPAND_MAX_LEVEL) {
+ return null;
+ }
+
+ let expandedNodes = new Set();
+ for (let prop in object) {
+ let nodePath = path + "/" + prop;
+ expandedNodes.add(nodePath);
+
+ let nodes = this.getExpandedNodes(object[prop], nodePath, level + 1);
+ if (nodes) {
+ expandedNodes = new Set([...expandedNodes, ...nodes]);
+ }
+ }
+ return expandedNodes;
+ },
+
+ renderValue: props => {
+ let member = props.member;
+
+ // Hide object summary when object is expanded (bug 1244912).
+ if (typeof member.value == "object" && member.open) {
+ return null;
+ }
+
+ // Render the value (summary) using Reps library.
+ return Rep(Object.assign({}, props, {
+ cropLimit: 50,
+ }));
+ },
+
+ renderTree: function () {
+ // Append custom column for displaying values. This column
+ // Take all available horizontal space.
+ let columns = [{
+ id: "value",
+ width: "100%"
+ }];
+
+ // Expand the document by default if its size isn't bigger than 100KB.
+ let expandedNodes = new Set();
+ if (this.props.jsonTextLength <= AUTO_EXPAND_MAX_SIZE) {
+ expandedNodes = this.getExpandedNodes(this.props.data);
+ }
+
+ // Render tree component.
+ return TreeView({
+ object: this.props.data,
+ mode: "tiny",
+ onFilter: this.onFilter,
+ columns: columns,
+ renderValue: this.renderValue,
+ expandedNodes: expandedNodes,
+ });
+ },
+
+ render: function () {
+ let content;
+ let data = this.props.data;
+
+ try {
+ if (typeof data == "object") {
+ content = this.renderTree();
+ } else {
+ content = div({className: "jsonParseError"},
+ data + ""
+ );
+ }
+ } catch (err) {
+ content = div({className: "jsonParseError"},
+ err + ""
+ );
+ }
+
+ return (
+ div({className: "jsonPanelBox"},
+ JsonToolbar({actions: this.props.actions}),
+ div({className: "panelContent"},
+ content
+ )
+ )
+ );
+ }
+ });
+
+ /**
+ * This template represents a toolbar within the 'JSON' panel.
+ */
+ let JsonToolbar = createFactory(createClass({
+ displayName: "JsonToolbar",
+
+ propTypes: {
+ actions: PropTypes.object,
+ },
+
+ // Commands
+
+ onSave: function (event) {
+ this.props.actions.onSaveJson();
+ },
+
+ onCopy: function (event) {
+ this.props.actions.onCopyJson();
+ },
+
+ render: function () {
+ return (
+ Toolbar({},
+ ToolbarButton({className: "btn save", onClick: this.onSave},
+ Locale.$STR("jsonViewer.Save")
+ ),
+ ToolbarButton({className: "btn copy", onClick: this.onCopy},
+ Locale.$STR("jsonViewer.Copy")
+ ),
+ SearchBox({
+ actions: this.props.actions
+ })
+ )
+ );
+ },
+ }));
+
+ // Exports from this module
+ exports.JsonPanel = JsonPanel;
+});
diff --git a/devtools/client/jsonview/components/main-tabbed-area.js b/devtools/client/jsonview/components/main-tabbed-area.js
new file mode 100644
index 000000000..ecba73807
--- /dev/null
+++ b/devtools/client/jsonview/components/main-tabbed-area.js
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+ const { JsonPanel } = createFactories(require("./json-panel"));
+ const { TextPanel } = createFactories(require("./text-panel"));
+ const { HeadersPanel } = createFactories(require("./headers-panel"));
+ const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/tabs"));
+
+ /**
+ * This object represents the root application template
+ * responsible for rendering the basic tab layout.
+ */
+ let MainTabbedArea = createClass({
+ displayName: "MainTabbedArea",
+
+ propTypes: {
+ jsonText: PropTypes.string,
+ tabActive: PropTypes.number,
+ actions: PropTypes.object,
+ headers: PropTypes.object,
+ searchFilter: PropTypes.string,
+ json: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.object,
+ PropTypes.array
+ ])
+ },
+
+ getInitialState: function () {
+ return {
+ json: {},
+ headers: {},
+ jsonText: this.props.jsonText,
+ tabActive: this.props.tabActive
+ };
+ },
+
+ onTabChanged: function (index) {
+ this.setState({tabActive: index});
+ },
+
+ render: function () {
+ return (
+ Tabs({
+ tabActive: this.state.tabActive,
+ onAfterChange: this.onTabChanged},
+ TabPanel({
+ className: "json",
+ title: Locale.$STR("jsonViewer.tab.JSON")},
+ JsonPanel({
+ data: this.props.json,
+ jsonTextLength: this.props.jsonText.length,
+ actions: this.props.actions,
+ searchFilter: this.state.searchFilter
+ })
+ ),
+ TabPanel({
+ className: "rawdata",
+ title: Locale.$STR("jsonViewer.tab.RawData")},
+ TextPanel({
+ data: this.state.jsonText,
+ actions: this.props.actions
+ })
+ ),
+ TabPanel({
+ className: "headers",
+ title: Locale.$STR("jsonViewer.tab.Headers")},
+ HeadersPanel({
+ data: this.props.headers,
+ actions: this.props.actions,
+ searchFilter: this.props.searchFilter
+ })
+ )
+ )
+ );
+ }
+ });
+
+ // Exports from this module
+ exports.MainTabbedArea = MainTabbedArea;
+});
diff --git a/devtools/client/jsonview/components/moz.build b/devtools/client/jsonview/components/moz.build
new file mode 100644
index 000000000..fa66c8709
--- /dev/null
+++ b/devtools/client/jsonview/components/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'reps'
+]
+
+DevToolsModules(
+ 'headers-panel.js',
+ 'headers.js',
+ 'json-panel.js',
+ 'main-tabbed-area.js',
+ 'search-box.js',
+ 'text-panel.js'
+)
diff --git a/devtools/client/jsonview/components/reps/moz.build b/devtools/client/jsonview/components/reps/moz.build
new file mode 100644
index 000000000..1d239b7bd
--- /dev/null
+++ b/devtools/client/jsonview/components/reps/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+ 'toolbar.js',
+)
diff --git a/devtools/client/jsonview/components/reps/toolbar.js b/devtools/client/jsonview/components/reps/toolbar.js
new file mode 100644
index 000000000..52a35ffbe
--- /dev/null
+++ b/devtools/client/jsonview/components/reps/toolbar.js
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const React = require("devtools/client/shared/vendor/react");
+ const DOM = React.DOM;
+
+ /**
+ * Renders a simple toolbar.
+ */
+ let Toolbar = React.createClass({
+ displayName: "Toolbar",
+
+ propTypes: {
+ children: React.PropTypes.oneOfType([
+ React.PropTypes.array,
+ React.PropTypes.element
+ ])
+ },
+
+ render: function () {
+ return (
+ DOM.div({className: "toolbar"},
+ this.props.children
+ )
+ );
+ }
+ });
+
+ /**
+ * Renders a simple toolbar button.
+ */
+ let ToolbarButton = React.createClass({
+ displayName: "ToolbarButton",
+
+ propTypes: {
+ active: React.PropTypes.bool,
+ disabled: React.PropTypes.bool,
+ children: React.PropTypes.string,
+ },
+
+ render: function () {
+ let props = Object.assign({className: "btn"}, this.props);
+ return (
+ DOM.button(props, this.props.children)
+ );
+ },
+ });
+
+ // Exports from this module
+ exports.Toolbar = Toolbar;
+ exports.ToolbarButton = ToolbarButton;
+});
diff --git a/devtools/client/jsonview/components/search-box.js b/devtools/client/jsonview/components/search-box.js
new file mode 100644
index 000000000..fc9bcbcb8
--- /dev/null
+++ b/devtools/client/jsonview/components/search-box.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+
+ const { input } = dom;
+
+ // For smooth incremental searching (in case the user is typing quickly).
+ const searchDelay = 250;
+
+ /**
+ * This object represents a search box located at the
+ * top right corner of the application.
+ */
+ let SearchBox = createClass({
+ displayName: "SearchBox",
+
+ propTypes: {
+ actions: PropTypes.object,
+ },
+
+ onSearch: function (event) {
+ let searchBox = event.target;
+ let win = searchBox.ownerDocument.defaultView;
+
+ if (this.searchTimeout) {
+ win.clearTimeout(this.searchTimeout);
+ }
+
+ let callback = this.doSearch.bind(this, searchBox);
+ this.searchTimeout = win.setTimeout(callback, searchDelay);
+ },
+
+ doSearch: function (searchBox) {
+ this.props.actions.onSearch(searchBox.value);
+ },
+
+ render: function () {
+ return (
+ input({className: "searchBox",
+ placeholder: Locale.$STR("jsonViewer.filterJSON"),
+ onChange: this.onSearch})
+ );
+ },
+ });
+
+ // Exports from this module
+ exports.SearchBox = SearchBox;
+});
diff --git a/devtools/client/jsonview/components/text-panel.js b/devtools/client/jsonview/components/text-panel.js
new file mode 100644
index 000000000..1df2e349d
--- /dev/null
+++ b/devtools/client/jsonview/components/text-panel.js
@@ -0,0 +1,95 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+ const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+ const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
+ const { div, pre } = dom;
+
+ /**
+ * This template represents the 'Raw Data' panel displaying
+ * JSON as a text received from the server.
+ */
+ let TextPanel = createClass({
+ displayName: "TextPanel",
+
+ propTypes: {
+ actions: PropTypes.object,
+ data: PropTypes.string
+ },
+
+ getInitialState: function () {
+ return {};
+ },
+
+ render: function () {
+ return (
+ div({className: "textPanelBox"},
+ TextToolbar({actions: this.props.actions}),
+ div({className: "panelContent"},
+ pre({className: "data"},
+ this.props.data
+ )
+ )
+ )
+ );
+ }
+ });
+
+ /**
+ * This object represents a toolbar displayed within the
+ * 'Raw Data' panel.
+ */
+ let TextToolbar = createFactory(createClass({
+ displayName: "TextToolbar",
+
+ propTypes: {
+ actions: PropTypes.object,
+ },
+
+ // Commands
+
+ onPrettify: function (event) {
+ this.props.actions.onPrettify();
+ },
+
+ onSave: function (event) {
+ this.props.actions.onSaveJson();
+ },
+
+ onCopy: function (event) {
+ this.props.actions.onCopyJson();
+ },
+
+ render: function () {
+ return (
+ Toolbar({},
+ ToolbarButton({
+ className: "btn save",
+ onClick: this.onSave},
+ Locale.$STR("jsonViewer.Save")
+ ),
+ ToolbarButton({
+ className: "btn copy",
+ onClick: this.onCopy},
+ Locale.$STR("jsonViewer.Copy")
+ ),
+ ToolbarButton({
+ className: "btn prettyprint",
+ onClick: this.onPrettify},
+ Locale.$STR("jsonViewer.PrettyPrint")
+ )
+ )
+ );
+ },
+ }));
+
+ // Exports from this module
+ exports.TextPanel = TextPanel;
+});