Bug 1257173 - Make JSON viewer code eslint clean; r=linclark

MozReview-Commit-ID: LUzs6Pb4c3z

--HG--
extra : rebase_source : 72554e907dd29bb5ab60ed8b0b4c29facf1d4a85
This commit is contained in:
Jan Odvarko 2016-03-17 22:26:53 -07:00
Родитель da16b06600
Коммит 781ed5c343
26 изменённых файлов: 1126 добавлений и 1007 удалений

Просмотреть файл

@ -90,7 +90,7 @@ devtools/client/inspector/fonts/**
devtools/client/inspector/shared/test/**
devtools/client/inspector/test/**
devtools/client/inspector/*.js
devtools/client/jsonview/**
devtools/client/jsonview/lib/**
devtools/client/memory/**
devtools/client/netmonitor/test/**
devtools/client/netmonitor/har/test/**

Просмотреть файл

@ -0,0 +1,9 @@
{
"globals": {
"define": true,
"document": true,
"window": true,
"CustomEvent": true,
"Locale": true
}
}

Просмотреть файл

@ -4,66 +4,76 @@
* 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 { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Headers } = createFactories(require("./headers"));
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
const React = 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 DOM = React.DOM;
const DOM = React.DOM;
/**
* This template represents the 'Headers' panel
* s responsible for rendering its content.
*/
let HeadersPanel = React.createClass({
propTypes: {
actions: React.PropTypes.object,
data: React.PropTypes.object,
},
/**
* This template represents the 'Headers' panel
* s responsible for rendering its content.
*/
var HeadersPanel = React.createClass({
displayName: "HeadersPanel",
displayName: "HeadersPanel",
getInitialState: function() {
return {
data: {}
};
},
getInitialState: function() {
return {
data: {}
};
},
render: function() {
var data = this.props.data;
render: function() {
let data = this.props.data;
return (
DOM.div({className: "headersPanelBox"},
HeadersToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
Headers({data: data})
return (
DOM.div({className: "headersPanelBox"},
HeadersToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
Headers({data: data})
)
)
)
);
}
});
);
}
});
/**
* This template is responsible for rendering a toolbar
* within the 'Headers' panel.
*/
var HeadersToolbar = React.createFactory(React.createClass({
displayName: "HeadersToolbar",
/**
* This template is responsible for rendering a toolbar
* within the 'Headers' panel.
*/
let HeadersToolbar = React.createFactory(React.createClass({
propTypes: {
actions: React.PropTypes.object,
},
render: function() {
return (
Toolbar({},
ToolbarButton({className: "btn copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
displayName: "HeadersToolbar",
// Commands
onCopy: function(event) {
this.props.actions.onCopyHeaders();
},
render: function() {
return (
Toolbar({},
ToolbarButton({className: "btn copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
)
)
)
)
},
);
},
}));
// Commands
onCopy: function(event) {
this.props.actions.onCopyHeaders();
},
}));
// Exports from this module
exports.HeadersPanel = HeadersPanel;
// Exports from this module
exports.HeadersPanel = HeadersPanel;
});

Просмотреть файл

@ -4,97 +4,110 @@
* 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 React = require("devtools/client/shared/vendor/react");
// Constants
const DOM = React.DOM;
const PropTypes = React.PropTypes;
// Constants
const DOM = React.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 = React.createClass({
propTypes: {
data: PropTypes.object,
},
/**
* This template is responsible for rendering basic layout
* of the 'Headers' panel. It displays HTTP headers groups such as
* received or response headers.
*/
var Headers = React.createClass({
displayName: "Headers",
displayName: "Headers",
getInitialState: function() {
return {};
},
getInitialState: function() {
return {};
},
render: function() {
var data = this.props.data;
render: function() {
let data = this.props.data;
return (
DOM.div({className: "netInfoHeadersTable"},
DOM.div({className: "netHeadersGroup"},
DOM.div({className: "netInfoHeadersGroup"},
DOM.span({className: "netHeader twisty"},
Locale.$STR("jsonViewer.responseHeaders")
return (
DOM.div({className: "netInfoHeadersTable"},
DOM.div({className: "netHeadersGroup"},
DOM.div({className: "netInfoHeadersGroup"},
DOM.span({className: "netHeader twisty"},
Locale.$STR("jsonViewer.responseHeaders")
)
),
DOM.table({cellPadding: 0, cellSpacing: 0},
HeaderList({headers: data.response})
)
),
DOM.table({cellPadding: 0, cellSpacing: 0},
HeaderList({headers: data.response})
)
),
DOM.div({className: "netHeadersGroup"},
DOM.div({className: "netInfoHeadersGroup"},
DOM.span({className: "netHeader twisty"},
Locale.$STR("jsonViewer.requestHeaders")
DOM.div({className: "netHeadersGroup"},
DOM.div({className: "netInfoHeadersGroup"},
DOM.span({className: "netHeader twisty"},
Locale.$STR("jsonViewer.requestHeaders")
)
),
DOM.table({cellPadding: 0, cellSpacing: 0},
HeaderList({headers: data.request})
)
),
DOM.table({cellPadding: 0, cellSpacing: 0},
HeaderList({headers: data.request})
)
)
)
);
}
});
);
}
});
/**
* This template renders headers list,
* name + value pairs.
*/
var HeaderList = React.createFactory(React.createClass({
displayName: "HeaderList",
/**
* This template renders headers list,
* name + value pairs.
*/
let HeaderList = React.createFactory(React.createClass({
propTypes: {
headers: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
value: PropTypes.string
}))
},
getInitialState: function() {
return {
headers: []
};
},
displayName: "HeaderList",
render: function() {
var headers = this.props.headers;
getInitialState: function() {
return {
headers: []
};
},
headers.sort(function(a, b) {
return a.name > b.name ? 1 : -1;
});
render: function() {
let headers = this.props.headers;
var rows = [];
headers.forEach(header => {
rows.push(
DOM.tr({key: header.name},
DOM.td({className: "netInfoParamName"},
DOM.span({title: header.name}, header.name)
),
DOM.td({className: "netInfoParamValue"},
DOM.code({}, header.value)
headers.sort(function(a, b) {
return a.name > b.name ? 1 : -1;
});
let rows = [];
headers.forEach(header => {
rows.push(
DOM.tr({key: header.name},
DOM.td({className: "netInfoParamName"},
DOM.span({title: header.name}, header.name)
),
DOM.td({className: "netInfoParamValue"},
DOM.code({}, header.value)
)
)
);
});
return (
DOM.tbody({},
rows
)
)
});
);
}
}));
return (
DOM.tbody({},
rows
)
)
}
}));
// Exports from this module
exports.Headers = Headers;
// Exports from this module
exports.Headers = Headers;
});

Просмотреть файл

@ -4,105 +4,120 @@
* 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 { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { TreeView } = createFactories(require("./reps/tree-view"));
const { SearchBox } = createFactories(require("./search-box"));
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
const DOM = React.DOM;
const React = require("devtools/client/shared/vendor/react");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { TreeView } = createFactories(require("./reps/tree-view"));
const { SearchBox } = createFactories(require("./search-box"));
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
const DOM = React.DOM;
/**
* This template represents the 'JSON' panel. The panel is
* responsible for rendering an expandable tree that allows simple
* inspection of JSON structure.
*/
let JsonPanel = React.createClass({
propTypes: {
data: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.array,
React.PropTypes.object
]),
searchFilter: React.PropTypes.string,
actions: React.PropTypes.object,
},
/**
* This template represents the 'JSON' panel. The panel is
* responsible for rendering an expandable tree that allows simple
* inspection of JSON structure.
*/
var JsonPanel = React.createClass({
displayName: "JsonPanel",
displayName: "JsonPanel",
getInitialState: function() {
return {};
},
getInitialState: function() {
return {};
},
componentDidMount: function() {
document.addEventListener("keypress", this.onKeyPress, true);
},
componentDidMount: function() {
document.addEventListener("keypress", this.onKeyPress, true);
},
componentWillUnmount: function() {
document.removeEventListener("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).
},
onKeyPress: function(e) {
// XXX shortcut for focusing the Filter field (see Bug 1178771).
},
render: function() {
var content;
var data = this.props.data;
render: function() {
let content;
let data = this.props.data;
try {
if (typeof data == "object") {
content = TreeView({
data: this.props.data,
mode: "tiny",
searchFilter: this.props.searchFilter
});
} else {
try {
if (typeof data == "object") {
content = TreeView({
data: this.props.data,
mode: "tiny",
searchFilter: this.props.searchFilter
});
} else {
content = DOM.div({className: "jsonParseError"},
data + ""
);
}
} catch (err) {
content = DOM.div({className: "jsonParseError"},
data + ""
err + ""
);
}
} catch (err) {
content = DOM.div({className: "jsonParseError"},
err + ""
return (
DOM.div({className: "jsonPanelBox"},
JsonToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
content
)
)
);
}
});
return (
DOM.div({className: "jsonPanelBox"},
JsonToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
content
/**
* This template represents a toolbar within the 'JSON' panel.
*/
let JsonToolbar = React.createFactory(React.createClass({
propTypes: {
actions: React.PropTypes.object,
},
displayName: "JsonToolbar",
// 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
})
)
)
);
}
});
/**
* This template represents a toolbar within the 'JSON' panel.
*/
var JsonToolbar = React.createFactory(React.createClass({
displayName: "JsonToolbar",
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
})
)
)
},
// Commands
onSave: function(event) {
this.props.actions.onSaveJson();
},
onCopy: function(event) {
this.props.actions.onCopyJson();
},
}));
// Exports from this module
exports.JsonPanel = JsonPanel;
);
},
}));
// Exports from this module
exports.JsonPanel = JsonPanel;
});

