зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1257173 - Make JSON viewer code eslint clean; r=linclark
MozReview-Commit-ID: LUzs6Pb4c3z --HG-- extra : rebase_source : 72554e907dd29bb5ab60ed8b0b4c29facf1d4a85
This commit is contained in:
Родитель
da16b06600
Коммит
781ed5c343
|
@ -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,"&").
|
||||
replace(/"/g,""").replace(/</g,"<").replace(/>/g,">") : '';
|
||||
return t !== null ? t.toString()
|
||||
.replace(/&/g, "&")
|
||||
.replace(/"/g, """)
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">") : "";
|
||||
},
|
||||
|
||||
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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче