/* 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 . */ import React, { PureComponent } from "react"; import PropTypes from "prop-types"; import { connect } from "../../utils/connect"; import { showMenu, buildMenu } from "../../context-menu/menu"; import SourceIcon from "../shared/SourceIcon"; import { CloseButton } from "../shared/Button"; import { copyToTheClipboard } from "../../utils/clipboard"; import actions from "../../actions"; import { getDisplayPath, getFileURL, getRawSourceURL, getSourceQueryString, getTruncatedFileName, isPretty, shouldBlackbox, } from "../../utils/source"; import { getTabMenuItems } from "../../utils/tabs"; import { createLocation } from "../../utils/location"; import { getSelectedLocation, getActiveSearch, getSourcesForTabs, isSourceBlackBoxed, getContext, isSourceMapIgnoreListEnabled, isSourceOnSourceMapIgnoreList, } from "../../selectors"; const classnames = require("devtools/client/shared/classnames.js"); class Tab extends PureComponent { static get propTypes() { return { activeSearch: PropTypes.string, closeTab: PropTypes.func.isRequired, closeTabs: PropTypes.func.isRequired, copyToClipboard: PropTypes.func.isRequired, cx: PropTypes.object.isRequired, onDragEnd: PropTypes.func.isRequired, onDragOver: PropTypes.func.isRequired, onDragStart: PropTypes.func.isRequired, selectSource: PropTypes.func.isRequired, selectedLocation: PropTypes.object, showSource: PropTypes.func.isRequired, source: PropTypes.object.isRequired, sourceActor: PropTypes.object.isRequired, tabSources: PropTypes.array.isRequired, toggleBlackBox: PropTypes.func.isRequired, togglePrettyPrint: PropTypes.func.isRequired, isBlackBoxed: PropTypes.bool.isRequired, isSourceOnIgnoreList: PropTypes.bool.isRequired, }; } onTabContextMenu = (event, tab) => { event.preventDefault(); this.showContextMenu(event, tab); }; showContextMenu(e, tab) { const { cx, closeTab, closeTabs, copyToClipboard, tabSources, showSource, toggleBlackBox, togglePrettyPrint, selectedLocation, source, isBlackBoxed, isSourceOnIgnoreList, } = this.props; const tabCount = tabSources.length; const otherTabs = tabSources.filter(t => t.id !== tab); const sourceTab = tabSources.find(t => t.id == tab); const tabURLs = tabSources.map(t => t.url); const otherTabURLs = otherTabs.map(t => t.url); if (!sourceTab || !selectedLocation || !selectedLocation.sourceId) { return; } const tabMenuItems = getTabMenuItems(); const items = [ { item: { ...tabMenuItems.closeTab, click: () => closeTab(cx, sourceTab), }, }, { item: { ...tabMenuItems.closeOtherTabs, click: () => closeTabs(cx, otherTabURLs), disabled: otherTabURLs.length === 0, }, }, { item: { ...tabMenuItems.closeTabsToEnd, click: () => { const tabIndex = tabSources.findIndex(t => t.id == tab); closeTabs( cx, tabURLs.filter((t, i) => i > tabIndex) ); }, disabled: tabCount === 1 || tabSources.some((t, i) => t === tab && tabCount - 1 === i), }, }, { item: { ...tabMenuItems.closeAllTabs, click: () => closeTabs(cx, tabURLs), }, }, { item: { type: "separator" } }, { item: { ...tabMenuItems.copySource, disabled: selectedLocation.sourceId !== tab, click: () => copyToClipboard(sourceTab), }, }, { item: { ...tabMenuItems.copySourceUri2, disabled: !selectedLocation.sourceUrl, click: () => copyToTheClipboard(getRawSourceURL(sourceTab.url)), }, }, { item: { ...tabMenuItems.showSource, disabled: !selectedLocation.sourceUrl, click: () => showSource(cx, tab), }, }, { item: { ...tabMenuItems.toggleBlackBox, label: isBlackBoxed ? L10N.getStr("ignoreContextItem.unignore") : L10N.getStr("ignoreContextItem.ignore"), disabled: isSourceOnIgnoreList || !shouldBlackbox(source), click: () => toggleBlackBox(cx, source), }, }, { item: { ...tabMenuItems.prettyPrint, click: () => togglePrettyPrint(cx, tab), disabled: isPretty(sourceTab), }, }, ]; showMenu(e, buildMenu(items)); } isSourceSearchEnabled() { return this.props.activeSearch === "source"; } render() { const { cx, selectedLocation, selectSource, closeTab, source, sourceActor, tabSources, onDragOver, onDragStart, onDragEnd, } = this.props; const sourceId = source.id; const active = selectedLocation && sourceId == selectedLocation.sourceId && !this.isSourceSearchEnabled(); const isPrettyCode = isPretty(source); function onClickClose(e) { e.stopPropagation(); closeTab(cx, source); } function handleTabClick(e) { e.preventDefault(); e.stopPropagation(); return selectSource(cx, source, sourceActor); } const className = classnames("source-tab", { active, pretty: isPrettyCode, blackboxed: this.props.isBlackBoxed, }); const path = getDisplayPath(source, tabSources); const query = getSourceQueryString(source); return (
e.button === 1 && closeTab(cx, source)} onContextMenu={e => this.onTabContextMenu(e, sourceId)} title={getFileURL(source, false)} > ["file", "javascript"].includes(icon) ? null : icon } />
{getTruncatedFileName(source, query)} {path && {`../${path}/..`}}
); } } const mapStateToProps = (state, { source }) => { return { cx: getContext(state), tabSources: getSourcesForTabs(state), selectedLocation: getSelectedLocation(state), isBlackBoxed: isSourceBlackBoxed(state, source), isSourceOnIgnoreList: isSourceMapIgnoreListEnabled(state) && isSourceOnSourceMapIgnoreList(state, source), activeSearch: getActiveSearch(state), }; }; export default connect( mapStateToProps, { selectSource: actions.selectSource, copyToClipboard: actions.copyToClipboard, closeTab: actions.closeTab, closeTabs: actions.closeTabs, togglePrettyPrint: actions.togglePrettyPrint, showSource: actions.showSource, toggleBlackBox: actions.toggleBlackBox, }, null, { withRef: true, } )(Tab);