Просмотреть файл

@ -7,62 +7,82 @@
"use strict";
define(function(require, exports, module) {
const React = 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("./reps/tabs"));
const React = 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("./reps/tabs"));
/**
* This object represents the root application template
* responsible for rendering the basic tab layout.
*/
let MainTabbedArea = React.createClass({
propTypes: {
jsonText: React.PropTypes.string,
tabActive: React.PropTypes.number,
actions: React.PropTypes.object,
headers: React.PropTypes.object,
searchFilter: React.PropTypes.string,
json: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object,
React.PropTypes.array
])
},
/**
* This object represents the root application template
* responsible for rendering the basic tab layout.
*/
var MainTabbedArea = React.createClass({
displayName: "MainTabbedArea",
displayName: "MainTabbedArea",
getInitialState: function() {
return {
json: {},
headers: {},
jsonText: this.props.jsonText,
tabActive: this.props.tabActive
};
},
getInitialState: function() {
return {
json: {},
headers: {},
jsonText: this.props.jsonText,
tabActive: this.props.tabActive
};
},
onTabChanged: function(index) {
this.setState({tabActive: index});
},
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,
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
})
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,
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;
// Exports from this module
exports.MainTabbedArea = MainTabbedArea;
});

Просмотреть файл

@ -7,186 +7,185 @@
"use strict";
define(function(require, exports, module) {
const React = require("devtools/client/shared/vendor/react");
const DOM = React.DOM;
const React = require("devtools/client/shared/vendor/react");
const DOM = React.DOM;
/**
* Renders simple 'tab' widget.
*
* Based on ReactSimpleTabs component
* https://github.com/pedronauck/react-simpletabs
*
* Component markup (+CSS) example:
*
* <div class='tabs'>
* <nav class='tabs-navigation'>
* <ul class='tabs-menu'>
* <li class='tabs-menu-item is-active'>Tab #1</li>
* <li class='tabs-menu-item'>Tab #2</li>
* </ul>
* </nav>
* <article class='tab-panel'>
* The content of active panel here
* </article>
* <div>
*/
let Tabs = React.createClass({
propTypes: {
className: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.string,
React.PropTypes.object
]),
tabActive: React.PropTypes.number,
onMount: React.PropTypes.func,
onBeforeChange: React.PropTypes.func,
onAfterChange: React.PropTypes.func,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
},
/**
* Renders simple 'tab' widget.
*
* Based on ReactSimpleTabs component
* https://github.com/pedronauck/react-simpletabs
*
* Component markup (+CSS) example:
*
* <div class='tabs'>
* <nav class='tabs-navigation'>
* <ul class='tabs-menu'>
* <li class='tabs-menu-item is-active'>Tab #1</li>
* <li class='tabs-menu-item'>Tab #2</li>
* </ul>
* </nav>
* <article class='tab-panel'>
* The content of active panel here
* </article>
* <div>
*/
var Tabs = React.createClass({
displayName: "Tabs",
displayName: "Tabs",
propTypes: {
className: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.string,
React.PropTypes.object
]),
tabActive: React.PropTypes.number,
onMount: React.PropTypes.func,
onBeforeChange: React.PropTypes.func,
onAfterChange: React.PropTypes.func,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
},
getDefaultProps: function() {
return {
tabActive: 1
};
},
getDefaultProps: function () {
return {
tabActive: 1
};
},
getInitialState: function() {
return {
tabActive: this.props.tabActive
};
},
getInitialState: function () {
return {
tabActive: this.props.tabActive
};
},
componentDidMount: function() {
var index = this.state.tabActive;
if (this.props.onMount) {
this.props.onMount(index);
}
},
componentWillReceiveProps: function(newProps){
if (newProps.tabActive) {
this.setState({tabActive: newProps.tabActive})
}
},
render: function () {
var classNames = ["tabs", this.props.className].join(" ");
return (
DOM.div({className: classNames},
this.getMenuItems(),
this.getSelectedPanel()
)
);
},
setActive: function(index, e) {
var onAfterChange = this.props.onAfterChange;
var onBeforeChange = this.props.onBeforeChange;
if (onBeforeChange) {
var cancel = onBeforeChange(index);
if (cancel) {
return;
componentDidMount: function() {
let index = this.state.tabActive;
if (this.props.onMount) {
this.props.onMount(index);
}
}
},
var newState = {
tabActive: index
};
this.setState(newState, () => {
if (onAfterChange) {
onAfterChange(index);
componentWillReceiveProps: function(newProps) {
if (newProps.tabActive) {
this.setState({tabActive: newProps.tabActive});
}
});
},
e.preventDefault();
},
setActive: function(index, e) {
let onAfterChange = this.props.onAfterChange;
let onBeforeChange = this.props.onBeforeChange;
getMenuItems: function () {
if (!this.props.children) {
throw new Error("Tabs must contain at least one Panel");
}
if (onBeforeChange) {
let cancel = onBeforeChange(index);
if (cancel) {
return;
}
}
if (!Array.isArray(this.props.children)) {
this.props.children = [this.props.children];
}
let newState = {
tabActive: index
};
var menuItems = this.props.children
.map(function(panel) {
return typeof panel === "function" ? panel() : panel;
}).filter(function(panel) {
return panel;
}).map(function(panel, index) {
var ref = ("tab-menu-" + (index + 1));
var title = panel.props.title;
var tabClassName = panel.props.className;
this.setState(newState, () => {
if (onAfterChange) {
onAfterChange(index);
}
});
var classes = [
"tabs-menu-item",
tabClassName,
this.state.tabActive === (index + 1) && "is-active"
].join(" ");
e.preventDefault();
},
return (
DOM.li({ref: ref, key: index, className: classes},
DOM.a({href: "#", onClick: this.setActive.bind(this, index + 1)},
title
getMenuItems: function() {
if (!this.props.children) {
throw new Error("Tabs must contain at least one Panel");
}
if (!Array.isArray(this.props.children)) {
this.props.children = [this.props.children];
}
let menuItems = this.props.children
.map(function(panel) {
return typeof panel === "function" ? panel() : panel;
}).filter(function(panel) {
return panel;
}).map(function(panel, index) {
let ref = ("tab-menu-" + (index + 1));
let title = panel.props.title;
let tabClassName = panel.props.className;
let classes = [
"tabs-menu-item",
tabClassName,
this.state.tabActive === (index + 1) && "is-active"
].join(" ");
return (
DOM.li({ref: ref, key: index, className: classes},
DOM.a({href: "#", onClick: this.setActive.bind(this, index + 1)},
title
)
)
);
}.bind(this));
return (
DOM.nav({className: "tabs-navigation"},
DOM.ul({className: "tabs-menu"},
menuItems
)
);
}.bind(this));
return (
DOM.nav({className: "tabs-navigation"},
DOM.ul({className: "tabs-menu"},
menuItems
)
)
);
},
);
},
getSelectedPanel: function () {
var index = this.state.tabActive - 1;
var panel = this.props.children[index];
getSelectedPanel: function() {
let index = this.state.tabActive - 1;
let panel = this.props.children[index];
return (
DOM.article({ref: "tab-panel", className: "tab-panel"},
panel
)
);
}
});
/**
* Renders simple tab 'panel'.
*/
var Panel = React.createClass({
displayName: "Panel",
propTypes: {
title: React.PropTypes.string.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
},
render: function () {
return DOM.div({},
this.props.children
);
}
});
// Exports from this module
exports.TabPanel = Panel;
exports.Tabs = Tabs;
return (
DOM.article({ref: "tab-panel", className: "tab-panel"},
panel
)
);
},
render: function() {
let classNames = ["tabs", this.props.className].join(" ");
return (
DOM.div({className: classNames},
this.getMenuItems(),
this.getSelectedPanel()
)
);
},
});
/**
* Renders simple tab 'panel'.
*/
let Panel = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
},
displayName: "Panel",
render: function() {
return DOM.div({},
this.props.children
);
}
});
// Exports from this module
exports.TabPanel = Panel;
exports.Tabs = Tabs;
});

