summaryrefslogtreecommitdiffstats
path: root/devtools/client/jsonview/components
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/jsonview/components
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/jsonview/components')
-rw-r--r--devtools/client/jsonview/components/Headers.js118
-rw-r--r--devtools/client/jsonview/components/HeadersPanel.js56
-rw-r--r--devtools/client/jsonview/components/HeadersToolbar.js51
-rw-r--r--devtools/client/jsonview/components/JsonPanel.js163
-rw-r--r--devtools/client/jsonview/components/JsonToolbar.js92
-rw-r--r--devtools/client/jsonview/components/LiveText.js43
-rw-r--r--devtools/client/jsonview/components/MainTabbedArea.js120
-rw-r--r--devtools/client/jsonview/components/SearchBox.js102
-rw-r--r--devtools/client/jsonview/components/TextPanel.js52
-rw-r--r--devtools/client/jsonview/components/TextToolbar.js80
-rw-r--r--devtools/client/jsonview/components/moz.build20
-rw-r--r--devtools/client/jsonview/components/reps/Toolbar.js48
-rw-r--r--devtools/client/jsonview/components/reps/moz.build9
13 files changed, 954 insertions, 0 deletions
diff --git a/devtools/client/jsonview/components/Headers.js b/devtools/client/jsonview/components/Headers.js
new file mode 100644
index 0000000000..7477627328
--- /dev/null
+++ b/devtools/client/jsonview/components/Headers.js
@@ -0,0 +1,118 @@
+/* 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 {
+ createFactory,
+ Component,
+ } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ 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.
+ */
+ class Headers extends Component {
+ static get propTypes() {
+ return {
+ data: PropTypes.object,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+
+ render() {
+ const data = this.props.data;
+
+ return div(
+ { className: "netInfoHeadersTable" },
+ div(
+ { className: "netHeadersGroup" },
+ div(
+ { className: "netInfoHeadersGroup" },
+ JSONView.Locale["jsonViewer.responseHeaders"]
+ ),
+ table(
+ { cellPadding: 0, cellSpacing: 0 },
+ HeaderListFactory({ headers: data.response })
+ )
+ ),
+ div(
+ { className: "netHeadersGroup" },
+ div(
+ { className: "netInfoHeadersGroup" },
+ JSONView.Locale["jsonViewer.requestHeaders"]
+ ),
+ table(
+ { cellPadding: 0, cellSpacing: 0 },
+ HeaderListFactory({ headers: data.request })
+ )
+ )
+ );
+ }
+ }
+
+ /**
+ * This template renders headers list,
+ * name + value pairs.
+ */
+ class HeaderList extends Component {
+ static get propTypes() {
+ return {
+ headers: PropTypes.arrayOf(
+ PropTypes.shape({
+ name: PropTypes.string,
+ value: PropTypes.string,
+ })
+ ),
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ headers: [],
+ };
+ }
+
+ render() {
+ const headers = this.props.headers;
+
+ headers.sort(function (a, b) {
+ return a.name > b.name ? 1 : -1;
+ });
+
+ const 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);
+ }
+ }
+
+ const HeaderListFactory = createFactory(HeaderList);
+
+ // Exports from this module
+ exports.Headers = Headers;
+});
diff --git a/devtools/client/jsonview/components/HeadersPanel.js b/devtools/client/jsonview/components/HeadersPanel.js
new file mode 100644
index 0000000000..0e9e17190e
--- /dev/null
+++ b/devtools/client/jsonview/components/HeadersPanel.js
@@ -0,0 +1,56 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const { createFactories } = require("devtools/client/shared/react-utils");
+
+ const { Headers } = createFactories(
+ require("devtools/client/jsonview/components/Headers")
+ );
+ const { HeadersToolbar } = createFactories(
+ require("devtools/client/jsonview/components/HeadersToolbar")
+ );
+
+ const { div } = dom;
+
+ /**
+ * This template represents the 'Headers' panel
+ * s responsible for rendering its content.
+ */
+ class HeadersPanel extends Component {
+ static get propTypes() {
+ return {
+ actions: PropTypes.object,
+ data: PropTypes.object,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ data: {},
+ };
+ }
+
+ render() {
+ const data = this.props.data;
+
+ return div(
+ { className: "headersPanelBox tab-panel-inner" },
+ HeadersToolbar({ actions: this.props.actions }),
+ div({ className: "panelContent" }, Headers({ data }))
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.HeadersPanel = HeadersPanel;
+});
diff --git a/devtools/client/jsonview/components/HeadersToolbar.js b/devtools/client/jsonview/components/HeadersToolbar.js
new file mode 100644
index 0000000000..f9122c3a31
--- /dev/null
+++ b/devtools/client/jsonview/components/HeadersToolbar.js
@@ -0,0 +1,51 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { createFactories } = require("devtools/client/shared/react-utils");
+
+ const { Toolbar, ToolbarButton } = createFactories(
+ require("devtools/client/jsonview/components/reps/Toolbar")
+ );
+
+ /**
+ * This template is responsible for rendering a toolbar
+ * within the 'Headers' panel.
+ */
+ class HeadersToolbar extends Component {
+ static get propTypes() {
+ return {
+ actions: PropTypes.object,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onCopy = this.onCopy.bind(this);
+ }
+
+ // Commands
+
+ onCopy(event) {
+ this.props.actions.onCopyHeaders();
+ }
+
+ render() {
+ return Toolbar(
+ {},
+ ToolbarButton(
+ { className: "btn copy", onClick: this.onCopy },
+ JSONView.Locale["jsonViewer.Copy"]
+ )
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.HeadersToolbar = HeadersToolbar;
+});
diff --git a/devtools/client/jsonview/components/JsonPanel.js b/devtools/client/jsonview/components/JsonPanel.js
new file mode 100644
index 0000000000..f569aad4c6
--- /dev/null
+++ b/devtools/client/jsonview/components/JsonPanel.js
@@ -0,0 +1,163 @@
+/* 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 {
+ createFactory,
+ Component,
+ } = require("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { createFactories } = require("devtools/client/shared/react-utils");
+
+ const TreeView = createFactory(
+ require("devtools/client/shared/components/tree/TreeView")
+ );
+ const { JsonToolbar } = createFactories(
+ require("devtools/client/jsonview/components/JsonToolbar")
+ );
+
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+
+ const { div } = dom;
+
+ function isObject(value) {
+ return Object(value) === value;
+ }
+
+ /**
+ * This template represents the 'JSON' panel. The panel is
+ * responsible for rendering an expandable tree that allows simple
+ * inspection of JSON structure.
+ */
+ class JsonPanel extends Component {
+ static get propTypes() {
+ return {
+ data: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.array,
+ PropTypes.object,
+ PropTypes.bool,
+ PropTypes.number,
+ ]),
+ dataSize: PropTypes.number,
+ expandedNodes: PropTypes.instanceOf(Set),
+ searchFilter: PropTypes.string,
+ actions: PropTypes.object,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.onKeyPress = this.onKeyPress.bind(this);
+ this.onFilter = this.onFilter.bind(this);
+ this.renderValue = this.renderValue.bind(this);
+ this.renderTree = this.renderTree.bind(this);
+ }
+
+ componentDidMount() {
+ document.addEventListener("keypress", this.onKeyPress, true);
+ document.getElementById("json-scrolling-panel").focus();
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener("keypress", this.onKeyPress, true);
+ }
+
+ onKeyPress(e) {
+ // XXX shortcut for focusing the Filter field (see Bug 1178771).
+ }
+
+ onFilter(object) {
+ if (!this.props.searchFilter) {
+ return true;
+ }
+
+ const json = object.name + JSON.stringify(object.value);
+ return json.toLowerCase().includes(this.props.searchFilter.toLowerCase());
+ }
+
+ renderValue(props) {
+ const member = props.member;
+
+ // Hide object summary when non-empty object is expanded (bug 1244912).
+ if (isObject(member.value) && member.hasChildren && member.open) {
+ return null;
+ }
+
+ // Render the value (summary) using Reps library.
+ return Rep(
+ Object.assign({}, props, {
+ cropLimit: 50,
+ noGrip: true,
+ isInContentPage: true,
+ })
+ );
+ }
+
+ renderTree() {
+ // Append custom column for displaying values. This column
+ // Take all available horizontal space.
+ const columns = [
+ {
+ id: "value",
+ width: "100%",
+ },
+ ];
+
+ // Render tree component.
+ return TreeView({
+ object: this.props.data,
+ mode: MODE.TINY,
+ onFilter: this.onFilter,
+ columns,
+ renderValue: this.renderValue,
+ expandedNodes: this.props.expandedNodes,
+ });
+ }
+
+ render() {
+ let content;
+ const data = this.props.data;
+
+ if (!isObject(data)) {
+ content = div(
+ { className: "jsonPrimitiveValue" },
+ Rep({
+ object: data,
+ })
+ );
+ } else if (data instanceof Error) {
+ content = div({ className: "jsonParseError" }, data + "");
+ } else {
+ content = this.renderTree();
+ }
+
+ return div(
+ { className: "jsonPanelBox tab-panel-inner" },
+ JsonToolbar({
+ actions: this.props.actions,
+ dataSize: this.props.dataSize,
+ }),
+ div(
+ {
+ className: "panelContent",
+ id: "json-scrolling-panel",
+ tabIndex: 0,
+ },
+ content
+ )
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.JsonPanel = JsonPanel;
+});
diff --git a/devtools/client/jsonview/components/JsonToolbar.js b/devtools/client/jsonview/components/JsonToolbar.js
new file mode 100644
index 0000000000..34e6ed285b
--- /dev/null
+++ b/devtools/client/jsonview/components/JsonToolbar.js
@@ -0,0 +1,92 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const { createFactories } = require("devtools/client/shared/react-utils");
+ const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const { SearchBox } = createFactories(
+ require("devtools/client/jsonview/components/SearchBox")
+ );
+ const { Toolbar, ToolbarButton } = createFactories(
+ require("devtools/client/jsonview/components/reps/Toolbar")
+ );
+
+ /* 100kB file */
+ const EXPAND_THRESHOLD = 100 * 1024;
+
+ /**
+ * This template represents a toolbar within the 'JSON' panel.
+ */
+ class JsonToolbar extends Component {
+ static get propTypes() {
+ return {
+ actions: PropTypes.object,
+ dataSize: PropTypes.number,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onSave = this.onSave.bind(this);
+ this.onCopy = this.onCopy.bind(this);
+ this.onCollapse = this.onCollapse.bind(this);
+ this.onExpand = this.onExpand.bind(this);
+ }
+
+ // Commands
+
+ onSave(event) {
+ this.props.actions.onSaveJson();
+ }
+
+ onCopy(event) {
+ this.props.actions.onCopyJson();
+ }
+
+ onCollapse(event) {
+ this.props.actions.onCollapse();
+ }
+
+ onExpand(event) {
+ this.props.actions.onExpand();
+ }
+
+ render() {
+ return Toolbar(
+ {},
+ ToolbarButton(
+ { className: "btn save", onClick: this.onSave },
+ JSONView.Locale["jsonViewer.Save"]
+ ),
+ ToolbarButton(
+ { className: "btn copy", onClick: this.onCopy },
+ JSONView.Locale["jsonViewer.Copy"]
+ ),
+ ToolbarButton(
+ { className: "btn collapse", onClick: this.onCollapse },
+ JSONView.Locale["jsonViewer.CollapseAll"]
+ ),
+ ToolbarButton(
+ { className: "btn expand", onClick: this.onExpand },
+ this.props.dataSize > EXPAND_THRESHOLD
+ ? JSONView.Locale["jsonViewer.ExpandAllSlow"]
+ : JSONView.Locale["jsonViewer.ExpandAll"]
+ ),
+ div({ className: "devtools-separator" }),
+ SearchBox({
+ actions: this.props.actions,
+ })
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.JsonToolbar = JsonToolbar;
+});
diff --git a/devtools/client/jsonview/components/LiveText.js b/devtools/client/jsonview/components/LiveText.js
new file mode 100644
index 0000000000..d90c099340
--- /dev/null
+++ b/devtools/client/jsonview/components/LiveText.js
@@ -0,0 +1,43 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+ const { pre } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ /**
+ * This object represents a live DOM text node in a <pre>.
+ */
+ class LiveText extends Component {
+ static get propTypes() {
+ return {
+ data: PropTypes.instanceOf(Text),
+ };
+ }
+
+ componentDidMount() {
+ this.componentDidUpdate();
+ }
+
+ componentDidUpdate() {
+ const el = findDOMNode(this);
+ if (el.firstChild === this.props.data) {
+ return;
+ }
+ el.textContent = "";
+ el.append(this.props.data);
+ }
+
+ render() {
+ return pre({ className: "data" });
+ }
+ }
+
+ // Exports from this module
+ exports.LiveText = LiveText;
+});
diff --git a/devtools/client/jsonview/components/MainTabbedArea.js b/devtools/client/jsonview/components/MainTabbedArea.js
new file mode 100644
index 0000000000..f340cbd89e
--- /dev/null
+++ b/devtools/client/jsonview/components/MainTabbedArea.js
@@ -0,0 +1,120 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { createFactories } = require("devtools/client/shared/react-utils");
+ const { JsonPanel } = createFactories(
+ require("devtools/client/jsonview/components/JsonPanel")
+ );
+ const { TextPanel } = createFactories(
+ require("devtools/client/jsonview/components/TextPanel")
+ );
+ const { HeadersPanel } = createFactories(
+ require("devtools/client/jsonview/components/HeadersPanel")
+ );
+ 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.
+ */
+ class MainTabbedArea extends Component {
+ static get propTypes() {
+ return {
+ jsonText: PropTypes.instanceOf(Text),
+ activeTab: PropTypes.number,
+ actions: PropTypes.object,
+ headers: PropTypes.object,
+ searchFilter: PropTypes.string,
+ json: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.object,
+ PropTypes.array,
+ PropTypes.bool,
+ PropTypes.number,
+ ]),
+ expandedNodes: PropTypes.instanceOf(Set),
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ json: props.json,
+ expandedNodes: props.expandedNodes,
+ jsonText: props.jsonText,
+ activeTab: props.activeTab,
+ };
+
+ this.onTabChanged = this.onTabChanged.bind(this);
+ }
+
+ onTabChanged(index) {
+ this.setState({ activeTab: index });
+
+ // Send notification event to the window. This is useful for tests.
+ window.dispatchEvent(new CustomEvent("TabChanged"));
+ }
+
+ render() {
+ return Tabs(
+ {
+ activeTab: this.state.activeTab,
+ onAfterChange: this.onTabChanged,
+ tall: true,
+ },
+ TabPanel(
+ {
+ id: "json",
+ className: "json",
+ title: JSONView.Locale["jsonViewer.tab.JSON"],
+ },
+ JsonPanel({
+ data: this.state.json,
+ expandedNodes: this.state.expandedNodes,
+ actions: this.props.actions,
+ searchFilter: this.state.searchFilter,
+ dataSize: this.state.jsonText.length,
+ })
+ ),
+ TabPanel(
+ {
+ id: "rawdata",
+ className: "rawdata",
+ title: JSONView.Locale["jsonViewer.tab.RawData"],
+ },
+ TextPanel({
+ isValidJson:
+ !(this.state.json instanceof Error) &&
+ document.readyState != "loading",
+ data: this.state.jsonText,
+ actions: this.props.actions,
+ })
+ ),
+ TabPanel(
+ {
+ id: "headers",
+ className: "headers",
+ title: JSONView.Locale["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/SearchBox.js b/devtools/client/jsonview/components/SearchBox.js
new file mode 100644
index 0000000000..cb736db85b
--- /dev/null
+++ b/devtools/client/jsonview/components/SearchBox.js
@@ -0,0 +1,102 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const { input, div, button } = 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.
+ */
+ class SearchBox extends Component {
+ static get propTypes() {
+ return {
+ actions: PropTypes.object,
+ value: PropTypes.toString,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onSearch = this.onSearch.bind(this);
+ this.doSearch = this.doSearch.bind(this);
+ this.onClearButtonClick = this.onClearButtonClick.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+
+ this.state = {
+ value: props.value || "",
+ };
+ }
+
+ onSearch(event) {
+ this.setState({
+ value: event.target.value,
+ });
+ const searchBox = event.target;
+ const win = searchBox.ownerDocument.defaultView;
+
+ if (this.searchTimeout) {
+ win.clearTimeout(this.searchTimeout);
+ }
+
+ const callback = this.doSearch.bind(this, searchBox);
+ this.searchTimeout = win.setTimeout(callback, searchDelay);
+ }
+
+ doSearch(searchBox) {
+ this.props.actions.onSearch(searchBox.value);
+ }
+
+ onClearButtonClick() {
+ this.setState({ value: "" });
+ this.props.actions.onSearch("");
+ if (this._searchBoxRef) {
+ this._searchBoxRef.focus();
+ }
+ }
+
+ onKeyDown(e) {
+ switch (e.key) {
+ case "Escape":
+ e.preventDefault();
+ this.onClearButtonClick();
+ break;
+ }
+ }
+
+ render() {
+ const { value } = this.state;
+ return div(
+ { className: "devtools-searchbox" },
+ input({
+ className: "searchBox devtools-filterinput",
+ placeholder: JSONView.Locale["jsonViewer.filterJSON"],
+ onChange: this.onSearch,
+ onKeyDown: this.onKeyDown,
+ value,
+ ref: c => {
+ this._searchBoxRef = c;
+ },
+ }),
+ button({
+ className: "devtools-searchinput-clear",
+ hidden: !value,
+ onClick: this.onClearButtonClick,
+ })
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.SearchBox = SearchBox;
+});
diff --git a/devtools/client/jsonview/components/TextPanel.js b/devtools/client/jsonview/components/TextPanel.js
new file mode 100644
index 0000000000..a830d9d01b
--- /dev/null
+++ b/devtools/client/jsonview/components/TextPanel.js
@@ -0,0 +1,52 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { createFactories } = require("devtools/client/shared/react-utils");
+ const { TextToolbar } = createFactories(
+ require("devtools/client/jsonview/components/TextToolbar")
+ );
+ const { LiveText } = createFactories(
+ require("devtools/client/jsonview/components/LiveText")
+ );
+ const { div } = dom;
+
+ /**
+ * This template represents the 'Raw Data' panel displaying
+ * JSON as a text received from the server.
+ */
+ class TextPanel extends Component {
+ static get propTypes() {
+ return {
+ isValidJson: PropTypes.bool,
+ actions: PropTypes.object,
+ data: PropTypes.instanceOf(Text),
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+
+ render() {
+ return div(
+ { className: "textPanelBox tab-panel-inner" },
+ TextToolbar({
+ actions: this.props.actions,
+ isValidJson: this.props.isValidJson,
+ }),
+ div({ className: "panelContent" }, LiveText({ data: this.props.data }))
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.TextPanel = TextPanel;
+});
diff --git a/devtools/client/jsonview/components/TextToolbar.js b/devtools/client/jsonview/components/TextToolbar.js
new file mode 100644
index 0000000000..0ab16ef192
--- /dev/null
+++ b/devtools/client/jsonview/components/TextToolbar.js
@@ -0,0 +1,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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { createFactories } = require("devtools/client/shared/react-utils");
+ const { Toolbar, ToolbarButton } = createFactories(
+ require("devtools/client/jsonview/components/reps/Toolbar")
+ );
+
+ /**
+ * This object represents a toolbar displayed within the
+ * 'Raw Data' panel.
+ */
+ class TextToolbar extends Component {
+ static get propTypes() {
+ return {
+ actions: PropTypes.object,
+ isValidJson: PropTypes.bool,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onPrettify = this.onPrettify.bind(this);
+ this.onSave = this.onSave.bind(this);
+ this.onCopy = this.onCopy.bind(this);
+ }
+
+ // Commands
+
+ onPrettify(event) {
+ this.props.actions.onPrettify();
+ }
+
+ onSave(event) {
+ this.props.actions.onSaveJson();
+ }
+
+ onCopy(event) {
+ this.props.actions.onCopyJson();
+ }
+
+ render() {
+ return Toolbar(
+ {},
+ ToolbarButton(
+ {
+ className: "btn save",
+ onClick: this.onSave,
+ },
+ JSONView.Locale["jsonViewer.Save"]
+ ),
+ ToolbarButton(
+ {
+ className: "btn copy",
+ onClick: this.onCopy,
+ },
+ JSONView.Locale["jsonViewer.Copy"]
+ ),
+ this.props.isValidJson
+ ? ToolbarButton(
+ {
+ className: "btn prettyprint",
+ onClick: this.onPrettify,
+ },
+ JSONView.Locale["jsonViewer.PrettyPrint"]
+ )
+ : null
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.TextToolbar = TextToolbar;
+});
diff --git a/devtools/client/jsonview/components/moz.build b/devtools/client/jsonview/components/moz.build
new file mode 100644
index 0000000000..8cb9dc8e87
--- /dev/null
+++ b/devtools/client/jsonview/components/moz.build
@@ -0,0 +1,20 @@
+# -*- 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.js",
+ "HeadersPanel.js",
+ "HeadersToolbar.js",
+ "JsonPanel.js",
+ "JsonToolbar.js",
+ "LiveText.js",
+ "MainTabbedArea.js",
+ "SearchBox.js",
+ "TextPanel.js",
+ "TextToolbar.js",
+)
diff --git a/devtools/client/jsonview/components/reps/Toolbar.js b/devtools/client/jsonview/components/reps/Toolbar.js
new file mode 100644
index 0000000000..458acf236f
--- /dev/null
+++ b/devtools/client/jsonview/components/reps/Toolbar.js
@@ -0,0 +1,48 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ /**
+ * Renders a simple toolbar.
+ */
+ class Toolbar extends Component {
+ static get propTypes() {
+ return {
+ children: PropTypes.oneOfType([PropTypes.array, PropTypes.element]),
+ };
+ }
+
+ render() {
+ return dom.div({ className: "toolbar" }, this.props.children);
+ }
+ }
+
+ /**
+ * Renders a simple toolbar button.
+ */
+ class ToolbarButton extends Component {
+ static get propTypes() {
+ return {
+ active: PropTypes.bool,
+ disabled: PropTypes.bool,
+ children: PropTypes.string,
+ };
+ }
+
+ render() {
+ const 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/reps/moz.build b/devtools/client/jsonview/components/reps/moz.build
new file mode 100644
index 0000000000..ba39d7767b
--- /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",
+)