summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/components/Editor/Footer.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/components/Editor/Footer.js')
-rw-r--r--devtools/client/debugger/src/components/Editor/Footer.js302
1 files changed, 302 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/components/Editor/Footer.js b/devtools/client/debugger/src/components/Editor/Footer.js
new file mode 100644
index 0000000000..ea9acbc6f6
--- /dev/null
+++ b/devtools/client/debugger/src/components/Editor/Footer.js
@@ -0,0 +1,302 @@
+/* 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/>. */
+
+import React, { PureComponent } from "react";
+import PropTypes from "prop-types";
+import { connect } from "../../utils/connect";
+import { createLocation } from "../../utils/location";
+import actions from "../../actions";
+import {
+ getSelectedSource,
+ getSelectedLocation,
+ getSelectedSourceTextContent,
+ getPrettySource,
+ getPaneCollapse,
+ getContext,
+ getGeneratedSource,
+ isSourceBlackBoxed,
+ canPrettyPrintSource,
+ getPrettyPrintMessage,
+ isSourceOnSourceMapIgnoreList,
+ isSourceMapIgnoreListEnabled,
+} from "../../selectors";
+
+import { isPretty, getFilename, shouldBlackbox } from "../../utils/source";
+
+import { PaneToggleButton } from "../shared/Button";
+import AccessibleImage from "../shared/AccessibleImage";
+
+const classnames = require("devtools/client/shared/classnames.js");
+
+import "./Footer.css";
+
+class SourceFooter extends PureComponent {
+ constructor() {
+ super();
+
+ this.state = { cursorPosition: { line: 0, column: 0 } };
+ }
+
+ static get propTypes() {
+ return {
+ canPrettyPrint: PropTypes.bool.isRequired,
+ prettyPrintMessage: PropTypes.string.isRequired,
+ cx: PropTypes.object.isRequired,
+ endPanelCollapsed: PropTypes.bool.isRequired,
+ horizontal: PropTypes.bool.isRequired,
+ jumpToMappedLocation: PropTypes.func.isRequired,
+ mappedSource: PropTypes.object,
+ selectedSource: PropTypes.object,
+ isSelectedSourceBlackBoxed: PropTypes.bool.isRequired,
+ sourceLoaded: PropTypes.bool.isRequired,
+ toggleBlackBox: PropTypes.func.isRequired,
+ togglePaneCollapse: PropTypes.func.isRequired,
+ togglePrettyPrint: PropTypes.func.isRequired,
+ isSourceOnIgnoreList: PropTypes.bool.isRequired,
+ };
+ }
+
+ componentDidUpdate() {
+ const eventDoc = document.querySelector(".editor-mount .CodeMirror");
+ // querySelector can return null
+ if (eventDoc) {
+ this.toggleCodeMirror(eventDoc, true);
+ }
+ }
+
+ componentWillUnmount() {
+ const eventDoc = document.querySelector(".editor-mount .CodeMirror");
+
+ if (eventDoc) {
+ this.toggleCodeMirror(eventDoc, false);
+ }
+ }
+
+ toggleCodeMirror(eventDoc, toggle) {
+ if (toggle === true) {
+ eventDoc.CodeMirror.on("cursorActivity", this.onCursorChange);
+ } else {
+ eventDoc.CodeMirror.off("cursorActivity", this.onCursorChange);
+ }
+ }
+
+ prettyPrintButton() {
+ const {
+ cx,
+ selectedSource,
+ canPrettyPrint,
+ prettyPrintMessage,
+ togglePrettyPrint,
+ sourceLoaded,
+ } = this.props;
+
+ if (!selectedSource) {
+ return null;
+ }
+
+ if (!sourceLoaded && selectedSource.isPrettyPrinted) {
+ return (
+ <div className="action" key="pretty-loader">
+ <AccessibleImage className="loader spin" />
+ </div>
+ );
+ }
+
+ const type = "prettyPrint";
+ return (
+ <button
+ onClick={() => {
+ if (!canPrettyPrint) {
+ return;
+ }
+ togglePrettyPrint(cx, selectedSource.id);
+ }}
+ className={classnames("action", type, {
+ active: sourceLoaded && canPrettyPrint,
+ pretty: isPretty(selectedSource),
+ })}
+ key={type}
+ title={prettyPrintMessage}
+ aria-label={prettyPrintMessage}
+ disabled={!canPrettyPrint}
+ >
+ <AccessibleImage className={type} />
+ </button>
+ );
+ }
+
+ blackBoxButton() {
+ const {
+ cx,
+ selectedSource,
+ isSelectedSourceBlackBoxed,
+ toggleBlackBox,
+ sourceLoaded,
+ isSourceOnIgnoreList,
+ } = this.props;
+
+ if (!selectedSource || !shouldBlackbox(selectedSource)) {
+ return null;
+ }
+
+ let tooltip = isSelectedSourceBlackBoxed
+ ? L10N.getStr("sourceFooter.unignore")
+ : L10N.getStr("sourceFooter.ignore");
+
+ if (isSourceOnIgnoreList) {
+ tooltip = L10N.getStr("sourceFooter.ignoreList");
+ }
+
+ const type = "black-box";
+
+ return (
+ <button
+ onClick={() => toggleBlackBox(cx, selectedSource)}
+ className={classnames("action", type, {
+ active: sourceLoaded,
+ blackboxed: isSelectedSourceBlackBoxed || isSourceOnIgnoreList,
+ })}
+ key={type}
+ title={tooltip}
+ aria-label={tooltip}
+ disabled={isSourceOnIgnoreList}
+ >
+ <AccessibleImage className="blackBox" />
+ </button>
+ );
+ }
+
+ renderToggleButton() {
+ if (this.props.horizontal) {
+ return null;
+ }
+
+ return (
+ <PaneToggleButton
+ key="toggle"
+ collapsed={this.props.endPanelCollapsed}
+ horizontal={this.props.horizontal}
+ handleClick={this.props.togglePaneCollapse}
+ position="end"
+ />
+ );
+ }
+
+ renderCommands() {
+ const commands = [this.blackBoxButton(), this.prettyPrintButton()].filter(
+ Boolean
+ );
+
+ return commands.length ? <div className="commands">{commands}</div> : null;
+ }
+
+ renderSourceSummary() {
+ const { cx, mappedSource, jumpToMappedLocation, selectedSource } =
+ this.props;
+
+ if (!mappedSource || !selectedSource || !selectedSource.isOriginal) {
+ return null;
+ }
+
+ const filename = getFilename(mappedSource);
+ const tooltip = L10N.getFormatStr(
+ "sourceFooter.mappedSourceTooltip",
+ filename
+ );
+ const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
+ const mappedSourceLocation = createLocation({
+ source: selectedSource,
+ line: 1,
+ column: 1,
+ });
+ return (
+ <button
+ className="mapped-source"
+ onClick={() => jumpToMappedLocation(cx, mappedSourceLocation)}
+ title={tooltip}
+ >
+ <span>{title}</span>
+ </button>
+ );
+ }
+
+ onCursorChange = event => {
+ const { line, ch } = event.doc.getCursor();
+ this.setState({ cursorPosition: { line, column: ch } });
+ };
+
+ renderCursorPosition() {
+ if (!this.props.selectedSource) {
+ return null;
+ }
+
+ const { line, column } = this.state.cursorPosition;
+
+ const text = L10N.getFormatStr(
+ "sourceFooter.currentCursorPosition",
+ line + 1,
+ column + 1
+ );
+ const title = L10N.getFormatStr(
+ "sourceFooter.currentCursorPosition.tooltip",
+ line + 1,
+ column + 1
+ );
+ return (
+ <div className="cursor-position" title={title}>
+ {text}
+ </div>
+ );
+ }
+
+ render() {
+ return (
+ <div className="source-footer">
+ <div className="source-footer-start">{this.renderCommands()}</div>
+ <div className="source-footer-end">
+ {this.renderSourceSummary()}
+ {this.renderCursorPosition()}
+ {this.renderToggleButton()}
+ </div>
+ </div>
+ );
+ }
+}
+
+const mapStateToProps = state => {
+ const selectedSource = getSelectedSource(state);
+ const selectedLocation = getSelectedLocation(state);
+ const sourceTextContent = getSelectedSourceTextContent(state);
+
+ return {
+ cx: getContext(state),
+ selectedSource,
+ isSelectedSourceBlackBoxed: selectedSource
+ ? isSourceBlackBoxed(state, selectedSource)
+ : null,
+ isSourceOnIgnoreList:
+ isSourceMapIgnoreListEnabled(state) &&
+ isSourceOnSourceMapIgnoreList(state, selectedSource),
+ sourceLoaded: !!sourceTextContent,
+ mappedSource: getGeneratedSource(state, selectedSource),
+ prettySource: getPrettySource(
+ state,
+ selectedSource ? selectedSource.id : null
+ ),
+ endPanelCollapsed: getPaneCollapse(state, "end"),
+ canPrettyPrint: selectedLocation
+ ? canPrettyPrintSource(state, selectedLocation)
+ : false,
+ prettyPrintMessage: selectedLocation
+ ? getPrettyPrintMessage(state, selectedLocation)
+ : null,
+ };
+};
+
+export default connect(mapStateToProps, {
+ togglePrettyPrint: actions.togglePrettyPrint,
+ toggleBlackBox: actions.toggleBlackBox,
+ jumpToMappedLocation: actions.jumpToMappedLocation,
+ togglePaneCollapse: actions.togglePaneCollapse,
+})(SourceFooter);