Просмотреть файл

@ -7,45 +7,52 @@
"use strict";
define(function(require, exports, module) {
const React = require("devtools/client/shared/vendor/react");
const DOM = React.DOM;
const React = require("devtools/client/shared/vendor/react");
const DOM = React.DOM;
/**
* Renders a simple toolbar.
*/
let Toolbar = React.createClass({
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
])
},
/**
* Renders a simple toolbar.
*/
var Toolbar = React.createClass({
displayName: "Toolbar",
displayName: "Toolbar",
render: function() {
return (
DOM.div({className: "toolbar"},
this.props.children
)
);
}
});
/**
* Renders a simple toolbar button.
*/
var ToolbarButton = React.createClass({
displayName: "ToolbarButton",
propTypes: {
active: React.PropTypes.bool,
disabled: React.PropTypes.bool,
},
render: function() {
var props = Object.assign({className: "btn"}, this.props);
return (
DOM.button(props, this.props.children)
);
},
});
// Exports from this module
exports.Toolbar = Toolbar;
exports.ToolbarButton = ToolbarButton;
render: function() {
return (
DOM.div({className: "toolbar"},
this.props.children
)
);
}
});
/**
* Renders a simple toolbar button.
*/
let ToolbarButton = React.createClass({
propTypes: {
active: React.PropTypes.bool,
disabled: React.PropTypes.bool,
children: React.PropTypes.string,
},
displayName: "ToolbarButton",
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;
});

Просмотреть файл

@ -4,258 +4,264 @@
* 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) {
// Dependencies
const React = require("devtools/client/shared/vendor/react");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
const { StringRep } = require("devtools/client/shared/components/reps/string");
const DOM = React.DOM;
// Dependencies
const React = require("devtools/client/shared/vendor/react");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
const { StringRep } = require("devtools/client/shared/components/reps/string");
const DOM = React.DOM;
let uid = 0;
var uid = 0;
/**
* Renders a tree view with expandable/collapsible items.
*/
let TreeView = React.createClass({
propTypes: {
searchFilter: React.PropTypes.string,
data: React.PropTypes.any,
mode: React.PropTypes.string,
},
/**
* Renders a tree view with expandable/collapsible items.
*/
var TreeView = React.createClass({
displayName: "TreeView",
displayName: "TreeView",
getInitialState: function() {
return {
data: {},
searchFilter: null
};
},
// Rendering
render: function() {
var mode = this.props.mode;
var root = this.state.data;
var children = [];
if (Array.isArray(root)) {
for (var i=0; i<root.length; i++) {
var child = root[i];
children.push(TreeNode({
key: child.key,
data: child,
mode: mode,
searchFilter: this.state.searchFilter || this.props.searchFilter
}));
}
} else {
children.push(React.addons.createFragment(root));
}
return (
DOM.div({className: "domTable", cellPadding: 0, cellSpacing: 0},
children
)
);
},
// Data
componentDidMount: function() {
var members = initMembers(this.props.data, 0);
this.setState({data: members, searchFilter: this.props.searchFilter});
},
componentWillReceiveProps: function(nextProps) {
var updatedState = {
searchFilter: nextProps.searchFilter
};
if (this.props.data !== nextProps.data) {
updatedState.data = initMembers(nextProps.data, 0);
}
this.setState(updatedState);
}
});
/**
* Represents a node within the tree.
*/
var TreeNode = React.createFactory(React.createClass({
displayName: "TreeNode",
getInitialState: function() {
return { data: {}, searchFilter: null };
},
componentDidMount: function() {
this.setState({data: this.props.data});
},
render: function() {
var member = this.state.data;
var mode = this.props.mode;
var classNames = ["memberRow"];
classNames.push(member.type + "Row");
if (member.hasChildren) {
classNames.push("hasChildren");
}
if (member.open) {
classNames.push("opened");
}
if (!member.children) {
// Cropped strings are expandable, but they don't have children.
var isString = typeof(member.value) == "string";
if (member.hasChildren && !isString) {
member.children = initMembers(member.value);
} else {
member.children = [];
}
}
var children = [];
if (member.open && member.children.length) {
for (var i in member.children) {
var child = member.children[i];
children.push(TreeNode({
key: child.key,
data: child,
mode: mode,
searchFilter: this.state.searchFilter || this.props.searchFilter
}));
getInitialState: function() {
return {
data: {},
searchFilter: null
};
}
},
var filter = this.props.searchFilter || "";
var name = member.name || "";
var value = member.value || "";
// Data
// Filtering is case-insensitive
filter = filter.toLowerCase();
name = name.toLowerCase();
componentDidMount: function() {
let members = initMembers(this.props.data, 0);
this.setState({ // eslint-disable-line
data: members,
searchFilter:
this.props.searchFilter
});
},
if (filter && (name.indexOf(filter) < 0)) {
// Cache the stringify result, so the filtering is fast
// the next time.
if (!member.valueString) {
member.valueString = JSON.stringify(value).toLowerCase();
componentWillReceiveProps: function(nextProps) {
let updatedState = {
searchFilter: nextProps.searchFilter
};
if (this.props.data !== nextProps.data) {
updatedState.data = initMembers(nextProps.data, 0);
}
if (member.valueString && member.valueString.indexOf(filter) < 0) {
classNames.push("hidden");
}
}
this.setState(updatedState);
},
return (
DOM.div({className: classNames.join(" ")},
DOM.span({className: "memberLabelCell", onClick: this.onClick},
DOM.span({className: "memberIcon"}),
DOM.span({className: "memberLabel " + member.type + "Label"},
member.name)
),
DOM.span({className: "memberValueCell"},
DOM.span({},
Rep({
object: member.value,
mode: this.props.mode,
member: member
})
)
),
DOM.div({className: "memberChildren"},
// Rendering
render: function() {
let mode = this.props.mode;
let root = this.state.data;
let children = [];
if (Array.isArray(root)) {
for (let i = 0; i < root.length; i++) {
let child = root[i];
children.push(TreeNode({
key: child.key,
data: child,
mode: mode,
searchFilter: this.state.searchFilter || this.props.searchFilter
}));
}
} else {
children.push(React.addons.createFragment(root));
}
return (
DOM.div({className: "domTable", cellPadding: 0, cellSpacing: 0},
children
)
)
)
},
onClick: function(e) {
var member = this.state.data;
member.open = !member.open;
this.setState({data: member});
e.stopPropagation();
},
}));
// Helpers
function initMembers(parent) {
var members = getMembers(parent);
return members;
}
function getMembers(object) {
var members = [];
getObjectProperties(object, function(prop, value) {
var valueType = typeof(value);
var hasChildren = (valueType === "object" && hasProperties(value));
// Cropped strings are expandable, so the user can see the
// entire original value.
if (StringRep.isCropped(value)) {
hasChildren = true;
}
var type = getType(value);
var member = createMember(type, prop, value, hasChildren);
members.push(member);
);
},
});
return members;
}
/**
* Represents a node within the tree.
*/
let TreeNode = React.createFactory(React.createClass({
propTypes: {
searchFilter: React.PropTypes.string,
data: React.PropTypes.object,
mode: React.PropTypes.string,
},
function createMember(type, name, value, hasChildren) {
var member = {
name: name,
type: type,
rowClass: "memberRow-" + type,
open: "",
hasChildren: hasChildren,
value: value,
open: false,
key: uid++
};
displayName: "TreeNode",
return member;
}
getInitialState: function() {
return {
data: this.props.data,
searchFilter: null
};
},
function getObjectProperties(obj, callback) {
for (var p in obj) {
try {
callback.call(this, p, obj[p]);
}
catch (e) {
console.error(e)
onClick: function(e) {
let member = this.state.data;
member.open = !member.open;
this.setState({data: member});
e.stopPropagation();
},
render: function() {
let member = this.state.data;
let mode = this.props.mode;
let classNames = ["memberRow"];
classNames.push(member.type + "Row");
if (member.hasChildren) {
classNames.push("hasChildren");
}
if (member.open) {
classNames.push("opened");
}
if (!member.children) {
// Cropped strings are expandable, but they don't have children.
let isString = typeof (member.value) == "string";
if (member.hasChildren && !isString) {
member.children = initMembers(member.value);
} else {
member.children = [];
}
}
let children = [];
if (member.open && member.children.length) {
for (let i in member.children) {
let child = member.children[i];
children.push(TreeNode({
key: child.key,
data: child,
mode: mode,
searchFilter: this.state.searchFilter || this.props.searchFilter
}));
}
}
let filter = this.props.searchFilter || "";
let name = member.name || "";
let value = member.value || "";
// Filtering is case-insensitive
filter = filter.toLowerCase();
name = name.toLowerCase();
if (filter && (name.indexOf(filter) < 0)) {
// Cache the stringify result, so the filtering is fast
// the next time.
if (!member.valueString) {
member.valueString = JSON.stringify(value).toLowerCase();
}
if (member.valueString && member.valueString.indexOf(filter) < 0) {
classNames.push("hidden");
}
}
return (
DOM.div({className: classNames.join(" ")},
DOM.span({className: "memberLabelCell", onClick: this.onClick},
DOM.span({className: "memberIcon"}),
DOM.span({className: "memberLabel " + member.type + "Label"},
member.name)
),
DOM.span({className: "memberValueCell"},
DOM.span({},
Rep({
object: member.value,
mode: this.props.mode,
member: member
})
)
),
DOM.div({className: "memberChildren"},
children
)
)
);
},
}));
// Helpers
function initMembers(parent) {
let members = getMembers(parent);
return members;
}
function getMembers(object) {
let members = [];
getObjectProperties(object, function(prop, value) {
let valueType = typeof (value);
let hasChildren = (valueType === "object" && hasProperties(value));
// Cropped strings are expandable, so the user can see the
// entire original value.
if (StringRep.isCropped(value)) {
hasChildren = true;
}
let type = getType(value);
let member = createMember(type, prop, value, hasChildren);
members.push(member);
});
return members;
}
function createMember(type, name, value, hasChildren) {
let member = {
name: name,
type: type,
rowClass: "memberRow-" + type,
hasChildren: hasChildren,
value: value,
open: false,
key: uid++
};
return member;
}
function getObjectProperties(obj, callback) {
for (let p in obj) {
try {
callback.call(this, p, obj[p]);
} catch (e) {
// Ignore
}
}
}
}
function hasProperties(obj) {
if (typeof(obj) == "string") {
return false;
}
try {
for (var name in obj) {
return true;
function hasProperties(obj) {
if (typeof (obj) == "string") {
return false;
}
}
catch (exc) {
return Object.keys(obj).length > 1;
}
return false;
}
function getType(object) {
// A type provider (or a decorator) should be used here.
return "dom";
}
function getType(object) {
// A type provider (or a decorator) should be used here.
return "dom";
}
// Exports from this module
exports.TreeView = TreeView;
// Exports from this module
exports.TreeView = TreeView;
});

Просмотреть файл

@ -7,46 +7,49 @@
"use strict";
define(function(require, exports, module) {
const React = require("devtools/client/shared/vendor/react");
const React = require("devtools/client/shared/vendor/react");
const DOM = React.DOM;
const DOM = React.DOM;
// For smooth incremental searching (in case the user is typing quickly).
const searchDelay = 250;
// 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 = React.createClass({
propTypes: {
actions: React.PropTypes.object,
},
/**
* This object represents a search box located at the
* top right corner of the application.
*/
var SearchBox = React.createClass({
displayName: "SearchBox",
displayName: "SearchBox",
render: function() {
return (
DOM.input({className: "searchBox",
placeholder: Locale.$STR("jsonViewer.filterJSON"),
onChange: this.onSearch})
)
},
onSearch: function(event) {
let searchBox = event.target;
let win = searchBox.ownerDocument.defaultView;
onSearch: function(event) {
var searchBox = event.target;
var win = searchBox.ownerDocument.defaultView;
if (this.searchTimeout) {
win.clearTimeout(this.searchTimeout);
}
if (this.searchTimeout) {
win.clearTimeout(this.searchTimeout);
}
let callback = this.doSearch.bind(this, searchBox);
this.searchTimeout = win.setTimeout(callback, searchDelay);
},
var callback = this.doSearch.bind(this, searchBox);
this.searchTimeout = win.setTimeout(callback, searchDelay);
},
doSearch: function(searchBox) {
this.props.actions.onSearch(searchBox.value);
},
doSearch: function(searchBox) {
this.props.actions.onSearch(searchBox.value);
}
});
// Exports from this module
exports.SearchBox = SearchBox;
render: function() {
return (
DOM.input({className: "searchBox",
placeholder: Locale.$STR("jsonViewer.filterJSON"),
onChange: this.onSearch})
);
},
});
// Exports from this module
exports.SearchBox = SearchBox;
});

Просмотреть файл

@ -4,76 +4,92 @@
* 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 { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
const DOM = React.DOM;
const React = require("devtools/client/shared/vendor/react");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
const DOM = React.DOM;
/**
* This template represents the 'Raw Data' panel displaying
* JSON as a text received from the server.
*/
let TextPanel = React.createClass({
propTypes: {
actions: React.PropTypes.object,
data: React.PropTypes.string
},
/**
* This template represents the 'Raw Data' panel displaying
* JSON as a text received from the server.
*/
var TextPanel = React.createClass({
displayName: "TextPanel",
displayName: "TextPanel",
getInitialState: function() {
return {};
},
getInitialState: function() {
return {};
},
render: function() {
return (
DOM.div({className: "textPanelBox"},
TextToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
DOM.pre({className: "data"},
this.props.data
render: function() {
return (
DOM.div({className: "textPanelBox"},
TextToolbar({actions: this.props.actions}),
DOM.div({className: "panelContent"},
DOM.pre({className: "data"},
this.props.data
)
)
)
)
);
}
});
);
}
});
/**
* This object represents a toolbar displayed within the
* 'Raw Data' panel.
*/
var TextToolbar = React.createFactory(React.createClass({
displayName: "TextToolbar",
/**
* This object represents a toolbar displayed within the
* 'Raw Data' panel.
*/
let TextToolbar = React.createFactory(React.createClass({
propTypes: {
actions: React.PropTypes.object,
},
render: function() {
return (
Toolbar({},
ToolbarButton({className: "btn prettyprint",onClick: this.onPrettify},
Locale.$STR("jsonViewer.PrettyPrint")
),
ToolbarButton({className: "btn save", onClick: this.onSave},
Locale.$STR("jsonViewer.Save")
),
ToolbarButton({className: "btn copy", onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
displayName: "TextToolbar",
// 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 prettyprint",
onClick: this.onPrettify},
Locale.$STR("jsonViewer.PrettyPrint")
),
ToolbarButton({
className: "btn save",
onClick: this.onSave},
Locale.$STR("jsonViewer.Save")
),
ToolbarButton({
className: "btn copy",
onClick: this.onCopy},
Locale.$STR("jsonViewer.Copy")
)
)
)
)
},
);
},
}));
// Commands
onPrettify: function(event) {
this.props.actions.onPrettify();
},
onSave: function(event) {
this.props.actions.onSaveJson();
},
onCopy: function(event) {
this.props.actions.onCopyJson();
},
}));
// Exports from this module
exports.TextPanel = TextPanel;
// Exports from this module
exports.TextPanel = TextPanel;
});

Просмотреть файл

@ -19,21 +19,21 @@ loader.lazyRequireGetter(this, "NetworkHelper",
loader.lazyRequireGetter(this, "JsonViewUtils",
"devtools/client/jsonview/utils");
const childProcessMessageManager =
Cc["@mozilla.org/childprocessmessagemanager;1"].
getService(Ci.nsISyncMessageSender);
Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
// Amount of space that will be allocated for the stream's backing-store.
// Must be power of 2. Used to copy the data stream in onStopRequest.
const SEGMENT_SIZE = Math.pow(2, 17);
const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
const CONTRACT_ID = "@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*";
const CONTRACT_ID = "@mozilla.org/streamconv;1?from=" +
JSON_VIEW_MIME_TYPE + "&to=*/*";
const CLASS_ID = "{d8c9acee-dec5-11e4-8c75-1681e6b88ec1}";
// Localization
var jsonViewStrings = Services.strings.createBundle(
let jsonViewStrings = Services.strings.createBundle(
"chrome://devtools/locale/jsonview.properties");
/**
@ -43,7 +43,7 @@ var jsonViewStrings = Services.strings.createBundle(
*
* Inspired by JSON View: https://github.com/bhollis/jsonview/
*/
var Converter = Class({
let Converter = Class({
extends: Unknown,
interfaces: [
@ -59,52 +59,55 @@ var Converter = Class({
/**
* This component works as such:
* 1. asyncConvertData captures the listener
* 2. onStartRequest fires, initializes stuff, modifies the listener to match our output type
* 2. onStartRequest fires, initializes stuff, modifies the listener
* to match our output type
* 3. onDataAvailable transcodes the data into a UTF-8 string
* 4. onStopRequest gets the collected data and converts it, spits it to the listener
* 5. convert does nothing, it's just the synchronous version of asyncConvertData
* 4. onStopRequest gets the collected data and converts it,
* spits it to the listener
* 5. convert does nothing, it's just the synchronous version
* of asyncConvertData
*/
convert: function(aFromStream, aFromType, aToType, aCtxt) {
return aFromStream;
convert: function(fromStream, fromType, toType, ctx) {
return fromStream;
},
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
this.listener = aListener;
asyncConvertData: function(fromType, toType, listener, ctx) {
this.listener = listener;
},
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
onDataAvailable: function(request, context, inputStream, offset, count) {
// From https://developer.mozilla.org/en/Reading_textual_data
var is = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
is.init(aInputStream, this.charset, -1,
let is = Cc["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Ci.nsIConverterInputStream);
is.init(inputStream, this.charset, -1,
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
// Seed it with something positive
var bytesRead = 1;
while (aCount) {
var str = {};
var bytesRead = is.readString(aCount, str);
while (count) {
let str = {};
let bytesRead = is.readString(count, str);
if (!bytesRead) {
break;
}
aCount -= bytesRead;
count -= bytesRead;
this.data += str.value;
}
},
onStartRequest: function(aRequest, aContext) {
onStartRequest: function(request, context) {
this.data = "";
this.uri = aRequest.QueryInterface(Ci.nsIChannel).URI.spec;
this.uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
// Sets the charset if it is available. (For documents loaded from the
// filesystem, this is not set.)
this.charset = aRequest.QueryInterface(Ci.nsIChannel).contentCharset || 'UTF-8';
this.charset =
request.QueryInterface(Ci.nsIChannel).contentCharset || "UTF-8";
this.channel = aRequest;
this.channel = request;
this.channel.contentType = "text/html";
this.channel.contentCharset = "UTF-8";
this.listener.onStartRequest(this.channel, aContext);
this.listener.onStartRequest(this.channel, context);
},
/**
@ -115,13 +118,13 @@ var Converter = Class({
* 3. Convert that to HTML? Or XUL?
* 4. Spit it back out at the listener
*/
onStopRequest: function(aRequest, aContext, aStatusCode) {
onStopRequest: function(request, context, statusCode) {
let headers = {
response: [],
request: []
}
};
let win = NetworkHelper.getWindowForRequest(aRequest);
let win = NetworkHelper.getWindowForRequest(request);
let Locale = {
$STR: key => {
@ -139,18 +142,18 @@ var Converter = Class({
Cu.exportFunction(this.postChromeMessage.bind(this), win, {
defineAs: "postChromeMessage"
});
})
});
// The request doesn't have to be always nsIHttpChannel
// (e.g. in case of data: URLs)
if (aRequest instanceof Ci.nsIHttpChannel) {
aRequest.visitResponseHeaders({
if (request instanceof Ci.nsIHttpChannel) {
request.visitResponseHeaders({
visitHeader: function(name, value) {
headers.response.push({name: name, value: value});
}
});
aRequest.visitRequestHeaders({
request.visitRequestHeaders({
visitHeader: function(name, value) {
headers.request.push({name: name, value: value});
}
@ -167,11 +170,13 @@ var Converter = Class({
outputDoc = this.toErrorPage(e, this.data, this.uri);
}
var storage = Cc["@mozilla.org/storagestream;1"].createInstance(Ci.nsIStorageStream);
storage.init(SEGMENT_SIZE, 0xffffffff, null);
var out = storage.getOutputStream(0);
let storage = Cc["@mozilla.org/storagestream;1"]
.createInstance(Ci.nsIStorageStream);
var binout = Cc["@mozilla.org/binaryoutputstream;1"]
storage.init(SEGMENT_SIZE, 0xffffffff, null);
let out = storage.getOutputStream(0);
let binout = Cc["@mozilla.org/binaryoutputstream;1"]
.createInstance(Ci.nsIBinaryOutputStream);
binout.setOutputStream(out);
@ -179,100 +184,104 @@ var Converter = Class({
binout.close();
// We need to trim 4 bytes off the front (this could be underlying bug).
var trunc = 4;
var instream = storage.newInputStream(trunc);
let trunc = 4;
let instream = storage.newInputStream(trunc);
// Pass the data to the main content listener
this.listener.onDataAvailable(this.channel, aContext, instream, 0,
this.listener.onDataAvailable(this.channel, context, instream, 0,
instream.available());
this.listener.onStopRequest(this.channel, aContext, aStatusCode);
this.listener.onStopRequest(this.channel, context, statusCode);
this.listener = null;
},
htmlEncode: function(t) {
return t !== null ? t.toString().replace(/&/g,"&amp;").
replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;") : '';
return t !== null ? t.toString()
.replace(/&/g, "&amp;")
.replace(/"/g, "&quot;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;") : "";
},
toHTML: function(json, headers, title) {
var themeClassName = "theme-" + JsonViewUtils.getCurrentTheme();
var clientBaseUrl = "resource://devtools/client/";
var baseUrl = clientBaseUrl + "jsonview/";
var themeVarsUrl = clientBaseUrl + "themes/variables.css";
let themeClassName = "theme-" + JsonViewUtils.getCurrentTheme();
let clientBaseUrl = "resource://devtools/client/";
let baseUrl = clientBaseUrl + "jsonview/";
let themeVarsUrl = clientBaseUrl + "themes/variables.css";
return '<!DOCTYPE html>\n' +
'<html class="' + themeClassName + '">' +
'<head><title>' + this.htmlEncode(title) + '</title>' +
'<base href="' + this.htmlEncode(baseUrl) + '">' +
'<link rel="stylesheet" type="text/css" href="' + themeVarsUrl + '">' +
'<link rel="stylesheet" type="text/css" href="css/main.css">' +
'<script data-main="viewer-config" src="lib/require.js"></script>' +
'</head><body>' +
'<div id="content"></div>' +
'<div id="json">' + this.htmlEncode(json) + '</div>' +
'<div id="headers">' + this.htmlEncode(headers) + '</div>' +
'</body></html>';
return "<!DOCTYPE html>\n" +
"<html class=\"" + themeClassName + "\">" +
"<head><title>" + this.htmlEncode(title) + "</title>" +
"<base href=\"" + this.htmlEncode(baseUrl) + "\">" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" +
themeVarsUrl + "\">" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"css/main.css\">" +
"<script data-main=\"viewer-config\" src=\"lib/require.js\"></script>" +
"</head><body>" +
"<div id=\"content\"></div>" +
"<div id=\"json\">" + this.htmlEncode(json) + "</div>" +
"<div id=\"headers\">" + this.htmlEncode(headers) + "</div>" +
"</body></html>";
},
toErrorPage: function(error, data, uri) {
// Escape unicode nulls
data = data.replace("\u0000", "\uFFFD");
var errorInfo = error + "";
let errorInfo = error + "";
var output = '<div id="error">' + _('errorParsing')
let output = "<div id=\"error\">" + "error parsing";
if (errorInfo.message) {
output += '<div class="errormessage">' + errorInfo.message + '</div>';
output += "<div class=\"errormessage\">" + errorInfo.message + "</div>";
}
output += '</div><div id="json">' + this.highlightError(data,
errorInfo.line, errorInfo.column) + '</div>';
output += "</div><div id=\"json\">" + this.highlightError(data,
errorInfo.line, errorInfo.column) + "</div>";
return '<!DOCTYPE html>\n' +
'<html><head><title>' + this.htmlEncode(uri + ' - Error') + '</title>' +
'<base href="' + this.htmlEncode(self.data.url()) + '">' +
'</head><body>' +
return "<!DOCTYPE html>\n" +
"<html><head><title>" + this.htmlEncode(uri + " - Error") + "</title>" +
"<base href=\"" + this.htmlEncode(this.data.url()) + "\">" +
"</head><body>" +
output +
'</body></html>';
"</body></html>";
},
// Chrome <-> Content communication
postChromeMessage: function(type, args, objects) {
var value = args;
let value = args;
switch (type) {
case "copy":
Clipboard.set(value, "text");
break;
case "copy":
Clipboard.set(value, "text");
break;
case "copy-headers":
this.copyHeaders(value);
break;
case "copy-headers":
this.copyHeaders(value);
break;
case "save":
childProcessMessageManager.sendAsyncMessage(
"devtools:jsonview:save", value);
case "save":
childProcessMessageManager.sendAsyncMessage(
"devtools:jsonview:save", value);
}
},
copyHeaders: function(headers) {
var value = "";
var eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";
let value = "";
let eol = (Services.appinfo.OS !== "WINNT") ? "\n" : "\r\n";
var responseHeaders = headers.response;
for (var i=0; i<responseHeaders.length; i++) {
var header = responseHeaders[i];
let responseHeaders = headers.response;
for (let i = 0; i < responseHeaders.length; i++) {
let header = responseHeaders[i];
value += header.name + ": " + header.value + eol;
}
value += eol;
var requestHeaders = headers.request;
for (var i=0; i<requestHeaders.length; i++) {
var header = requestHeaders[i];
let requestHeaders = headers.request;
for (let i = 0; i < requestHeaders.length; i++) {
let header = requestHeaders[i];
value += header.name + ": " + header.value + eol;
}
@ -281,7 +290,7 @@ var Converter = Class({
});
// Stream converter component definition
var service = xpcom.Service({
let service = xpcom.Service({
id: components.ID(CLASS_ID),
contract: CONTRACT_ID,
Component: Converter,
@ -308,4 +317,4 @@ function unregister() {
exports.JsonViewService = {
register: register,
unregister: unregister
}
};

Просмотреть файл

@ -7,8 +7,6 @@
"use strict";
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
@ -61,7 +59,7 @@ ConverterObserver.prototype = {
case "nsPref:changed":
this.onPrefChanged();
break;
};
}
},
onShutdown: function() {

Просмотреть файл

@ -11,10 +11,11 @@ const xpcom = require("sdk/platform/xpcom");
const {Unknown} = require("sdk/platform/xpcom");
const {Class} = require("sdk/core/heritage");
const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
const categoryManager = Cc["@mozilla.org/categorymanager;1"]
.getService(Ci.nsICategoryManager);
loader.lazyRequireGetter(this, "NetworkHelper",
"devtools/shared/webconsole/network-helper");
"devtools/shared/webconsole/network-helper");
// Constants
const JSON_TYPE = "application/json";
@ -43,15 +44,16 @@ var Sniffer = Class({
return this;
},
getMIMETypeFromContent: function(aRequest, aData, aLength) {
getMIMETypeFromContent: function(request, data, length) {
// JSON View is enabled only for top level loads only.
if (!NetworkHelper.isTopLevelLoad(aRequest)) {
if (!NetworkHelper.isTopLevelLoad(request)) {
return "";
}
if (aRequest instanceof Ci.nsIChannel) {
if (request instanceof Ci.nsIChannel) {
try {
if (aRequest.contentDisposition == Ci.nsIChannel.DISPOSITION_ATTACHMENT) {
if (request.contentDisposition ==
Ci.nsIChannel.DISPOSITION_ATTACHMENT) {
return "";
}
} catch (e) {
@ -60,7 +62,7 @@ var Sniffer = Class({
// Check the response content type and if it's application/json
// change it to new internal type consumed by JSON View.
if (aRequest.contentType == JSON_TYPE) {
if (request.contentType == JSON_TYPE) {
return JSON_VIEW_MIME_TYPE;
}
}
@ -91,7 +93,7 @@ function register() {
function unregister() {
if (xpcom.isRegistered(service)) {
categoryManager.deleteCategoryEntry(CONTENT_SNIFFER_CATEGORY,
JSON_VIEW_TYPE, false)
JSON_VIEW_TYPE, false);
xpcom.unregister(service);
return true;
}
@ -101,4 +103,4 @@ function unregister() {
exports.JsonViewSniffer = {
register: register,
unregister: unregister
}
};

Просмотреть файл

@ -3,96 +3,96 @@
/* 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/. */
/* global postChromeMessage */
"use strict";
define(function(require, exports, module) {
// ReactJS
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { MainTabbedArea } = createFactories(require("./components/main-tabbed-area"));
// ReactJS
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { MainTabbedArea } = createFactories(require("./components/main-tabbed-area"));
const json = document.getElementById("json");
const headers = document.getElementById("headers");
const json = document.getElementById("json");
const headers = document.getElementById("headers");
let jsonData;
var jsonData;
try {
jsonData = JSON.parse(json.textContent);
} catch (err) {
jsonData = err + "";
}
try {
jsonData = JSON.parse(json.textContent);
} catch (err) {
jsonData = err + "";
}
// Application state object.
let input = {
jsonText: json.textContent,
jsonPretty: null,
json: jsonData,
headers: JSON.parse(headers.textContent),
tabActive: 1,
prettified: false
};
// Application state object.
var input = {
jsonText: json.textContent,
jsonPretty : null,
json: jsonData,
headers: JSON.parse(headers.textContent),
tabActive: 1,
prettified: false
}
json.remove();
headers.remove();
json.remove();
headers.remove();
/**
* Application actions/commands. This list implements all commands
* available for the JSON viewer.
*/
input.actions = {
onCopyJson: function() {
let value = input.prettified ? input.jsonPretty : input.jsonText;
postChromeMessage("copy", value);
},
/**
* Application actions/commands. This list implements all commands
* available for the JSON viewer.
*/
input.actions = {
onCopyJson: function() {
var value = input.prettified ? input.jsonPretty : input.jsonText;
postChromeMessage("copy", value);
},
onSaveJson: function() {
let value = input.prettified ? input.jsonPretty : input.jsonText;
postChromeMessage("save", value);
},
onSaveJson: function() {
var value = input.prettified ? input.jsonPretty : input.jsonText;
postChromeMessage("save", value);
},
onCopyHeaders: function() {
postChromeMessage("copy-headers", input.headers);
},
onCopyHeaders: function() {
postChromeMessage("copy-headers", input.headers);
},
onSearch: function(value) {
theApp.setState({searchFilter: value});
},
onSearch: function(value) {
theApp.setState({searchFilter: value});
},
onPrettify: function(data) {
if (input.prettified) {
theApp.setState({jsonText: input.jsonText});
} else {
if (!input.jsonPretty) {
input.jsonPretty = JSON.stringify(jsonData, null, " ");
onPrettify: function(data) {
if (input.prettified) {
theApp.setState({jsonText: input.jsonText});
} else {
if (!input.jsonPretty) {
input.jsonPretty = JSON.stringify(jsonData, null, " ");
}
theApp.setState({jsonText: input.jsonPretty});
}
theApp.setState({jsonText: input.jsonPretty});
}
input.prettified = !input.prettified;
},
}
input.prettified = !input.prettified;
},
};
/**
* Render the main application component. It's the main tab bar displayed
* at the top of the window. This component also represents ReacJS root.
*/
var content = document.getElementById("content");
var theApp = ReactDOM.render(MainTabbedArea(input), content);
/**
* Render the main application component. It's the main tab bar displayed
* at the top of the window. This component also represents ReacJS root.
*/
let content = document.getElementById("content");
let theApp = ReactDOM.render(MainTabbedArea(input), content);
var onResize = event => {
window.document.body.style.height = window.innerHeight + "px";
window.document.body.style.width = window.innerWidth + "px";
}
let onResize = event => {
window.document.body.style.height = window.innerHeight + "px";
window.document.body.style.width = window.innerWidth + "px";
};
window.addEventListener("resize", onResize);
onResize();
window.addEventListener("resize", onResize);
onResize();
// Send notification event to the window. Can be useful for
// tests as well as extensions.
var event = new CustomEvent("JSONViewInitialized", {});
window.jsonViewInitialized = true;
window.dispatchEvent(event);
// End of json-viewer.js
// Send notification event to the window. Can be useful for
// tests as well as extensions.
let event = new CustomEvent("JSONViewInitialized", {});
window.jsonViewInitialized = true;
window.dispatchEvent(event);
});

Просмотреть файл

@ -3,13 +3,14 @@
/* 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/. */
/* globals JsonViewUtils*/
"use strict";
const {Cu, Ci, Cc} = require("chrome");
const { Cu } = require("chrome");
const Services = require("Services");
const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
XPCOMUtils.defineLazyGetter(this, "JsonViewService", function() {
return require("devtools/client/jsonview/utils");

Просмотреть файл

@ -10,7 +10,7 @@ const TEST_JSON_URL = URL_ROOT + "valid_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
// Select the RawData tab
yield selectJsonViewContentTab("headers");
@ -22,7 +22,7 @@ add_task(function* () {
let text = yield getElementText(".headersPanelBox .netInfoHeadersTable");
isnot(text, "", "Headers text must not be empty");
let browser = gBrowser.selectedBrowser
let browser = gBrowser.selectedBrowser;
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {

Просмотреть файл

@ -10,7 +10,7 @@ const TEST_JSON_URL = URL_ROOT + "simple_json.json";
add_task(function* () {
info("Test copy JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
let countBefore = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(countBefore == 1, "There must be one row");
@ -20,7 +20,7 @@ add_task(function* () {
// Verify JSON copy into the clipboard.
let value = "{\"name\": \"value\"}\n";
let browser = gBrowser.selectedBrowser
let browser = gBrowser.selectedBrowser;
let selector = ".jsonPanelBox .toolbar button.copy";
yield waitForClipboardPromise(function setup() {
BrowserTestUtils.synthesizeMouseAtCenter(selector, {}, browser);

Просмотреть файл

@ -13,7 +13,7 @@ let prettyJson = "{\n \"name\": \"value\"\n}";
add_task(function* () {
info("Test copy raw data started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
// Select the RawData tab
yield selectJsonViewContentTab("rawdata");
@ -22,7 +22,7 @@ add_task(function* () {
let text = yield getElementText(".textPanelBox .data");
is(text, jsonText, "Proper JSON must be displayed in DOM");
let browser = gBrowser.selectedBrowser
let browser = gBrowser.selectedBrowser;
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {
@ -38,7 +38,8 @@ add_task(function* () {
let prettyText = yield getElementText(".textPanelBox .data");
prettyText = normalizeNewLines(prettyText);
ok(prettyText.startsWith(prettyJson), "Pretty printed JSON must be displayed");
ok(prettyText.startsWith(prettyJson),
"Pretty printed JSON must be displayed");
// Verify JSON copy into the clipboard.
yield waitForClipboardPromise(function setup() {

Просмотреть файл

@ -10,7 +10,7 @@ const TEST_JSON_URL = URL_ROOT + "array_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
let count = yield getElementCount(".jsonPanelBox .domTable .memberRow");
is(count, 3, "There must be three rows");
@ -22,6 +22,7 @@ add_task(function* () {
// The filtering is done asynchronously so, we need to wait.
yield waitForFilter();
let hiddenCount = yield getElementCount(".jsonPanelBox .domTable .memberRow.hidden");
let hiddenCount = yield getElementCount(
".jsonPanelBox .domTable .memberRow.hidden");
is(hiddenCount, 2, "There must be two hidden rows");
});

Просмотреть файл

@ -10,7 +10,7 @@ const TEST_JSON_URL = URL_ROOT + "invalid_json.json";
add_task(function* () {
info("Test invalid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
let count = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(count == 0, "There must be no row");

Просмотреть файл

@ -10,7 +10,7 @@ const TEST_JSON_URL = URL_ROOT + "valid_json.json";
add_task(function* () {
info("Test valid JSON started");
let tab = yield addJsonViewTab(TEST_JSON_URL);
yield addJsonViewTab(TEST_JSON_URL);
let countBefore = yield getElementCount(".jsonPanelBox .domTable .memberRow");
ok(countBefore == 1, "There must be one row");

Просмотреть файл

@ -6,7 +6,8 @@
/* globals Services, sendAsyncMessage, addMessageListener */
// XXX Some helper API could go to testing/mochitest/tests/SimpleTest/AsyncContentUtils.js
// XXX Some helper API could go to:
// testing/mochitest/tests/SimpleTest/AsyncContentUtils.js
// (or at least to share test API in devtools)
// Set up a dummy environment so that EventUtils works. We need to be careful to
@ -15,8 +16,8 @@
let EventUtils = {};
EventUtils.window = content;
EventUtils.parent = EventUtils.window;
EventUtils._EU_Ci = Components.interfaces;
EventUtils._EU_Cc = Components.classes;
EventUtils._EU_Ci = Components.interfaces; // eslint-disable-line
EventUtils._EU_Cc = Components.classes; // eslint-disable-line
EventUtils.navigator = content.navigator;
EventUtils.KeyboardEvent = content.KeyboardEvent;
@ -80,7 +81,7 @@ addMessageListener("Test:JsonView:WaitForFilter", function(msg) {
}
// Wait till the first row has 'hidden' class set.
var observer = new content.MutationObserver(function(mutations) {
let observer = new content.MutationObserver(function(mutations) {
for (let i = 0; i < mutations.length; i++) {
let mutation = mutations[i];
if (mutation.attributeName == "class") {

Просмотреть файл

@ -1,6 +1,9 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
/* import-globals-from ../../framework/test/shared-head.js */
/* import-globals-from ../../framework/test/head.js */
"use strict";
@ -81,7 +84,8 @@ function getElementCount(selector) {
selector: selector
};
return executeInContent("Test:JsonView:GetElementCount", data).then(result => {
return executeInContent("Test:JsonView:GetElementCount", data)
.then(result => {
return result.count;
});
}
@ -93,7 +97,8 @@ function getElementText(selector) {
selector: selector
};
return executeInContent("Test:JsonView:GetElementText", data).then(result => {
return executeInContent("Test:JsonView:GetElementText", data)
.then(result => {
return result.text;
});
}
@ -125,9 +130,9 @@ function sendString(str, selector) {
return executeInContent("Test:JsonView:SendString", data);
}
function waitForTime(aDelay) {
function waitForTime(delay) {
let deferred = promise.defer();
setTimeout(deferred.resolve, aDelay);
setTimeout(deferred.resolve, delay);
return deferred.promise;
}

Просмотреть файл

@ -6,46 +6,46 @@
"use strict";
const {Cu, Cc, Ci} = require("chrome");
const { Cu, Cc, Ci } = require("chrome");
const Services = require("Services");
const {getMostRecentBrowserWindow} = require("sdk/window/utils");
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const OPEN_FLAGS = {
RDONLY: parseInt("0x01"),
WRONLY: parseInt("0x02"),
CREATE_FILE: parseInt("0x08"),
APPEND: parseInt("0x10"),
TRUNCATE: parseInt("0x20"),
EXCL: parseInt("0x80")
RDONLY: parseInt("0x01", 16),
WRONLY: parseInt("0x02", 16),
CREATE_FILE: parseInt("0x08", 16),
APPEND: parseInt("0x10", 16),
TRUNCATE: parseInt("0x20", 16),
EXCL: parseInt("0x80", 16)
};
/**
* Open File Save As dialog and let the user to pick proper file location.
*/
exports.getTargetFile = function() {
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
var win = getMostRecentBrowserWindow();
let win = getMostRecentBrowserWindow();
fp.init(win, null, Ci.nsIFilePicker.modeSave);
fp.appendFilter("JSON Files","*.json; *.jsonp;");
fp.appendFilter("JSON Files", "*.json; *.jsonp;");
fp.appendFilters(Ci.nsIFilePicker.filterText);
fp.appendFilters(Ci.nsIFilePicker.filterAll);
fp.filterIndex = 0;
var rv = fp.show();
let rv = fp.show();
if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) {
return fp.file;
}
return null;
}
};
/**
* Save JSON to a file
*/
exports.saveToFile = function(file, jsonString) {
var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
// write, create, truncate
let openFlags = OPEN_FLAGS.WRONLY | OPEN_FLAGS.CREATE_FILE |
@ -54,15 +54,15 @@ exports.saveToFile = function(file, jsonString) {
let permFlags = parseInt("0666", 8);
foStream.init(file, openFlags, permFlags, 0);
var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Ci.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
// The entire jsonString can be huge so, write the data in chunks.
var chunkLength = 1024*1204;
for (var i=0; i<=jsonString.length; i++) {
var data = jsonString.substr(i, chunkLength+1);
let chunkLength = 1024 * 1204;
for (let i = 0; i <= jsonString.length; i++) {
let data = jsonString.substr(i, chunkLength + 1);
if (data) {
converter.writeString(data);
}
@ -71,31 +71,31 @@ exports.saveToFile = function(file, jsonString) {
// this closes foStream
converter.close();
}
};
/**
* Get the current theme from preferences.
*/
exports.getCurrentTheme = function() {
return Services.prefs.getCharPref("devtools.theme");
}
};
/**
* Export given object into the target window scope.
*/
exports.exportIntoContentScope = function(win, obj, defineAs) {
var clone = Cu.createObjectIn(win, {
let clone = Cu.createObjectIn(win, {
defineAs: defineAs
});
var props = Object.getOwnPropertyNames(obj);
for (var i=0; i<props.length; i++) {
var propName = props[i];
var propValue = obj[propName];
let props = Object.getOwnPropertyNames(obj);
for (let i = 0; i < props.length; i++) {
let propName = props[i];
let propValue = obj[propName];
if (typeof propValue == "function") {
Cu.exportFunction(propValue, clone, {
defineAs: propName
});
}
}
}
};

Просмотреть файл

@ -3,6 +3,9 @@
/* 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/. */
/* global requirejs */
"use strict";
/**
* RequireJS configuration for JSON Viewer.