fune/devtools/client/shared/components/reps/reps.js
2019-02-21 14:56:47 +00:00

7377 lines
No EOL
188 KiB
JavaScript

(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-dom-factories"), require("Services"), require("devtools/client/shared/vendor/react-redux"));
else if(typeof define === 'function' && define.amd)
define(["devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-dom-factories", "Services", "devtools/client/shared/vendor/react-redux"], factory);
else {
var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-dom-factories"), require("Services"), require("devtools/client/shared/vendor/react-redux")) : factory(root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-dom-factories"], root["Services"], root["devtools/client/shared/vendor/react-redux"]);
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_20__, __WEBPACK_EXTERNAL_MODULE_102__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/assets/build";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 767);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
/***/ }),
/***/ 1:
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
/***/ }),
/***/ 100:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _react = __webpack_require__(1);
var _react2 = _interopRequireDefault(_react);
var _reactDomFactories = __webpack_require__(2);
var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
var _propTypes = __webpack_require__(0);
var _propTypes2 = _interopRequireDefault(_propTypes);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const { Component, createFactory } = _react2.default; /* 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/>. */
__webpack_require__(101);
// depth
const AUTO_EXPAND_DEPTH = 0;
// Simplied selector targetting elements that can receive the focus,
// full version at https://stackoverflow.com/questions/1599660.
const FOCUSABLE_SELECTOR = ["a[href]:not([tabindex='-1'])", "button:not([disabled]):not([tabindex='-1'])", "iframe:not([tabindex='-1'])", "input:not([disabled]):not([tabindex='-1'])", "select:not([disabled]):not([tabindex='-1'])", "textarea:not([disabled]):not([tabindex='-1'])", "[tabindex]:not([tabindex='-1'])"].join(", ");
/**
* An arrow that displays whether its node is expanded (▼) or collapsed
* (▶). When its node has no children, it is hidden.
*/
class ArrowExpander extends Component {
static get propTypes() {
return {
expanded: _propTypes2.default.bool
};
}
shouldComponentUpdate(nextProps, nextState) {
return this.props.expanded !== nextProps.expanded;
}
render() {
const { expanded } = this.props;
const classNames = ["arrow"];
if (expanded) {
classNames.push("expanded");
}
return _reactDomFactories2.default.button({
className: classNames.join(" ")
});
}
}
const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
class TreeNode extends Component {
static get propTypes() {
return {
id: _propTypes2.default.any.isRequired,
index: _propTypes2.default.number.isRequired,
depth: _propTypes2.default.number.isRequired,
focused: _propTypes2.default.bool.isRequired,
active: _propTypes2.default.bool.isRequired,
expanded: _propTypes2.default.bool.isRequired,
item: _propTypes2.default.any.isRequired,
isExpandable: _propTypes2.default.bool.isRequired,
onClick: _propTypes2.default.func,
renderItem: _propTypes2.default.func.isRequired
};
}
constructor(props) {
super(props);
this.treeNodeRef = _react2.default.createRef();
this._onKeyDown = this._onKeyDown.bind(this);
}
componentDidMount() {
// Make sure that none of the focusable elements inside the tree node
// container are tabbable if the tree node is not active. If the tree node
// is active and focus is outside its container, focus on the first
// focusable element inside.
const elms = this.getFocusableElements();
if (this.props.active) {
if (elms.length > 0 && !elms.includes(document.activeElement)) {
elms[0].focus();
}
} else {
elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
}
}
shouldComponentUpdate(nextProps) {
return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
}
/**
* Get a list of all elements that are focusable with a keyboard inside the
* tree node.
*/
getFocusableElements() {
return this.treeNodeRef.current ? Array.from(this.treeNodeRef.current.querySelectorAll(FOCUSABLE_SELECTOR)) : [];
}
/**
* Wrap and move keyboard focus to first/last focusable element inside the
* tree node to prevent the focus from escaping the tree node boundaries.
* element).
*
* @param {DOMNode} current currently focused element
* @param {Boolean} back direction
* @return {Boolean} true there is a newly focused element.
*/
_wrapMoveFocus(current, back) {
const elms = this.getFocusableElements();
let next;
if (elms.length === 0) {
return false;
}
if (back) {
if (elms.indexOf(current) === 0) {
next = elms[elms.length - 1];
next.focus();
}
} else if (elms.indexOf(current) === elms.length - 1) {
next = elms[0];
next.focus();
}
return !!next;
}
_onKeyDown(e) {
const { target, key, shiftKey } = e;
if (key !== "Tab") {
return;
}
const focusMoved = this._wrapMoveFocus(target, shiftKey);
if (focusMoved) {
// Focus was moved to the begining/end of the list, so we need to prevent
// the default focus change that would happen here.
e.preventDefault();
}
e.stopPropagation();
}
render() {
const {
depth,
id,
item,
focused,
active,
expanded,
renderItem,
isExpandable
} = this.props;
const arrow = isExpandable ? ArrowExpanderFactory({
item,
expanded
}) : null;
let ariaExpanded;
if (this.props.isExpandable) {
ariaExpanded = false;
}
if (this.props.expanded) {
ariaExpanded = true;
}
const indents = Array.from({ length: depth }).fill(treeIndent);
const items = indents.concat(renderItem(item, depth, focused, arrow, expanded));
return _reactDomFactories2.default.div({
id,
className: `tree-node${focused ? " focused" : ""}${active ? " active" : ""}`,
onClick: this.props.onClick,
onKeyDownCapture: active ? this._onKeyDown : null,
role: "treeitem",
ref: this.treeNodeRef,
"aria-level": depth + 1,
"aria-expanded": ariaExpanded,
"data-expandable": this.props.isExpandable
}, ...items);
}
}
const ArrowExpanderFactory = createFactory(ArrowExpander);
const TreeNodeFactory = createFactory(TreeNode);
/**
* Create a function that calls the given function `fn` only once per animation
* frame.
*
* @param {Function} fn
* @returns {Function}
*/
function oncePerAnimationFrame(fn) {
let animationId = null;
let argsToPass = null;
return function (...args) {
argsToPass = args;
if (animationId !== null) {
return;
}
animationId = requestAnimationFrame(() => {
fn.call(this, ...argsToPass);
animationId = null;
argsToPass = null;
});
};
}
/**
* A generic tree component. See propTypes for the public API.
*
* This tree component doesn't make any assumptions about the structure of your
* tree data. Whether children are computed on demand, or stored in an array in
* the parent's `_children` property, it doesn't matter. We only require the
* implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
* functions.
*
* This tree component is well tested and reliable. See the tests in ./tests
* and its usage in the performance and memory panels in mozilla-central.
*
* This tree component doesn't make any assumptions about how to render items in
* the tree. You provide a `renderItem` function, and this component will ensure
* that only those items whose parents are expanded and which are visible in the
* viewport are rendered. The `renderItem` function could render the items as a
* "traditional" tree or as rows in a table or anything else. It doesn't
* restrict you to only one certain kind of tree.
*
* The tree comes with basic styling for the indent, the arrow, as well as
* hovered and focused styles which can be override in CSS.
*
* ### Example Usage
*
* Suppose we have some tree data where each item has this form:
*
* {
* id: Number,
* label: String,
* parent: Item or null,
* children: Array of child items,
* expanded: bool,
* }
*
* Here is how we could render that data with this component:
*
* class MyTree extends Component {
* static get propTypes() {
* // The root item of the tree, with the form described above.
* return {
* root: PropTypes.object.isRequired
* };
* },
*
* render() {
* return Tree({
* itemHeight: 20, // px
*
* getRoots: () => [this.props.root],
*
* getParent: item => item.parent,
* getChildren: item => item.children,
* getKey: item => item.id,
* isExpanded: item => item.expanded,
*
* renderItem: (item, depth, isFocused, arrow, isExpanded) => {
* let className = "my-tree-item";
* if (isFocused) {
* className += " focused";
* }
* return dom.div({
* className,
* },
* arrow,
* // And here is the label for this item.
* dom.span({ className: "my-tree-item-label" }, item.label)
* );
* },
*
* onExpand: item => dispatchExpandActionToRedux(item),
* onCollapse: item => dispatchCollapseActionToRedux(item),
* });
* }
* }
*/
class Tree extends Component {
static get propTypes() {
return {
// Required props
// A function to get an item's parent, or null if it is a root.
//
// Type: getParent(item: Item) -> Maybe<Item>
//
// Example:
//
// // The parent of this item is stored in its `parent` property.
// getParent: item => item.parent
getParent: _propTypes2.default.func.isRequired,
// A function to get an item's children.
//
// Type: getChildren(item: Item) -> [Item]
//
// Example:
//
// // This item's children are stored in its `children` property.
// getChildren: item => item.children
getChildren: _propTypes2.default.func.isRequired,
// A function which takes an item and ArrowExpander component instance and
// returns a component, or text, or anything else that React considers
// renderable.
//
// Type: renderItem(item: Item,
// depth: Number,
// isFocused: Boolean,
// arrow: ReactComponent,
// isExpanded: Boolean) -> ReactRenderable
//
// Example:
//
// renderItem: (item, depth, isFocused, arrow, isExpanded) => {
// let className = "my-tree-item";
// if (isFocused) {
// className += " focused";
// }
// return dom.div(
// {
// className,
// style: { marginLeft: depth * 10 + "px" }
// },
// arrow,
// dom.span({ className: "my-tree-item-label" }, item.label)
// );
// },
renderItem: _propTypes2.default.func.isRequired,
// A function which returns the roots of the tree (forest).
//
// Type: getRoots() -> [Item]
//
// Example:
//
// // In this case, we only have one top level, root item. You could
// // return multiple items if you have many top level items in your
// // tree.
// getRoots: () => [this.props.rootOfMyTree]
getRoots: _propTypes2.default.func.isRequired,
// A function to get a unique key for the given item. This helps speed up
// React's rendering a *TON*.
//
// Type: getKey(item: Item) -> String
//
// Example:
//
// getKey: item => `my-tree-item-${item.uniqueId}`
getKey: _propTypes2.default.func.isRequired,
// A function to get whether an item is expanded or not. If an item is not
// expanded, then it must be collapsed.
//
// Type: isExpanded(item: Item) -> Boolean
//
// Example:
//
// isExpanded: item => item.expanded,
isExpanded: _propTypes2.default.func.isRequired,
// Optional props
// The currently focused item, if any such item exists.
focused: _propTypes2.default.any,
// Handle when a new item is focused.
onFocus: _propTypes2.default.func,
// The depth to which we should automatically expand new items.
autoExpandDepth: _propTypes2.default.number,
// Should auto expand all new items or just the new items under the first
// root item.
autoExpandAll: _propTypes2.default.bool,
// Auto expand a node only if number of its children
// are less than autoExpandNodeChildrenLimit
autoExpandNodeChildrenLimit: _propTypes2.default.number,
// Note: the two properties below are mutually exclusive. Only one of the
// label properties is necessary.
// ID of an element whose textual content serves as an accessible label
// for a tree.
labelledby: _propTypes2.default.string,
// Accessibility label for a tree widget.
label: _propTypes2.default.string,
// Optional event handlers for when items are expanded or collapsed.
// Useful for dispatching redux events and updating application state,
// maybe lazily loading subtrees from a worker, etc.
//
// Type:
// onExpand(item: Item)
// onCollapse(item: Item)
//
// Example:
//
// onExpand: item => dispatchExpandActionToRedux(item)
onExpand: _propTypes2.default.func,
onCollapse: _propTypes2.default.func,
// The currently active (keyboard) item, if any such item exists.
active: _propTypes2.default.any,
// Optional event handler called with the current focused node when the
// Enter key is pressed. Can be useful to allow further keyboard actions
// within the tree node.
onActivate: _propTypes2.default.func,
isExpandable: _propTypes2.default.func,
// Additional classes to add to the root element.
className: _propTypes2.default.string,
// style object to be applied to the root element.
style: _propTypes2.default.object,
// Prevents blur when Tree loses focus
preventBlur: _propTypes2.default.bool
};
}
static get defaultProps() {
return {
autoExpandDepth: AUTO_EXPAND_DEPTH,
autoExpandAll: true
};
}
constructor(props) {
super(props);
this.state = {
seen: new Set()
};
this.treeRef = _react2.default.createRef();
this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);
this._focusFirstNode = oncePerAnimationFrame(this._focusFirstNode).bind(this);
this._focusLastNode = oncePerAnimationFrame(this._focusLastNode).bind(this);
this._autoExpand = this._autoExpand.bind(this);
this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
this._preventEvent = this._preventEvent.bind(this);
this._dfs = this._dfs.bind(this);
this._dfsFromRoots = this._dfsFromRoots.bind(this);
this._focus = this._focus.bind(this);
this._activate = this._activate.bind(this);
this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
this._onBlur = this._onBlur.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
}
componentDidMount() {
this._autoExpand();
if (this.props.focused) {
this._scrollNodeIntoView(this.props.focused);
}
}
componentWillReceiveProps(nextProps) {
this._autoExpand();
}
componentDidUpdate(prevProps, prevState) {
if (this.props.focused && prevProps.focused !== this.props.focused) {
this._scrollNodeIntoView(this.props.focused);
}
}
_autoExpand() {
const { autoExpandDepth, autoExpandNodeChildrenLimit } = this.props;
if (!autoExpandDepth) {
return;
}
// Automatically expand the first autoExpandDepth levels for new items. Do
// not use the usual DFS infrastructure because we don't want to ignore
// collapsed nodes.
const autoExpand = (item, currentDepth) => {
if (currentDepth >= autoExpandDepth || this.state.seen.has(item)) {
return;
}
const children = this.props.getChildren(item);
if (autoExpandNodeChildrenLimit && children.length > autoExpandNodeChildrenLimit) {
return;
}
this.props.onExpand(item);
this.state.seen.add(item);
const length = children.length;
for (let i = 0; i < length; i++) {
autoExpand(children[i], currentDepth + 1);
}
};
const roots = this.props.getRoots();
const length = roots.length;
if (this.props.autoExpandAll) {
for (let i = 0; i < length; i++) {
autoExpand(roots[i], 0);
}
} else if (length != 0) {
autoExpand(roots[0], 0);
}
}
_preventArrowKeyScrolling(e) {
switch (e.key) {
case "ArrowUp":
case "ArrowDown":
case "ArrowLeft":
case "ArrowRight":
this._preventEvent(e);
break;
}
}
_preventEvent(e) {
e.preventDefault();
e.stopPropagation();
if (e.nativeEvent) {
if (e.nativeEvent.preventDefault) {
e.nativeEvent.preventDefault();
}
if (e.nativeEvent.stopPropagation) {
e.nativeEvent.stopPropagation();
}
}
}
/**
* Perform a pre-order depth-first search from item.
*/
_dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
traversal.push({ item, depth: _depth });
if (!this.props.isExpanded(item)) {
return traversal;
}
const nextDepth = _depth + 1;
if (nextDepth > maxDepth) {
return traversal;
}
const children = this.props.getChildren(item);
const length = children.length;
for (let i = 0; i < length; i++) {
this._dfs(children[i], maxDepth, traversal, nextDepth);
}
return traversal;
}
/**
* Perform a pre-order depth-first search over the whole forest.
*/
_dfsFromRoots(maxDepth = Infinity) {
const traversal = [];
const roots = this.props.getRoots();
const length = roots.length;
for (let i = 0; i < length; i++) {
this._dfs(roots[i], maxDepth, traversal);
}
return traversal;
}
/**
* Expands current row.
*
* @param {Object} item
* @param {Boolean} expandAllChildren
*/
_onExpand(item, expandAllChildren) {
if (this.props.onExpand) {
this.props.onExpand(item);
if (expandAllChildren) {
const children = this._dfs(item);
const length = children.length;
for (let i = 0; i < length; i++) {
this.props.onExpand(children[i].item);
}
}
}
}
/**
* Collapses current row.
*
* @param {Object} item
*/
_onCollapse(item) {
if (this.props.onCollapse) {
this.props.onCollapse(item);
}
}
/**
* Sets the passed in item to be the focused item.
*
* @param {Object|undefined} item
* The item to be focused, or undefined to focus no item.
*
* @param {Object|undefined} options
* An options object which can contain:
* - dir: "up" or "down" to indicate if we should scroll the element
* to the top or the bottom of the scrollable container when
* the element is off canvas.
*/
_focus(item, options = {}) {
const { preventAutoScroll } = options;
if (item && !preventAutoScroll) {
this._scrollNodeIntoView(item, options);
}
if (this.props.active != undefined) {
this._activate(undefined);
if (this.treeRef.current !== document.activeElement) {
this.treeRef.current.focus();
}
}
if (this.props.onFocus) {
this.props.onFocus(item);
}
}
/**
* Sets the passed in item to be the active item.
*
* @param {Object|undefined} item
* The item to be activated, or undefined to activate no item.
*/
_activate(item) {
if (this.props.onActivate) {
this.props.onActivate(item);
}
}
/**
* Sets the passed in item to be the focused item.
*
* @param {Object|undefined} item
* The item to be scrolled to.
*
* @param {Object|undefined} options
* An options object which can contain:
* - dir: "up" or "down" to indicate if we should scroll the element
* to the top or the bottom of the scrollable container when
* the element is off canvas.
*/
_scrollNodeIntoView(item, options = {}) {
if (item !== undefined) {
const treeElement = this.treeRef.current;
const element = document.getElementById(this.props.getKey(item));
if (element) {
const { top, bottom } = element.getBoundingClientRect();
const closestScrolledParent = node => {
if (node == null) {
return null;
}
if (node.scrollHeight > node.clientHeight) {
return node;
}
return closestScrolledParent(node.parentNode);
};
const scrolledParent = closestScrolledParent(treeElement);
const scrolledParentRect = scrolledParent ? scrolledParent.getBoundingClientRect() : null;
const isVisible = !scrolledParent || top >= scrolledParentRect.top && bottom <= scrolledParentRect.bottom;
if (!isVisible) {
const { alignTo } = options;
const scrollToTop = alignTo ? alignTo === "top" : !scrolledParentRect || top < scrolledParentRect.top;
element.scrollIntoView(scrollToTop);
}
}
}
}
/**
* Sets the state to have no focused item.
*/
_onBlur(e) {
if (this.props.active != undefined) {
const { relatedTarget } = e;
if (!this.treeRef.current.contains(relatedTarget)) {
this._activate(undefined);
}
} else if (!this.props.preventBlur) {
this._focus(undefined);
}
}
/**
* Handles key down events in the tree's container.
*
* @param {Event} e
*/
// eslint-disable-next-line complexity
_onKeyDown(e) {
if (this.props.focused == null) {
return;
}
// Allow parent nodes to use navigation arrows with modifiers.
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
return;
}
this._preventArrowKeyScrolling(e);
switch (e.key) {
case "ArrowUp":
this._focusPrevNode();
return;
case "ArrowDown":
this._focusNextNode();
return;
case "ArrowLeft":
if (this.props.isExpanded(this.props.focused) && this._nodeIsExpandable(this.props.focused)) {
this._onCollapse(this.props.focused);
} else {
this._focusParentNode();
}
return;
case "ArrowRight":
if (this._nodeIsExpandable(this.props.focused) && !this.props.isExpanded(this.props.focused)) {
this._onExpand(this.props.focused);
} else {
this._focusNextNode();
}
return;
case "Home":
this._focusFirstNode();
return;
case "End":
this._focusLastNode();
return;
case "Enter":
case " ":
if (this.treeRef.current === document.activeElement) {
this._preventEvent(e);
if (this.props.active !== this.props.focused) {
this._activate(this.props.focused);
}
}
return;
case "Escape":
this._preventEvent(e);
if (this.props.active != undefined) {
this._activate(undefined);
}
if (this.treeRef.current !== document.activeElement) {
this.treeRef.current.focus();
}
return;
}
}
/**
* Sets the previous node relative to the currently focused item, to focused.
*/
_focusPrevNode() {
// Start a depth first search and keep going until we reach the currently
// focused node. Focus the previous node in the DFS, if it exists. If it
// doesn't exist, we're at the first node already.
let prev;
const traversal = this._dfsFromRoots();
const length = traversal.length;
for (let i = 0; i < length; i++) {
const item = traversal[i].item;
if (item === this.props.focused) {
break;
}
prev = item;
}
if (prev === undefined) {
return;
}
this._focus(prev, { alignTo: "top" });
}
/**
* Handles the down arrow key which will focus either the next child
* or sibling row.
*/
_focusNextNode() {
// Start a depth first search and keep going until we reach the currently
// focused node. Focus the next node in the DFS, if it exists. If it
// doesn't exist, we're at the last node already.
const traversal = this._dfsFromRoots();
const length = traversal.length;
let i = 0;
while (i < length) {
if (traversal[i].item === this.props.focused) {
break;
}
i++;
}
if (i + 1 < traversal.length) {
this._focus(traversal[i + 1].item, { alignTo: "bottom" });
}
}
/**
* Handles the left arrow key, going back up to the current rows'
* parent row.
*/
_focusParentNode() {
const parent = this.props.getParent(this.props.focused);
if (!parent) {
this._focusPrevNode(this.props.focused);
return;
}
this._focus(parent, { alignTo: "top" });
}
_focusFirstNode() {
const traversal = this._dfsFromRoots();
this._focus(traversal[0].item, { alignTo: "top" });
}
_focusLastNode() {
const traversal = this._dfsFromRoots();
const lastIndex = traversal.length - 1;
this._focus(traversal[lastIndex].item, { alignTo: "bottom" });
}
_nodeIsExpandable(item) {
return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
}
render() {
const traversal = this._dfsFromRoots();
const { active, focused } = this.props;
const nodes = traversal.map((v, i) => {
const { item, depth } = traversal[i];
const key = this.props.getKey(item, i);
return TreeNodeFactory({
// We make a key unique depending on whether the tree node is in active
// or inactive state to make sure that it is actually replaced and the
// tabbable state is reset.
key: `${key}-${active === item ? "active" : "inactive"}`,
id: key,
index: i,
item,
depth,
renderItem: this.props.renderItem,
focused: focused === item,
active: active === item,
expanded: this.props.isExpanded(item),
isExpandable: this._nodeIsExpandable(item),
onExpand: this._onExpand,
onCollapse: this._onCollapse,
onClick: e => {
// We can stop the propagation since click handler on the node can be
// created in `renderItem`.
e.stopPropagation();
// Since the user just clicked the node, there's no need to check if
// it should be scrolled into view.
this._focus(item, { preventAutoScroll: true });
if (this.props.isExpanded(item)) {
this.props.onCollapse(item, e.altKey);
} else {
this.props.onExpand(item, e.altKey);
}
// Focus should always remain on the tree container itself.
this.treeRef.current.focus();
}
});
});
const style = Object.assign({}, this.props.style || {});
return _reactDomFactories2.default.div({
className: `tree ${this.props.className ? this.props.className : ""}`,
ref: this.treeRef,
role: "tree",
tabIndex: "0",
onKeyDown: this._onKeyDown,
onKeyPress: this._preventArrowKeyScrolling,
onKeyUp: this._preventArrowKeyScrolling,
onFocus: ({ nativeEvent }) => {
if (focused || !nativeEvent || !this.treeRef.current) {
return;
}
const { explicitOriginalTarget } = nativeEvent;
// Only set default focus to the first tree node if the focus came
// from outside the tree (e.g. by tabbing to the tree from other
// external elements).
if (explicitOriginalTarget !== this.treeRef.current && !this.treeRef.current.contains(explicitOriginalTarget)) {
this._focus(traversal[0].item);
}
},
onBlur: this._onBlur,
"aria-label": this.props.label,
"aria-labelledby": this.props.labelledby,
"aria-activedescendant": focused && this.props.getKey(focused),
style
}, nodes);
}
}
exports.default = Tree;
/***/ }),
/***/ 101:
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }),
/***/ 102:
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_102__;
/***/ }),
/***/ 103:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const {
enumEntries,
enumIndexedProperties,
enumNonIndexedProperties,
getPrototype,
enumSymbols,
getFullText
} = __webpack_require__(104);
const {
getClosestGripNode,
getClosestNonBucketNode,
getValue,
nodeHasAccessors,
nodeHasAllEntriesInPreview,
nodeHasProperties,
nodeIsBucket,
nodeIsDefaultProperties,
nodeIsEntries,
nodeIsMapEntry,
nodeIsPrimitive,
nodeIsProxy,
nodeNeedsNumericalBuckets,
nodeIsLongString
} = __webpack_require__(59);
function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
const promises = [];
let objectClient;
const getObjectClient = () => objectClient || createObjectClient(value);
if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
promises.push(enumIndexedProperties(getObjectClient(), start, end));
}
if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
}
if (shouldLoadItemEntries(item, loadedProperties)) {
promises.push(enumEntries(getObjectClient(), start, end));
}
if (shouldLoadItemPrototype(item, loadedProperties)) {
promises.push(getPrototype(getObjectClient()));
}
if (shouldLoadItemSymbols(item, loadedProperties)) {
promises.push(enumSymbols(getObjectClient(), start, end));
}
if (shouldLoadItemFullText(item, loadedProperties)) {
promises.push(getFullText(createLongStringClient(value), item));
}
return Promise.all(promises).then(mergeResponses);
}
function mergeResponses(responses) {
const data = {};
for (const response of responses) {
if (response.hasOwnProperty("ownProperties")) {
data.ownProperties = { ...data.ownProperties, ...response.ownProperties };
}
if (response.ownSymbols && response.ownSymbols.length > 0) {
data.ownSymbols = response.ownSymbols;
}
if (response.prototype) {
data.prototype = response.prototype;
}
if (response.fullText) {
data.fullText = response.fullText;
}
}
return data;
}
function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item)) &&
// The data is loaded when expanding the window node.
!nodeIsDefaultProperties(item);
}
function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item) &&
// The data is loaded when expanding the window node.
!nodeIsDefaultProperties(item);
}
function shouldLoadItemEntries(item, loadedProperties = new Map()) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
}
function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
const value = getValue(item);
return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
}
function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
const value = getValue(item);
return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
}
function shouldLoadItemFullText(item, loadedProperties = new Map()) {
return !loadedProperties.has(item.path) && nodeIsLongString(item);
}
module.exports = {
loadItemProperties,
mergeResponses,
shouldLoadItemEntries,
shouldLoadItemIndexedProperties,
shouldLoadItemNonIndexedProperties,
shouldLoadItemPrototype,
shouldLoadItemSymbols,
shouldLoadItemFullText
};
/***/ }),
/***/ 104:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const { getValue, nodeHasFullText } = __webpack_require__(59); /* 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/>. */
async function enumIndexedProperties(objectClient, start, end) {
try {
const { iterator } = await objectClient.enumProperties({
ignoreNonIndexedProperties: true
});
const response = await iteratorSlice(iterator, start, end);
return response;
} catch (e) {
console.error("Error in enumIndexedProperties", e);
return {};
}
}
async function enumNonIndexedProperties(objectClient, start, end) {
try {
const { iterator } = await objectClient.enumProperties({
ignoreIndexedProperties: true
});
const response = await iteratorSlice(iterator, start, end);
return response;
} catch (e) {
console.error("Error in enumNonIndexedProperties", e);
return {};
}
}
async function enumEntries(objectClient, start, end) {
try {
const { iterator } = await objectClient.enumEntries();
const response = await iteratorSlice(iterator, start, end);
return response;
} catch (e) {
console.error("Error in enumEntries", e);
return {};
}
}
async function enumSymbols(objectClient, start, end) {
try {
const { iterator } = await objectClient.enumSymbols();
const response = await iteratorSlice(iterator, start, end);
return response;
} catch (e) {
console.error("Error in enumSymbols", e);
return {};
}
}
async function getPrototype(objectClient) {
if (typeof objectClient.getPrototype !== "function") {
console.error("objectClient.getPrototype is not a function");
return Promise.resolve({});
}
return objectClient.getPrototype();
}
async function getFullText(longStringClient, item) {
const { initial, fullText, length } = getValue(item);
// Return fullText property if it exists so that it can be added to the
// loadedProperties map.
if (nodeHasFullText(item)) {
return Promise.resolve({ fullText });
}
return new Promise((resolve, reject) => {
longStringClient.substring(initial.length, length, response => {
if (response.error) {
console.error("LongStringClient.substring", `${response.error}: ${response.message}`);
reject({});
return;
}
resolve({
fullText: initial + response.substring
});
});
});
}
function iteratorSlice(iterator, start, end) {
start = start || 0;
const count = end ? end - start + 1 : iterator.count;
if (count === 0) {
return Promise.resolve({});
}
return iterator.slice(start, count);
}
module.exports = {
enumEntries,
enumIndexedProperties,
enumNonIndexedProperties,
enumSymbols,
getPrototype,
getFullText
};
/***/ }),
/***/ 17:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
__webpack_require__(173);
// Load all existing rep templates
const Undefined = __webpack_require__(174);
const Null = __webpack_require__(175);
const StringRep = __webpack_require__(18);
const Number = __webpack_require__(176);
const ArrayRep = __webpack_require__(38);
const Obj = __webpack_require__(177);
const SymbolRep = __webpack_require__(178);
const InfinityRep = __webpack_require__(179);
const NaNRep = __webpack_require__(180);
const Accessor = __webpack_require__(181);
// DOM types (grips)
const Accessible = __webpack_require__(182);
const Attribute = __webpack_require__(183);
const DateTime = __webpack_require__(184);
const Document = __webpack_require__(185);
const DocumentType = __webpack_require__(186);
const Event = __webpack_require__(187);
const Func = __webpack_require__(93);
const PromiseRep = __webpack_require__(188);
const RegExp = __webpack_require__(189);
const StyleSheet = __webpack_require__(190);
const CommentNode = __webpack_require__(191);
const ElementNode = __webpack_require__(192);
const TextNode = __webpack_require__(193);
const ErrorRep = __webpack_require__(95);
const Window = __webpack_require__(194);
const ObjectWithText = __webpack_require__(195);
const ObjectWithURL = __webpack_require__(196);
const GripArray = __webpack_require__(96);
const GripMap = __webpack_require__(98);
const GripMapEntry = __webpack_require__(99);
const Grip = __webpack_require__(58);
// List of all registered template.
// XXX there should be a way for extensions to register a new
// or modify an existing rep.
const reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, Accessible, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor, Obj];
/**
* Generic rep that is used for rendering native JS types or an object.
* The right template used for rendering is picked automatically according
* to the current value type. The value must be passed in as the 'object'
* property.
*/
const Rep = function (props) {
const { object, defaultRep } = props;
const rep = getRep(object, defaultRep, props.noGrip);
return rep(props);
};
// Helpers
/**
* Return a rep object that is responsible for rendering given
* object.
*
* @param object {Object} Object to be rendered in the UI. This
* can be generic JS object as well as a grip (handle to a remote
* debuggee object).
*
* @param defaultRep {React.Component} The default template
* that should be used to render given object if none is found.
*
* @param noGrip {Boolean} If true, will only check reps not made for remote
* objects.
*/
function getRep(object, defaultRep = Grip, noGrip = false) {
for (let i = 0; i < reps.length; i++) {
const rep = reps[i];
try {
// supportsObject could return weight (not only true/false
// but a number), which would allow to priorities templates and
// support better extensibility.
if (rep.supportsObject(object, noGrip)) {
return rep.rep;
}
} catch (err) {
console.error(err);
}
}
return defaultRep.rep;
}
module.exports = {
Rep,
REPS: {
Accessible,
Accessor,
ArrayRep,
Attribute,
CommentNode,
DateTime,
Document,
DocumentType,
ElementNode,
ErrorRep,
Event,
Func,
Grip,
GripArray,
GripMap,
GripMapEntry,
InfinityRep,
NaNRep,
Null,
Number,
Obj,
ObjectWithText,
ObjectWithURL,
PromiseRep,
RegExp,
Rep,
StringRep,
StyleSheet,
SymbolRep,
TextNode,
Undefined,
Window
},
// Exporting for tests
getRep
};
/***/ }),
/***/ 173:
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }),
/***/ 174:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const { getGripType, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders undefined value
*/
const Undefined = function () {
return span({ className: "objectBox objectBox-undefined" }, "undefined");
};
function supportsObject(object, noGrip = false) {
if (noGrip === true) {
return object === undefined;
}
return object && object.type && object.type == "undefined" || getGripType(object, noGrip) == "undefined";
}
// Exports from this module
module.exports = {
rep: wrapRender(Undefined),
supportsObject
};
/***/ }),
/***/ 175:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const { wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders null value
*/
function Null(props) {
return span({ className: "objectBox objectBox-null" }, "null");
}
function supportsObject(object, noGrip = false) {
if (noGrip === true) {
return object === null;
}
if (object && object.type && object.type == "null") {
return true;
}
return object == null;
}
// Exports from this module
module.exports = {
rep: wrapRender(Null),
supportsObject
};
/***/ }),
/***/ 176:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { getGripType, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a number
*/
Number.propTypes = {
object: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.bool]).isRequired
};
function Number(props) {
const value = props.object;
return span({ className: "objectBox objectBox-number" }, stringify(value));
}
function stringify(object) {
const isNegativeZero = Object.is(object, -0) || object.type && object.type == "-0";
return isNegativeZero ? "-0" : String(object);
}
function supportsObject(object, noGrip = false) {
return ["boolean", "number", "-0"].includes(getGripType(object, noGrip));
}
// Exports from this module
module.exports = {
rep: wrapRender(Number),
supportsObject
};
/***/ }),
/***/ 177:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { wrapRender, ellipsisElement } = __webpack_require__(4);
const PropRep = __webpack_require__(39);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
const DEFAULT_TITLE = "Object";
/**
* Renders an object. An object is represented by a list of its
* properties enclosed in curly brackets.
*/
ObjectRep.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
title: PropTypes.string
};
function ObjectRep(props) {
const object = props.object;
const propsArray = safePropIterator(props, object);
if (props.mode === MODE.TINY) {
const tinyModeItems = [];
if (getTitle(props, object) !== DEFAULT_TITLE) {
tinyModeItems.push(getTitleElement(props, object));
} else {
tinyModeItems.push(span({
className: "objectLeftBrace"
}, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
className: "objectRightBrace"
}, "}"));
}
return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
}
return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
className: "objectLeftBrace"
}, " { "), ...propsArray, span({
className: "objectRightBrace"
}, " }"));
}
function getTitleElement(props, object) {
return span({ className: "objectTitle" }, getTitle(props, object));
}
function getTitle(props, object) {
return props.title || DEFAULT_TITLE;
}
function safePropIterator(props, object, max) {
max = typeof max === "undefined" ? 3 : max;
try {
return propIterator(props, object, max);
} catch (err) {
console.error(err);
}
return [];
}
function propIterator(props, object, max) {
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
if (Object.prototype.toString.call(object) === "[object Generator]") {
object = Object.getPrototypeOf(object);
}
const elements = [];
const unimportantProperties = [];
let propertiesNumber = 0;
const propertiesNames = Object.keys(object);
const pushPropRep = (name, value) => {
elements.push(PropRep({
...props,
key: name,
mode: MODE.TINY,
name,
object: value,
equal: ": "
}));
propertiesNumber++;
if (propertiesNumber < propertiesNames.length) {
elements.push(", ");
}
};
try {
for (const name of propertiesNames) {
if (propertiesNumber >= max) {
break;
}
let value;
try {
value = object[name];
} catch (exc) {
continue;
}
// Object members with non-empty values are preferred since it gives the
// user a better overview of the object.
if (isInterestingProp(value)) {
pushPropRep(name, value);
} else {
// If the property is not important, put its name on an array for later
// use.
unimportantProperties.push(name);
}
}
} catch (err) {
console.error(err);
}
if (propertiesNumber < max) {
for (const name of unimportantProperties) {
if (propertiesNumber >= max) {
break;
}
let value;
try {
value = object[name];
} catch (exc) {
continue;
}
pushPropRep(name, value);
}
}
if (propertiesNumber < propertiesNames.length) {
elements.push(ellipsisElement);
}
return elements;
}
function isInterestingProp(value) {
const type = typeof value;
return type == "boolean" || type == "number" || type == "string" && value;
}
function supportsObject(object, noGrip = false) {
return noGrip;
}
// Exports from this module
module.exports = {
rep: wrapRender(ObjectRep),
supportsObject
};
/***/ }),
/***/ 178:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { getGripType, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { rep: StringRep } = __webpack_require__(18);
const { span } = dom;
const MAX_STRING_LENGTH = 50;
/**
* Renders a symbol.
*/
SymbolRep.propTypes = {
object: PropTypes.object.isRequired
};
function SymbolRep(props) {
const { className = "objectBox objectBox-symbol", object } = props;
const { name } = object;
let symbolText = name || "";
if (name && name.type && name.type === "longString") {
symbolText = StringRep({
object: symbolText,
shouldCrop: true,
cropLimit: MAX_STRING_LENGTH,
useQuotes: false
});
}
return span({
className,
"data-link-actor-id": object.actor
}, "Symbol(", symbolText, ")");
}
function supportsObject(object, noGrip = false) {
return getGripType(object, noGrip) == "symbol";
}
// Exports from this module
module.exports = {
rep: wrapRender(SymbolRep),
supportsObject
};
/***/ }),
/***/ 179:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { getGripType, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a Infinity object
*/
InfinityRep.propTypes = {
object: PropTypes.object.isRequired
};
function InfinityRep(props) {
const { object } = props;
return span({ className: "objectBox objectBox-number" }, object.type);
}
function supportsObject(object, noGrip = false) {
const type = getGripType(object, noGrip);
return type == "Infinity" || type == "-Infinity";
}
// Exports from this module
module.exports = {
rep: wrapRender(InfinityRep),
supportsObject
};
/***/ }),
/***/ 18:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const {
containsURL,
isURL,
escapeString,
getGripType,
rawCropString,
sanitizeString,
wrapRender,
isGrip,
tokenSplitRegex,
ELLIPSIS
} = __webpack_require__(4);
const dom = __webpack_require__(2);
const { a, span } = dom;
/**
* Renders a string. String value is enclosed within quotes.
*/
StringRep.propTypes = {
useQuotes: PropTypes.bool,
escapeWhitespace: PropTypes.bool,
style: PropTypes.object,
cropLimit: PropTypes.number.isRequired,
member: PropTypes.object,
object: PropTypes.object.isRequired,
openLink: PropTypes.func,
className: PropTypes.string,
title: PropTypes.string
};
function StringRep(props) {
const {
className,
style,
cropLimit,
object,
useQuotes = true,
escapeWhitespace = true,
member,
openLink,
title
} = props;
let text = object;
const isLong = isLongString(object);
const isOpen = member && member.open;
const shouldCrop = !isOpen && cropLimit && text.length > cropLimit;
if (isLong) {
text = maybeCropLongString({
shouldCrop,
cropLimit
}, text);
const { fullText } = object;
if (isOpen && fullText) {
text = fullText;
}
}
text = formatText({
useQuotes,
escapeWhitespace
}, text);
const config = getElementConfig({
className,
style,
actor: object.actor,
title
});
if (!isLong) {
if (containsURL(text)) {
return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, openLink));
}
// Cropping of longString has been handled before formatting.
text = maybeCropString({
isLong,
shouldCrop,
cropLimit
}, text);
}
return span(config, text);
}
function maybeCropLongString(opts, text) {
const { shouldCrop, cropLimit } = opts;
const { initial, length } = text;
text = shouldCrop ? initial.substring(0, cropLimit) : initial;
if (text.length < length) {
text += ELLIPSIS;
}
return text;
}
function formatText(opts, text) {
const { useQuotes, escapeWhitespace } = opts;
return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
}
function getElementConfig(opts) {
const { className, style, actor, title } = opts;
const config = {};
if (actor) {
config["data-link-actor-id"] = actor;
}
if (title) {
config.title = title;
}
const classNames = ["objectBox", "objectBox-string"];
if (className) {
classNames.push(className);
}
config.className = classNames.join(" ");
if (style) {
config.style = style;
}
return config;
}
function maybeCropString(opts, text) {
const { shouldCrop, cropLimit } = opts;
return shouldCrop ? rawCropString(text, cropLimit) : text;
}
/**
* Get an array of the elements representing the string, cropped if needed,
* with actual links.
*
* @param {String} text: The actual string to linkify.
* @param {Integer | null} cropLimit
* @param {Function} openLink: Function handling the link opening.
* @returns {Array<String|ReactElement>}
*/
function getLinkifiedElements(text, cropLimit, openLink) {
const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
const startCropIndex = cropLimit ? halfLimit : null;
const endCropIndex = cropLimit ? text.length - halfLimit : null;
// As we walk through the tokens of the source string, we make sure to
// preserve the original whitespace that separated the tokens.
let currentIndex = 0;
const items = [];
for (const token of text.split(tokenSplitRegex)) {
if (isURL(token)) {
// Let's grab all the non-url strings before the link.
const tokenStart = text.indexOf(token, currentIndex);
let nonUrlText = text.slice(currentIndex, tokenStart);
nonUrlText = getCroppedString(nonUrlText, currentIndex, startCropIndex, endCropIndex);
if (nonUrlText) {
items.push(nonUrlText);
}
// Update the index to match the beginning of the token.
currentIndex = tokenStart;
const linkText = getCroppedString(token, currentIndex, startCropIndex, endCropIndex);
if (linkText) {
items.push(a({
className: "url",
title: token,
draggable: false,
onClick: openLink ? e => {
e.preventDefault();
openLink(token, e);
} : null
}, linkText));
}
currentIndex = tokenStart + token.length;
}
}
// Clean up any non-URL text at the end of the source string,
// i.e. not handled in the loop.
if (currentIndex !== text.length) {
let nonUrlText = text.slice(currentIndex, text.length);
if (currentIndex < endCropIndex) {
nonUrlText = getCroppedString(nonUrlText, currentIndex, startCropIndex, endCropIndex);
}
items.push(nonUrlText);
}
return items;
}
/**
* Returns a cropped substring given an offset, start and end crop indices in a
* parent string.
*
* @param {String} text: The substring to crop.
* @param {Integer} offset: The offset corresponding to the index at which
* the substring is in the parent string.
* @param {Integer|null} startCropIndex: the index where the start of the crop
* should happen in the parent string.
* @param {Integer|null} endCropIndex: the index where the end of the crop
* should happen in the parent string
* @returns {String|null} The cropped substring, or null if the text is
* completly cropped.
*/
function getCroppedString(text, offset = 0, startCropIndex, endCropIndex) {
if (!startCropIndex) {
return text;
}
const start = offset;
const end = offset + text.length;
const shouldBeVisible = !(start >= startCropIndex && end <= endCropIndex);
if (!shouldBeVisible) {
return null;
}
const shouldCropEnd = start < startCropIndex && end > startCropIndex;
const shouldCropStart = start < endCropIndex && end > endCropIndex;
if (shouldCropEnd) {
const cutIndex = startCropIndex - start;
return text.substring(0, cutIndex) + ELLIPSIS + (shouldCropStart ? text.substring(endCropIndex - start) : "");
}
if (shouldCropStart) {
// The string should be cropped at the beginning.
const cutIndex = endCropIndex - start;
return text.substring(cutIndex);
}
return text;
}
function isLongString(object) {
return object && object.type === "longString";
}
function supportsObject(object, noGrip = false) {
if (noGrip === false && isGrip(object)) {
return isLongString(object);
}
return getGripType(object, noGrip) == "string";
}
// Exports from this module
module.exports = {
rep: wrapRender(StringRep),
supportsObject,
isLongString
};
/***/ }),
/***/ 180:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const { getGripType, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a NaN object
*/
function NaNRep(props) {
return span({ className: "objectBox objectBox-nan" }, "NaN");
}
function supportsObject(object, noGrip = false) {
return getGripType(object, noGrip) == "NaN";
}
// Exports from this module
module.exports = {
rep: wrapRender(NaNRep),
supportsObject
};
/***/ }),
/***/ 181:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const dom = __webpack_require__(2);
const PropTypes = __webpack_require__(0);
const { wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const { span } = dom;
/**
* Renders an object. An object is represented by a list of its
* properties enclosed in curly brackets.
*/
Accessor.propTypes = {
object: PropTypes.object.isRequired,
mode: PropTypes.oneOf(Object.values(MODE))
};
function Accessor(props) {
const { object, evaluation, onInvokeGetterButtonClick } = props;
if (evaluation) {
const { Rep, Grip } = __webpack_require__(17);
return span({
className: "objectBox objectBox-accessor objectTitle"
}, Rep({
...props,
object: evaluation.getterValue,
mode: props.mode || MODE.TINY,
defaultRep: Grip
}));
}
if (hasGetter(object) && onInvokeGetterButtonClick) {
return dom.button({
className: "invoke-getter",
title: "Invoke getter",
onClick: event => {
onInvokeGetterButtonClick();
event.stopPropagation();
}
});
}
const accessors = [];
if (hasGetter(object)) {
accessors.push("Getter");
}
if (hasSetter(object)) {
accessors.push("Setter");
}
return span({ className: "objectBox objectBox-accessor objectTitle" }, accessors.join(" & "));
}
function hasGetter(object) {
return object && object.get && object.get.type !== "undefined";
}
function hasSetter(object) {
return object && object.set && object.set.type !== "undefined";
}
function supportsObject(object, noGrip = false) {
if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
return true;
}
return false;
}
// Exports from this module
module.exports = {
rep: wrapRender(Accessor),
supportsObject
};
/***/ }),
/***/ 182:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
const { button, span } = __webpack_require__(2);
// Utils
const { isGrip, wrapRender } = __webpack_require__(4);
const { rep: StringRep } = __webpack_require__(18);
/**
* Renders Accessible object.
*/
Accessible.propTypes = {
object: PropTypes.object.isRequired,
inspectIconTitle: PropTypes.string,
nameMaxLength: PropTypes.number,
onAccessibleClick: PropTypes.func,
onAccessibleMouseOver: PropTypes.func,
onAccessibleMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func,
roleFirst: PropTypes.bool,
separatorText: PropTypes.string
};
function Accessible(props) {
const {
object,
inspectIconTitle,
nameMaxLength,
onAccessibleClick,
onAccessibleMouseOver,
onAccessibleMouseOut,
onInspectIconClick,
roleFirst,
separatorText
} = props;
const elements = getElements(object, nameMaxLength, roleFirst, separatorText);
const isInTree = object.preview && object.preview.isConnected === true;
const baseConfig = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-accessible"
};
let inspectIcon;
if (isInTree) {
if (onAccessibleClick) {
Object.assign(baseConfig, {
onClick: _ => onAccessibleClick(object),
className: `${baseConfig.className} clickable`
});
}
if (onAccessibleMouseOver) {
Object.assign(baseConfig, {
onMouseOver: _ => onAccessibleMouseOver(object)
});
}
if (onAccessibleMouseOut) {
Object.assign(baseConfig, {
onMouseOut: onAccessibleMouseOut
});
}
if (onInspectIconClick) {
inspectIcon = button({
className: "open-accessibility-inspector",
title: inspectIconTitle,
onClick: e => {
if (onAccessibleClick) {
e.stopPropagation();
}
onInspectIconClick(object, e);
}
});
}
}
return span(baseConfig, ...elements, inspectIcon);
}
function getElements(grip, nameMaxLength, roleFirst = false, separatorText = ": ") {
const { name, role } = grip.preview;
const elements = [];
if (name) {
elements.push(StringRep({
className: "accessible-name",
object: name,
cropLimit: nameMaxLength
}), span({ className: "separator" }, separatorText));
}
elements.push(span({ className: "accessible-role" }, role));
return roleFirst ? elements.reverse() : elements;
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return object.preview && object.typeName && object.typeName === "accessible";
}
// Exports from this module
module.exports = {
rep: wrapRender(Accessible),
supportsObject
};
/***/ }),
/***/ 183:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
const dom = __webpack_require__(2);
const { span } = dom;
// Reps
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const { rep: StringRep } = __webpack_require__(18);
/**
* Renders DOM attribute
*/
Attribute.propTypes = {
object: PropTypes.object.isRequired
};
function Attribute(props) {
const { object } = props;
const value = object.preview.value;
return span({
"data-link-actor-id": object.actor,
className: "objectBox-Attr"
}, span({ className: "attrName" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ className: "attrValue", object: value, title: value }));
}
function getTitle(grip) {
return grip.preview.nodeName;
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return getGripType(grip, noGrip) == "Attr" && grip.preview;
}
module.exports = {
rep: wrapRender(Attribute),
supportsObject
};
/***/ }),
/***/ 184:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Used to render JS built-in Date() object.
*/
DateTime.propTypes = {
object: PropTypes.object.isRequired
};
function DateTime(props) {
const grip = props.object;
let date;
try {
date = span({
"data-link-actor-id": grip.actor,
className: "objectBox"
}, getTitle(grip), span({ className: "Date" }, new Date(grip.preview.timestamp).toISOString()));
} catch (e) {
date = span({ className: "objectBox" }, "Invalid Date");
}
return date;
}
function getTitle(grip) {
return span({
className: "objectTitle"
}, `${grip.class} `);
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return getGripType(grip, noGrip) == "Date" && grip.preview;
}
// Exports from this module
module.exports = {
rep: wrapRender(DateTime),
supportsObject
};
/***/ }),
/***/ 185:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const {
getGripType,
isGrip,
getURLDisplayString,
wrapRender
} = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders DOM document object.
*/
Document.propTypes = {
object: PropTypes.object.isRequired
};
function Document(props) {
const grip = props.object;
const location = getLocation(grip);
return span({
"data-link-actor-id": grip.actor,
className: "objectBox objectBox-document"
}, getTitle(grip), location ? span({ className: "location" }, ` ${location}`) : null);
}
function getLocation(grip) {
const location = grip.preview.location;
return location ? getURLDisplayString(location) : null;
}
function getTitle(grip) {
return span({
className: "objectTitle"
}, grip.class);
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
const type = getGripType(object, noGrip);
return object.preview && (type === "HTMLDocument" || type === "XULDocument");
}
// Exports from this module
module.exports = {
rep: wrapRender(Document),
supportsObject
};
/***/ }),
/***/ 186:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders DOM documentType object.
*/
DocumentType.propTypes = {
object: PropTypes.object.isRequired
};
function DocumentType(props) {
const { object } = props;
const name = object && object.preview && object.preview.nodeName ? ` ${object.preview.nodeName}` : "";
return span({
"data-link-actor-id": props.object.actor,
className: "objectBox objectBox-document"
}, `<!DOCTYPE${name}>`);
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
const type = getGripType(object, noGrip);
return object.preview && type === "DocumentType";
}
// Exports from this module
module.exports = {
rep: wrapRender(DocumentType),
supportsObject
};
/***/ }),
/***/ 187:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { isGrip, wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const { rep } = __webpack_require__(58);
/**
* Renders DOM event objects.
*/
Event.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function Event(props) {
const gripProps = {
...props,
title: getTitle(props),
object: {
...props.object,
preview: {
...props.object.preview,
ownProperties: {}
}
}
};
if (gripProps.object.preview.target) {
Object.assign(gripProps.object.preview.ownProperties, {
target: gripProps.object.preview.target
});
}
Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
delete gripProps.object.preview.properties;
gripProps.object.ownPropertyLength = Object.keys(gripProps.object.preview.ownProperties).length;
switch (gripProps.object.class) {
case "MouseEvent":
gripProps.isInterestingProp = (type, value, name) => {
return ["target", "clientX", "clientY", "layerX", "layerY"].includes(name);
};
break;
case "KeyboardEvent":
gripProps.isInterestingProp = (type, value, name) => {
return ["target", "key", "charCode", "keyCode"].includes(name);
};
break;
case "MessageEvent":
gripProps.isInterestingProp = (type, value, name) => {
return ["target", "isTrusted", "data"].includes(name);
};
break;
default:
gripProps.isInterestingProp = (type, value, name) => {
// We want to show the properties in the order they are declared.
return Object.keys(gripProps.object.preview.ownProperties).includes(name);
};
}
return rep(gripProps);
}
function getTitle(props) {
const preview = props.object.preview;
let title = preview.type;
if (preview.eventKind == "key" && preview.modifiers && preview.modifiers.length) {
title = `${title} ${preview.modifiers.join("-")}`;
}
return title;
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && grip.preview.kind == "DOMEvent";
}
// Exports from this module
module.exports = {
rep: wrapRender(Event),
supportsObject
};
/***/ }),
/***/ 188:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Dependencies
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const PropRep = __webpack_require__(39);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a DOM Promise object.
*/
PromiseRep.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function PromiseRep(props) {
const object = props.object;
const { promiseState } = object;
const config = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-object"
};
if (props.mode === MODE.TINY) {
const { Rep } = __webpack_require__(17);
return span(config, getTitle(object), span({
className: "objectLeftBrace"
}, " { "), Rep({ object: promiseState.state }), span({
className: "objectRightBrace"
}, " }"));
}
const propsArray = getProps(props, promiseState);
return span(config, getTitle(object), span({
className: "objectLeftBrace"
}, " { "), ...propsArray, span({
className: "objectRightBrace"
}, " }"));
}
function getTitle(object) {
return span({
className: "objectTitle"
}, object.class);
}
function getProps(props, promiseState) {
const keys = ["state"];
if (Object.keys(promiseState).includes("value")) {
keys.push("value");
}
return keys.reduce((res, key, i) => {
const object = promiseState[key];
res = res.concat(PropRep({
...props,
mode: MODE.TINY,
name: `<${key}>`,
object,
equal: ": ",
suppressQuotes: true
}));
// Interleave commas between elements
if (i !== keys.length - 1) {
res.push(", ");
}
return res;
}, []);
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return getGripType(object, noGrip) == "Promise";
}
// Exports from this module
module.exports = {
rep: wrapRender(PromiseRep),
supportsObject
};
/***/ }),
/***/ 189:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a grip object with regular expression.
*/
RegExp.propTypes = {
object: PropTypes.object.isRequired
};
function RegExp(props) {
const { object } = props;
return span({
"data-link-actor-id": object.actor,
className: "objectBox objectBox-regexp regexpSource"
}, getSource(object));
}
function getSource(grip) {
return grip.displayString;
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return getGripType(object, noGrip) == "RegExp";
}
// Exports from this module
module.exports = {
rep: wrapRender(RegExp),
supportsObject
};
/***/ }),
/***/ 190:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const {
getGripType,
isGrip,
getURLDisplayString,
wrapRender
} = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a grip representing CSSStyleSheet
*/
StyleSheet.propTypes = {
object: PropTypes.object.isRequired
};
function StyleSheet(props) {
const grip = props.object;
return span({
"data-link-actor-id": grip.actor,
className: "objectBox objectBox-object"
}, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
}
function getTitle(grip) {
const title = "StyleSheet ";
return span({ className: "objectBoxTitle" }, title);
}
function getLocation(grip) {
// Embedded stylesheets don't have URL and so, no preview.
const url = grip.preview ? grip.preview.url : "";
return url ? getURLDisplayString(url) : "";
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return getGripType(object, noGrip) == "CSSStyleSheet";
}
// Exports from this module
module.exports = {
rep: wrapRender(StyleSheet),
supportsObject
};
/***/ }),
/***/ 191:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const {
isGrip,
cropString,
cropMultipleLines,
wrapRender
} = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const nodeConstants = __webpack_require__(94);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders DOM comment node.
*/
CommentNode.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
};
function CommentNode(props) {
const { object, mode = MODE.SHORT } = props;
let { textContent } = object.preview;
if (mode === MODE.TINY) {
textContent = cropMultipleLines(textContent, 30);
} else if (mode === MODE.SHORT) {
textContent = cropString(textContent, 50);
}
return span({
className: "objectBox theme-comment",
"data-link-actor-id": object.actor
}, `<!-- ${textContent} -->`);
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
}
// Exports from this module
module.exports = {
rep: wrapRender(CommentNode),
supportsObject
};
/***/ }),
/***/ 192:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Utils
const { isGrip, wrapRender } = __webpack_require__(4);
const { rep: StringRep, isLongString } = __webpack_require__(18);
const { MODE } = __webpack_require__(5);
const nodeConstants = __webpack_require__(94);
const dom = __webpack_require__(2);
const { span } = dom;
const MAX_ATTRIBUTE_LENGTH = 50;
/**
* Renders DOM element node.
*/
ElementNode.propTypes = {
object: PropTypes.object.isRequired,
inspectIconTitle: PropTypes.string,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeClick: PropTypes.func,
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function ElementNode(props) {
const {
object,
inspectIconTitle,
mode,
onDOMNodeClick,
onDOMNodeMouseOver,
onDOMNodeMouseOut,
onInspectIconClick
} = props;
const elements = getElements(object, mode);
const isInTree = object.preview && object.preview.isConnected === true;
const baseConfig = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-node"
};
let inspectIcon;
if (isInTree) {
if (onDOMNodeClick) {
Object.assign(baseConfig, {
onClick: _ => onDOMNodeClick(object),
className: `${baseConfig.className} clickable`
});
}
if (onDOMNodeMouseOver) {
Object.assign(baseConfig, {
onMouseOver: _ => onDOMNodeMouseOver(object)
});
}
if (onDOMNodeMouseOut) {
Object.assign(baseConfig, {
onMouseOut: onDOMNodeMouseOut
});
}
if (onInspectIconClick) {
inspectIcon = dom.button({
className: "open-inspector",
// TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
title: inspectIconTitle || "Click to select the node in the inspector",
onClick: e => {
if (onDOMNodeClick) {
e.stopPropagation();
}
onInspectIconClick(object, e);
}
});
}
}
return span(baseConfig, ...elements, inspectIcon);
}
function getElements(grip, mode) {
const {
attributes,
nodeName,
isAfterPseudoElement,
isBeforePseudoElement
} = grip.preview;
const nodeNameElement = span({
className: "tag-name"
}, nodeName);
if (isAfterPseudoElement || isBeforePseudoElement) {
return [span({ className: "attrName" }, `::${isAfterPseudoElement ? "after" : "before"}`)];
}
if (mode === MODE.TINY) {
const elements = [nodeNameElement];
if (attributes.id) {
elements.push(span({ className: "attrName" }, `#${attributes.id}`));
}
if (attributes.class) {
elements.push(span({ className: "attrName" }, attributes.class.trim().split(/\s+/).map(cls => `.${cls}`).join("")));
}
return elements;
}
const attributeKeys = Object.keys(attributes);
if (attributeKeys.includes("class")) {
attributeKeys.splice(attributeKeys.indexOf("class"), 1);
attributeKeys.unshift("class");
}
if (attributeKeys.includes("id")) {
attributeKeys.splice(attributeKeys.indexOf("id"), 1);
attributeKeys.unshift("id");
}
const attributeElements = attributeKeys.reduce((arr, name, i, keys) => {
const value = attributes[name];
let title = isLongString(value) ? value.initial : value;
if (title.length < MAX_ATTRIBUTE_LENGTH) {
title = null;
}
const attribute = span({}, span({ className: "attrName" }, name), span({ className: "attrEqual" }, "="), StringRep({
className: "attrValue",
object: value,
cropLimit: MAX_ATTRIBUTE_LENGTH,
title
}));
return arr.concat([" ", attribute]);
}, []);
return [span({ className: "angleBracket" }, "<"), nodeNameElement, ...attributeElements, span({ className: "angleBracket" }, ">")];
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
}
// Exports from this module
module.exports = {
rep: wrapRender(ElementNode),
supportsObject,
MAX_ATTRIBUTE_LENGTH
};
/***/ }),
/***/ 193:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { isGrip, cropString, wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders DOM #text node.
*/
TextNode.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function TextNode(props) {
const {
object: grip,
mode = MODE.SHORT,
onDOMNodeMouseOver,
onDOMNodeMouseOut,
onInspectIconClick
} = props;
const baseConfig = {
"data-link-actor-id": grip.actor,
className: "objectBox objectBox-textNode"
};
let inspectIcon;
const isInTree = grip.preview && grip.preview.isConnected === true;
if (isInTree) {
if (onDOMNodeMouseOver) {
Object.assign(baseConfig, {
onMouseOver: _ => onDOMNodeMouseOver(grip)
});
}
if (onDOMNodeMouseOut) {
Object.assign(baseConfig, {
onMouseOut: onDOMNodeMouseOut
});
}
if (onInspectIconClick) {
inspectIcon = dom.button({
className: "open-inspector",
draggable: false,
// TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
title: "Click to select the node in the inspector",
onClick: e => onInspectIconClick(grip, e)
});
}
}
if (mode === MODE.TINY) {
return span(baseConfig, getTitle(grip), inspectIcon);
}
return span(baseConfig, getTitle(grip), span({ className: "nodeValue" }, " ", `"${getTextContent(grip)}"`), inspectIcon);
}
function getTextContent(grip) {
return cropString(grip.preview.textContent);
}
function getTitle(grip) {
const title = "#text";
return span({}, title);
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && grip.class == "Text";
}
// Exports from this module
module.exports = {
rep: wrapRender(TextNode),
supportsObject
};
/***/ }),
/***/ 194:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const {
getGripType,
isGrip,
getURLDisplayString,
wrapRender
} = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a grip representing a window.
*/
WindowRep.propTypes = {
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
object: PropTypes.object.isRequired
};
function WindowRep(props) {
const { mode, object } = props;
const config = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-Window"
};
if (mode === MODE.TINY) {
return span(config, getTitle(object));
}
return span(config, getTitle(object, true), span({ className: "location" }, getLocation(object)));
}
function getTitle(object, trailingSpace) {
let title = object.displayClass || object.class || "Window";
if (trailingSpace === true) {
title = `${title} `;
}
return span({ className: "objectTitle" }, title);
}
function getLocation(object) {
return getURLDisplayString(object.preview.url);
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return object.preview && getGripType(object, noGrip) == "Window";
}
// Exports from this module
module.exports = {
rep: wrapRender(WindowRep),
supportsObject
};
/***/ }),
/***/ 195:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { isGrip, wrapRender } = __webpack_require__(4);
const String = __webpack_require__(18).rep;
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a grip object with textual data.
*/
ObjectWithText.propTypes = {
object: PropTypes.object.isRequired
};
function ObjectWithText(props) {
const grip = props.object;
return span({
"data-link-actor-id": grip.actor,
className: `objectTitle objectBox objectBox-${getType(grip)}`
}, `${getType(grip)} `, getDescription(grip));
}
function getType(grip) {
return grip.class;
}
function getDescription(grip) {
return String({
object: grip.preview.text
});
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && grip.preview.kind == "ObjectWithText";
}
// Exports from this module
module.exports = {
rep: wrapRender(ObjectWithText),
supportsObject
};
/***/ }),
/***/ 196:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { isGrip, getURLDisplayString, wrapRender } = __webpack_require__(4);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders a grip object with URL data.
*/
ObjectWithURL.propTypes = {
object: PropTypes.object.isRequired
};
function ObjectWithURL(props) {
const grip = props.object;
return span({
"data-link-actor-id": grip.actor,
className: `objectBox objectBox-${getType(grip)}`
}, getTitle(grip), span({ className: "objectPropValue" }, getDescription(grip)));
}
function getTitle(grip) {
return span({ className: "objectTitle" }, `${getType(grip)} `);
}
function getType(grip) {
return grip.class;
}
function getDescription(grip) {
return getURLDisplayString(grip.preview.url);
}
// Registration
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && grip.preview.kind == "ObjectWithURL";
}
// Exports from this module
module.exports = {
rep: wrapRender(ObjectWithURL),
supportsObject
};
/***/ }),
/***/ 197:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const ObjectInspector = __webpack_require__(198);
const utils = __webpack_require__(61);
const reducer = __webpack_require__(60);
module.exports = { ObjectInspector, utils, reducer };
/***/ }),
/***/ 198:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _devtoolsComponents = __webpack_require__(73);
var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
const { Component, createFactory, createElement } = __webpack_require__(1);
const { connect } = __webpack_require__(102);
const actions = __webpack_require__(199);
const selectors = __webpack_require__(60);
const Tree = createFactory(_devtoolsComponents2.default.Tree);
__webpack_require__(200);
const ObjectInspectorItem = createFactory(__webpack_require__(201));
const classnames = __webpack_require__(6);
const Utils = __webpack_require__(61);
const { renderRep, shouldRenderRootsInReps } = Utils;
const {
getChildrenWithEvaluations,
getActor,
getParent,
nodeIsPrimitive,
nodeHasGetter,
nodeHasSetter
} = Utils.node;
// This implements a component that renders an interactive inspector
// for looking at JavaScript objects. It expects descriptions of
// objects from the protocol, and will dynamically fetch children
// properties as objects are expanded.
//
// If you want to inspect a single object, pass the name and the
// protocol descriptor of it:
//
// ObjectInspector({
// name: "foo",
// desc: { writable: true, ..., { value: { actor: "1", ... }}},
// ...
// })
//
// If you want multiple top-level objects (like scopes), you can pass
// an array of manually constructed nodes as `roots`:
//
// ObjectInspector({
// roots: [{ name: ... }, ...],
// ...
// });
// There are 3 types of nodes: a simple node with a children array, an
// object that has properties that should be children when they are
// fetched, and a primitive value that should be displayed with no
// children.
class ObjectInspector extends Component {
constructor(props) {
super();
this.cachedNodes = new Map();
const self = this;
self.getItemChildren = this.getItemChildren.bind(this);
self.isNodeExpandable = this.isNodeExpandable.bind(this);
self.setExpanded = this.setExpanded.bind(this);
self.focusItem = this.focusItem.bind(this);
self.activateItem = this.activateItem.bind(this);
self.getRoots = this.getRoots.bind(this);
self.getNodeKey = this.getNodeKey.bind(this);
}
componentWillMount() {
this.roots = this.props.roots;
this.focusedItem = this.props.focusedItem;
this.activeItem = this.props.activeItem;
}
componentWillUpdate(nextProps) {
this.removeOutdatedNodesFromCache(nextProps);
if (this.roots !== nextProps.roots) {
// Since the roots changed, we assume the properties did as well,
// so we need to cleanup the component internal state.
this.roots = nextProps.roots;
this.focusedItem = nextProps.focusedItem;
this.activeItem = nextProps.activeItem;
if (this.props.rootsChanged) {
this.props.rootsChanged();
}
return;
}
}
removeOutdatedNodesFromCache(nextProps) {
// When the roots changes, we can wipe out everything.
if (this.roots !== nextProps.roots) {
this.cachedNodes.clear();
return;
}
// If there are new evaluations, we want to remove the existing cached
// nodes from the cache.
if (nextProps.evaluations > this.props.evaluations) {
for (const key of nextProps.evaluations.keys()) {
if (!this.props.evaluations.has(key)) {
this.cachedNodes.delete(key);
}
}
}
}
shouldComponentUpdate(nextProps) {
const { expandedPaths, loadedProperties, evaluations } = this.props;
// We should update if:
// - there are new loaded properties
// - OR there are new evaluations
// - OR the expanded paths number changed, and all of them have properties
// loaded
// - OR the expanded paths number did not changed, but old and new sets
// differ
// - OR the focused node changed.
// - OR the active node changed.
return loadedProperties.size !== nextProps.loadedProperties.size || evaluations.size !== nextProps.evaluations.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key)) || this.focusedItem !== nextProps.focusedItem || this.activeItem !== nextProps.activeItem || this.roots !== nextProps.roots;
}
componentWillUnmount() {
this.props.closeObjectInspector();
}
getItemChildren(item) {
const { loadedProperties, evaluations } = this.props;
const { cachedNodes } = this;
return getChildrenWithEvaluations({
evaluations,
loadedProperties,
cachedNodes,
item
});
}
getRoots() {
return this.props.roots;
}
getNodeKey(item) {
return item.path && typeof item.path.toString === "function" ? item.path.toString() : JSON.stringify(item);
}
isNodeExpandable(item) {
if (nodeIsPrimitive(item)) {
return false;
}
if (nodeHasSetter(item) || nodeHasGetter(item)) {
return false;
}
return true;
}
setExpanded(item, expand) {
if (!this.isNodeExpandable(item)) {
return;
}
const {
nodeExpand,
nodeCollapse,
recordTelemetryEvent,
roots
} = this.props;
if (expand === true) {
const actor = getActor(item, roots);
nodeExpand(item, actor);
if (recordTelemetryEvent) {
recordTelemetryEvent("object_expanded");
}
} else {
nodeCollapse(item);
}
}
focusItem(item) {
const { focusable = true, onFocus } = this.props;
if (focusable && this.focusedItem !== item) {
this.focusedItem = item;
this.forceUpdate();
if (onFocus) {
onFocus(item);
}
}
}
activateItem(item) {
const { focusable = true, onActivate } = this.props;
if (focusable && this.activeItem !== item) {
this.activeItem = item;
this.forceUpdate();
if (onActivate) {
onActivate(item);
}
}
}
render() {
const {
autoExpandAll = true,
autoExpandDepth = 1,
focusable = true,
disableWrap = false,
expandedPaths,
inline
} = this.props;
return Tree({
className: classnames({
inline,
nowrap: disableWrap,
"object-inspector": true
}),
autoExpandAll,
autoExpandDepth,
isExpanded: item => expandedPaths && expandedPaths.has(item.path),
isExpandable: this.isNodeExpandable,
focused: this.focusedItem,
active: this.activeItem,
getRoots: this.getRoots,
getParent,
getChildren: this.getItemChildren,
getKey: this.getNodeKey,
onExpand: item => this.setExpanded(item, true),
onCollapse: item => this.setExpanded(item, false),
onFocus: focusable ? this.focusItem : null,
onActivate: focusable ? this.activateItem : null,
renderItem: (item, depth, focused, arrow, expanded) => ObjectInspectorItem({
...this.props,
item,
depth,
focused,
arrow,
expanded,
setExpanded: this.setExpanded
})
});
}
}
function mapStateToProps(state, props) {
return {
expandedPaths: selectors.getExpandedPaths(state),
loadedProperties: selectors.getLoadedProperties(state),
evaluations: selectors.getEvaluations(state)
};
}
const OI = connect(mapStateToProps, actions)(ObjectInspector);
module.exports = props => {
const { roots } = props;
if (shouldRenderRootsInReps(roots)) {
return renderRep(roots[0], props);
}
return createElement(OI, props);
};
/***/ }),
/***/ 199:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const { loadItemProperties } = __webpack_require__(103); /* 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/>. */
const { getLoadedProperties, getActors } = __webpack_require__(60);
/**
* This action is responsible for expanding a given node, which also means that
* it will call the action responsible to fetch properties.
*/
function nodeExpand(node, actor) {
return async ({ dispatch, getState }) => {
dispatch({ type: "NODE_EXPAND", data: { node } });
dispatch(nodeLoadProperties(node, actor));
};
}
function nodeCollapse(node) {
return {
type: "NODE_COLLAPSE",
data: { node }
};
}
/*
* This action checks if we need to fetch properties, entries, prototype and
* symbols for a given node. If we do, it will call the appropriate ObjectClient
* functions.
*/
function nodeLoadProperties(node, actor) {
return async ({ dispatch, client, getState }) => {
const state = getState();
const loadedProperties = getLoadedProperties(state);
if (loadedProperties.has(node.path)) {
return;
}
try {
const properties = await loadItemProperties(node, client.createObjectClient, client.createLongStringClient, loadedProperties);
dispatch(nodePropertiesLoaded(node, actor, properties));
} catch (e) {
console.error(e);
}
};
}
function nodePropertiesLoaded(node, actor, properties) {
return {
type: "NODE_PROPERTIES_LOADED",
data: { node, actor, properties }
};
}
function closeObjectInspector() {
return async ({ getState, client }) => {
releaseActors(getState(), client);
};
}
/*
* This action is dispatched when the `roots` prop, provided by a consumer of
* the ObjectInspector (inspector, console, …), is modified. It will clean the
* internal state properties (expandedPaths, loadedProperties, …) and release
* the actors consumed with the previous roots.
* It takes a props argument which reflects what is passed by the upper-level
* consumer.
*/
function rootsChanged(props) {
return async ({ dispatch, client, getState }) => {
releaseActors(getState(), client);
dispatch({
type: "ROOTS_CHANGED",
data: props
});
};
}
function releaseActors(state, client) {
const actors = getActors(state);
for (const actor of actors) {
client.releaseActor(actor);
}
}
function invokeGetter(node, targetGrip, receiverId, getterName) {
return async ({ dispatch, client, getState }) => {
try {
const objectClient = client.createObjectClient(targetGrip);
const result = await objectClient.getPropertyValue(getterName, receiverId);
dispatch({
type: "GETTER_INVOKED",
data: {
node,
result
}
});
} catch (e) {
console.error(e);
}
};
}
module.exports = {
closeObjectInspector,
invokeGetter,
nodeExpand,
nodeCollapse,
nodeLoadProperties,
nodePropertiesLoaded,
rootsChanged
};
/***/ }),
/***/ 2:
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_2__;
/***/ }),
/***/ 20:
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_20__;
/***/ }),
/***/ 200:
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }),
/***/ 201:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _devtoolsServices = __webpack_require__(20);
var _devtoolsServices2 = _interopRequireDefault(_devtoolsServices);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
const { Component } = __webpack_require__(1);
const dom = __webpack_require__(2);
const { appinfo } = _devtoolsServices2.default;
const isMacOS = appinfo.OS === "Darwin";
const classnames = __webpack_require__(6);
const { MODE } = __webpack_require__(5);
const Utils = __webpack_require__(61);
const {
getValue,
nodeHasAccessors,
nodeHasProperties,
nodeIsBlock,
nodeIsDefaultProperties,
nodeIsFunction,
nodeIsGetter,
nodeIsMapEntry,
nodeIsMissingArguments,
nodeIsOptimizedOut,
nodeIsPrimitive,
nodeIsPrototype,
nodeIsSetter,
nodeIsUninitializedBinding,
nodeIsUnmappedBinding,
nodeIsUnscopedBinding,
nodeIsWindow,
nodeIsLongString,
nodeHasFullText,
nodeHasGetter,
getNonPrototypeParentGripValue,
getParentGripValue
} = Utils.node;
class ObjectInspectorItem extends Component {
// eslint-disable-next-line complexity
getLabelAndValue() {
const { item, depth, expanded, mode } = this.props;
const label = item.name;
const isPrimitive = nodeIsPrimitive(item);
if (nodeIsOptimizedOut(item)) {
return {
label,
value: dom.span({ className: "unavailable" }, "(optimized away)")
};
}
if (nodeIsUninitializedBinding(item)) {
return {
label,
value: dom.span({ className: "unavailable" }, "(uninitialized)")
};
}
if (nodeIsUnmappedBinding(item)) {
return {
label,
value: dom.span({ className: "unavailable" }, "(unmapped)")
};
}
if (nodeIsUnscopedBinding(item)) {
return {
label,
value: dom.span({ className: "unavailable" }, "(unscoped)")
};
}
const itemValue = getValue(item);
const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
if (nodeIsMissingArguments(item) || unavailable) {
return {
label,
value: dom.span({ className: "unavailable" }, "(unavailable)")
};
}
if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (mode === MODE.TINY || !mode)) {
return {
label: Utils.renderRep(item, {
...this.props,
functionName: label
})
};
}
if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || nodeIsLongString(item) || isPrimitive) {
const repProps = { ...this.props };
if (depth > 0) {
repProps.mode = mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
}
if (expanded) {
repProps.mode = MODE.TINY;
}
if (nodeIsLongString(item)) {
repProps.member = {
open: nodeHasFullText(item) && expanded
};
}
if (nodeHasGetter(item)) {
const targetGrip = getParentGripValue(item);
const receiverGrip = getNonPrototypeParentGripValue(item);
if (targetGrip && receiverGrip) {
Object.assign(repProps, {
onInvokeGetterButtonClick: () => this.props.invokeGetter(item, targetGrip, receiverGrip.actor, item.name)
});
}
}
return {
label,
value: Utils.renderRep(item, repProps)
};
}
return {
label
};
}
getTreeItemProps() {
const {
item,
depth,
focused,
expanded,
onCmdCtrlClick,
onDoubleClick,
dimTopLevelWindow
} = this.props;
const parentElementProps = {
className: classnames("node object-node", {
focused,
lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || nodeIsGetter(item) || nodeIsSetter(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0),
block: nodeIsBlock(item)
}),
onClick: e => {
if (onCmdCtrlClick && (isMacOS && e.metaKey || !isMacOS && e.ctrlKey)) {
onCmdCtrlClick(item, {
depth,
event: e,
focused,
expanded
});
e.stopPropagation();
return;
}
// If this click happened because the user selected some text, bail out.
// Note that if the user selected some text before and then clicks here,
// the previously selected text will be first unselected, unless the
// user clicked on the arrow itself. Indeed because the arrow is an
// image, clicking on it does not remove any existing text selection.
// So we need to also check if the arrow was clicked.
if (Utils.selection.documentHasSelection() && !(e.target && e.target.matches && e.target.matches(".arrow"))) {
e.stopPropagation();
}
}
};
if (onDoubleClick) {
parentElementProps.onDoubleClick = e => {
e.stopPropagation();
onDoubleClick(item, {
depth,
focused,
expanded
});
};
}
return parentElementProps;
}
renderLabel(label) {
if (label === null || typeof label === "undefined") {
return null;
}
const { item, depth, focused, expanded, onLabelClick } = this.props;
return dom.span({
className: "object-label",
onClick: onLabelClick ? event => {
event.stopPropagation();
// If the user selected text, bail out.
if (Utils.selection.documentHasSelection()) {
return;
}
onLabelClick(item, {
depth,
focused,
expanded,
setExpanded: this.props.setExpanded
});
} : undefined
}, label);
}
render() {
const { arrow } = this.props;
const { label, value } = this.getLabelAndValue();
const labelElement = this.renderLabel(label);
const delimiter = value && labelElement ? dom.span({ className: "object-delimiter" }, ": ") : null;
return dom.div(this.getTreeItemProps(), arrow, labelElement, delimiter, value);
}
}
module.exports = ObjectInspectorItem;
/***/ }),
/***/ 202:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
function documentHasSelection() {
const selection = getSelection();
if (!selection) {
return false;
}
return selection.type === "Range";
}
module.exports = {
documentHasSelection
};
/***/ }),
/***/ 38:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const dom = __webpack_require__(2);
const PropTypes = __webpack_require__(0);
const { wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const { span } = dom;
const ModePropType = PropTypes.oneOf(
// @TODO Change this to Object.values when supported in Node's version of V8
Object.keys(MODE).map(key => MODE[key]));
/**
* Renders an array. The array is enclosed by left and right bracket
* and the max number of rendered items depends on the current mode.
*/
ArrayRep.propTypes = {
mode: ModePropType,
object: PropTypes.array.isRequired
};
function ArrayRep(props) {
const { object, mode = MODE.SHORT } = props;
let items;
let brackets;
const needSpace = function (space) {
return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
};
if (mode === MODE.TINY) {
const isEmpty = object.length === 0;
if (isEmpty) {
items = [];
} else {
items = [span({
className: "more-ellipsis",
title: "more…"
}, "…")];
}
brackets = needSpace(false);
} else {
items = arrayIterator(props, object, maxLengthMap.get(mode));
brackets = needSpace(items.length > 0);
}
return span({
className: "objectBox objectBox-array"
}, span({
className: "arrayLeftBracket"
}, brackets.left), ...items, span({
className: "arrayRightBracket"
}, brackets.right));
}
function arrayIterator(props, array, max) {
const items = [];
for (let i = 0; i < array.length && i < max; i++) {
const config = {
mode: MODE.TINY,
delim: i == array.length - 1 ? "" : ", "
};
let item;
try {
item = ItemRep({
...props,
...config,
object: array[i]
});
} catch (exc) {
item = ItemRep({
...props,
...config,
object: exc
});
}
items.push(item);
}
if (array.length > max) {
items.push(span({
className: "more-ellipsis",
title: "more…"
}, "…"));
}
return items;
}
/**
* Renders array item. Individual values are separated by a comma.
*/
ItemRep.propTypes = {
object: PropTypes.any.isRequired,
delim: PropTypes.string.isRequired,
mode: ModePropType
};
function ItemRep(props) {
const { Rep } = __webpack_require__(17);
const { object, delim, mode } = props;
return span({}, Rep({
...props,
object: object,
mode: mode
}), delim);
}
function getLength(object) {
return object.length;
}
function supportsObject(object, noGrip = false) {
return noGrip && (Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]");
}
const maxLengthMap = new Map();
maxLengthMap.set(MODE.SHORT, 3);
maxLengthMap.set(MODE.LONG, 10);
// Exports from this module
module.exports = {
rep: wrapRender(ArrayRep),
supportsObject,
maxLengthMap,
getLength,
ModePropType
};
/***/ }),
/***/ 39:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { maybeEscapePropertyName, wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const { span } = __webpack_require__(2);
/**
* Property for Obj (local JS objects), Grip (remote JS objects)
* and GripMap (remote JS maps and weakmaps) reps.
* It's used to render object properties.
*/
PropRep.propTypes = {
// Property name.
name: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
// Equal character rendered between property name and value.
equal: PropTypes.string,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func,
// Normally a PropRep will quote a property name that isn't valid
// when unquoted; but this flag can be used to suppress the
// quoting.
suppressQuotes: PropTypes.bool
};
/**
* Function that given a name, a delimiter and an object returns an array
* of React elements representing an object property (e.g. `name: value`)
*
* @param {Object} props
* @return {Array} Array of React elements.
*/
function PropRep(props) {
const Grip = __webpack_require__(58);
const { Rep } = __webpack_require__(17);
let { name, mode, equal, suppressQuotes } = props;
let key;
// The key can be a simple string, for plain objects,
// or another object for maps and weakmaps.
if (typeof name === "string") {
if (!suppressQuotes) {
name = maybeEscapePropertyName(name);
}
key = span({ className: "nodeName" }, name);
} else {
key = Rep({
...props,
className: "nodeName",
object: name,
mode: mode || MODE.TINY,
defaultRep: Grip
});
}
return [key, span({
className: "objectEqual"
}, equal), Rep({ ...props })];
}
// Exports from this module
module.exports = wrapRender(PropRep);
/***/ }),
/***/ 4:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const validProtocols = /(http|https|ftp|data|resource|chrome):/i;
const tokenSplitRegex = /(\s|\'|\"|\\)+/;
const ELLIPSIS = "\u2026";
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Returns true if the given object is a grip (see RDP protocol)
*/
function isGrip(object) {
return object && object.actor;
}
function escapeNewLines(value) {
return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
}
// Map from character code to the corresponding escape sequence. \0
// isn't here because it would require special treatment in some
// situations. \b, \f, and \v aren't here because they aren't very
// common. \' isn't here because there's no need, we only
// double-quote strings.
const escapeMap = {
// Tab.
9: "\\t",
// Newline.
0xa: "\\n",
// Carriage return.
0xd: "\\r",
// Quote.
0x22: '\\"',
// Backslash.
0x5c: "\\\\"
};
// Regexp that matches any character we might possibly want to escape.
// Note that we over-match here, because it's difficult to, say, match
// an unpaired surrogate with a regexp. The details are worked out by
// the replacement function; see |escapeString|.
const escapeRegexp = new RegExp("[" +
// Quote and backslash.
'"\\\\' +
// Controls.
"\x00-\x1f" +
// More controls.
"\x7f-\x9f" +
// BOM
"\ufeff" +
// Specials, except for the replacement character.
"\ufff0-\ufffc\ufffe\uffff" +
// Surrogates.
"\ud800-\udfff" +
// Mathematical invisibles.
"\u2061-\u2064" +
// Line and paragraph separators.
"\u2028-\u2029" +
// Private use area.
"\ue000-\uf8ff" + "]", "g");
/**
* Escape a string so that the result is viewable and valid JS.
* Control characters, other invisibles, invalid characters,
* backslash, and double quotes are escaped. The resulting string is
* surrounded by double quotes.
*
* @param {String} str
* the input
* @param {Boolean} escapeWhitespace
* if true, TAB, CR, and NL characters will be escaped
* @return {String} the escaped string
*/
function escapeString(str, escapeWhitespace) {
return `"${str.replace(escapeRegexp, (match, offset) => {
const c = match.charCodeAt(0);
if (c in escapeMap) {
if (!escapeWhitespace && (c === 9 || c === 0xa || c === 0xd)) {
return match[0];
}
return escapeMap[c];
}
if (c >= 0xd800 && c <= 0xdfff) {
// Find the full code point containing the surrogate, with a
// special case for a trailing surrogate at the start of the
// string.
if (c >= 0xdc00 && offset > 0) {
--offset;
}
const codePoint = str.codePointAt(offset);
if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
// Unpaired surrogate.
return `\\u${codePoint.toString(16)}`;
} else if (codePoint >= 0xf0000 && codePoint <= 0x10fffd) {
// Private use area. Because we visit each pair of a such a
// character, return the empty string for one half and the
// real result for the other, to avoid duplication.
if (c <= 0xdbff) {
return `\\u{${codePoint.toString(16)}}`;
}
return "";
}
// Other surrogate characters are passed through.
return match;
}
return `\\u${`0000${c.toString(16)}`.substr(-4)}`;
})}"`;
}
/**
* Escape a property name, if needed. "Escaping" in this context
* means surrounding the property name with quotes.
*
* @param {String}
* name the property name
* @return {String} either the input, or the input surrounded by
* quotes, properly quoted in JS syntax.
*/
function maybeEscapePropertyName(name) {
// Quote the property name if it needs quoting. This particular
// test is an approximation; see
// https://mathiasbynens.be/notes/javascript-properties. However,
// the full solution requires a fair amount of Unicode data, and so
// let's defer that until either it's important, or the \p regexp
// syntax lands, see
// https://github.com/tc39/proposal-regexp-unicode-property-escapes.
if (!/^\w+$/.test(name)) {
name = escapeString(name);
}
return name;
}
function cropMultipleLines(text, limit) {
return escapeNewLines(cropString(text, limit));
}
function rawCropString(text, limit, alternativeText = ELLIPSIS) {
// Crop the string only if a limit is actually specified.
if (!limit || limit <= 0) {
return text;
}
// Set the limit at least to the length of the alternative text
// plus one character of the original text.
if (limit <= alternativeText.length) {
limit = alternativeText.length + 1;
}
const halfLimit = (limit - alternativeText.length) / 2;
if (text.length > limit) {
return text.substr(0, Math.ceil(halfLimit)) + alternativeText + text.substr(text.length - Math.floor(halfLimit));
}
return text;
}
function cropString(text, limit, alternativeText) {
return rawCropString(sanitizeString(`${text}`), limit, alternativeText);
}
function sanitizeString(text) {
// Replace all non-printable characters, except of
// (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
// with unicode replacement character (u+fffd).
// eslint-disable-next-line no-control-regex
const re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "g");
return text.replace(re, "\ufffd");
}
function parseURLParams(url) {
url = new URL(url);
return parseURLEncodedText(url.searchParams);
}
function parseURLEncodedText(text) {
const params = [];
// In case the text is empty just return the empty parameters
if (text == "") {
return params;
}
const searchParams = new URLSearchParams(text);
const entries = [...searchParams.entries()];
return entries.map(entry => {
return {
name: entry[0],
value: entry[1]
};
});
}
function getFileName(url) {
const split = splitURLBase(url);
return split.name;
}
function splitURLBase(url) {
if (!isDataURL(url)) {
return splitURLTrue(url);
}
return {};
}
function getURLDisplayString(url) {
return cropString(url);
}
function isDataURL(url) {
return url && url.substr(0, 5) == "data:";
}
function splitURLTrue(url) {
const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
const m = reSplitFile.exec(url);
if (!m) {
return {
name: url,
path: url
};
} else if (m[4] == "" && m[5] == "") {
return {
protocol: m[1],
domain: m[2],
path: m[3],
name: m[3] != "/" ? m[3] : m[2]
};
}
return {
protocol: m[1],
domain: m[2],
path: m[2] + m[3],
name: m[4] + m[5]
};
}
/**
* Wrap the provided render() method of a rep in a try/catch block that will
* render a fallback rep if the render fails.
*/
function wrapRender(renderMethod) {
const wrappedFunction = function (props) {
try {
return renderMethod.call(this, props);
} catch (e) {
console.error(e);
return span({
className: "objectBox objectBox-failure",
title: "This object could not be rendered, " + "please file a bug on bugzilla.mozilla.org"
},
/* Labels have to be hardcoded for reps, see Bug 1317038. */
"Invalid object");
}
};
wrappedFunction.propTypes = renderMethod.propTypes;
return wrappedFunction;
}
/**
* Get preview items from a Grip.
*
* @param {Object} Grip from which we want the preview items
* @return {Array} Array of the preview items of the grip, or an empty array
* if the grip does not have preview items
*/
function getGripPreviewItems(grip) {
if (!grip) {
return [];
}
// Promise resolved value Grip
if (grip.promiseState && grip.promiseState.value) {
return [grip.promiseState.value];
}
// Array Grip
if (grip.preview && grip.preview.items) {
return grip.preview.items;
}
// Node Grip
if (grip.preview && grip.preview.childNodes) {
return grip.preview.childNodes;
}
// Set or Map Grip
if (grip.preview && grip.preview.entries) {
return grip.preview.entries.reduce((res, entry) => res.concat(entry), []);
}
// Event Grip
if (grip.preview && grip.preview.target) {
const keys = Object.keys(grip.preview.properties);
const values = Object.values(grip.preview.properties);
return [grip.preview.target, ...keys, ...values];
}
// RegEx Grip
if (grip.displayString) {
return [grip.displayString];
}
// Generic Grip
if (grip.preview && grip.preview.ownProperties) {
let propertiesValues = Object.values(grip.preview.ownProperties).map(property => property.value || property);
const propertyKeys = Object.keys(grip.preview.ownProperties);
propertiesValues = propertiesValues.concat(propertyKeys);
// ArrayBuffer Grip
if (grip.preview.safeGetterValues) {
propertiesValues = propertiesValues.concat(Object.values(grip.preview.safeGetterValues).map(property => property.getterValue || property));
}
return propertiesValues;
}
return [];
}
/**
* Get the type of an object.
*
* @param {Object} Grip from which we want the type.
* @param {boolean} noGrip true if the object is not a grip.
* @return {boolean}
*/
function getGripType(object, noGrip) {
if (noGrip || Object(object) !== object) {
return typeof object;
}
if (object.type === "object") {
return object.class;
}
return object.type;
}
/**
* Determines whether a grip is a string containing a URL.
*
* @param string grip
* The grip, which may contain a URL.
* @return boolean
* Whether the grip is a string containing a URL.
*/
function containsURL(grip) {
// An URL can't be shorter than 5 char (e.g. "ftp:").
if (typeof grip !== "string" || grip.length < 5) {
return false;
}
return validProtocols.test(grip);
}
/**
* Determines whether a string token is a valid URL.
*
* @param string token
* The token.
* @return boolean
* Whenther the token is a URL.
*/
function isURL(token) {
try {
if (!validProtocols.test(token)) {
return false;
}
new URL(token);
return true;
} catch (e) {
return false;
}
}
/**
* Returns new array in which `char` are interleaved between the original items.
*
* @param {Array} items
* @param {String} char
* @returns Array
*/
function interleave(items, char) {
return items.reduce((res, item, index) => {
if (index !== items.length - 1) {
return res.concat(item, char);
}
return res.concat(item);
}, []);
}
const ellipsisElement = span({
key: "more",
className: "more-ellipsis",
title: `more${ELLIPSIS}`
}, ELLIPSIS);
module.exports = {
interleave,
isGrip,
isURL,
cropString,
containsURL,
rawCropString,
sanitizeString,
escapeString,
wrapRender,
cropMultipleLines,
parseURLParams,
parseURLEncodedText,
getFileName,
getURLDisplayString,
maybeEscapePropertyName,
getGripPreviewItems,
getGripType,
tokenSplitRegex,
ellipsisElement,
ELLIPSIS
};
/***/ }),
/***/ 5:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
module.exports = {
MODE: {
TINY: Symbol("TINY"),
SHORT: Symbol("SHORT"),
LONG: Symbol("LONG")
}
};
/***/ }),
/***/ 54:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const { MODE } = __webpack_require__(5);
const { REPS, getRep } = __webpack_require__(17);
const objectInspector = __webpack_require__(197);
const {
parseURLEncodedText,
parseURLParams,
maybeEscapePropertyName,
getGripPreviewItems
} = __webpack_require__(4);
module.exports = {
REPS,
getRep,
MODE,
maybeEscapePropertyName,
parseURLEncodedText,
parseURLParams,
getGripPreviewItems,
objectInspector
};
/***/ }),
/***/ 58:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Dependencies
const { interleave, isGrip, wrapRender } = __webpack_require__(4);
const PropRep = __webpack_require__(39);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
/**
* Renders generic grip. Grip is client representation
* of remote JS object and is used as an input object
* for this rep component.
*/
GripRep.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
isInterestingProp: PropTypes.func,
title: PropTypes.string,
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func,
noGrip: PropTypes.bool
};
const DEFAULT_TITLE = "Object";
function GripRep(props) {
const { mode = MODE.SHORT, object } = props;
const config = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-object"
};
if (mode === MODE.TINY) {
const propertiesLength = getPropertiesLength(object);
const tinyModeItems = [];
if (getTitle(props, object) !== DEFAULT_TITLE) {
tinyModeItems.push(getTitleElement(props, object));
} else {
tinyModeItems.push(span({
className: "objectLeftBrace"
}, "{"), propertiesLength > 0 ? span({
key: "more",
className: "more-ellipsis",
title: "more…"
}, "…") : null, span({
className: "objectRightBrace"
}, "}"));
}
return span(config, ...tinyModeItems);
}
const propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
return span(config, getTitleElement(props, object), span({
className: "objectLeftBrace"
}, " { "), ...interleave(propsArray, ", "), span({
className: "objectRightBrace"
}, " }"));
}
function getTitleElement(props, object) {
return span({
className: "objectTitle"
}, getTitle(props, object));
}
function getTitle(props, object) {
return props.title || object.class || DEFAULT_TITLE;
}
function getPropertiesLength(object) {
let propertiesLength = object.preview && object.preview.ownPropertiesLength ? object.preview.ownPropertiesLength : object.ownPropertyLength;
if (object.preview && object.preview.safeGetterValues) {
propertiesLength += Object.keys(object.preview.safeGetterValues).length;
}
if (object.preview && object.preview.ownSymbols) {
propertiesLength += object.preview.ownSymbolsLength;
}
return propertiesLength;
}
function safePropIterator(props, object, max) {
max = typeof max === "undefined" ? maxLengthMap.get(MODE.SHORT) : max;
try {
return propIterator(props, object, max);
} catch (err) {
console.error(err);
}
return [];
}
function propIterator(props, object, max) {
if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
const { Rep } = __webpack_require__(17);
return [Rep({
object: object.preview.wrappedValue,
mode: props.mode || MODE.TINY,
defaultRep: Grip
})];
}
// Property filter. Show only interesting properties to the user.
const isInterestingProp = props.isInterestingProp || ((type, value) => {
return type == "boolean" || type == "number" || type == "string" && value.length != 0;
});
let properties = object.preview ? object.preview.ownProperties || {} : {};
const propertiesLength = getPropertiesLength(object);
if (object.preview && object.preview.safeGetterValues) {
properties = { ...properties, ...object.preview.safeGetterValues };
}
let indexes = getPropIndexes(properties, max, isInterestingProp);
if (indexes.length < max && indexes.length < propertiesLength) {
// There are not enough props yet.
// Then add uninteresting props to display them.
indexes = indexes.concat(getPropIndexes(properties, max - indexes.length, (t, value, name) => {
return !isInterestingProp(t, value, name);
}));
}
// The server synthesizes some property names for a Proxy, like
// <target> and <handler>; we don't want to quote these because,
// as synthetic properties, they appear more natural when
// unquoted.
const suppressQuotes = object.class === "Proxy";
const propsArray = getProps(props, properties, indexes, suppressQuotes);
// Show symbols.
if (object.preview && object.preview.ownSymbols) {
const { ownSymbols } = object.preview;
const length = max - indexes.length;
const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
return PropRep({
...props,
mode: MODE.TINY,
name: symbolItem,
object: symbolItem.descriptor.value,
equal: ": ",
defaultRep: Grip,
title: null,
suppressQuotes
});
});
propsArray.push(...symbolsProps);
}
if (Object.keys(properties).length > max || propertiesLength > max ||
// When the object has non-enumerable properties, we don't have them in the
// packet, but we might want to show there's something in the object.
propertiesLength > propsArray.length) {
// There are some undisplayed props. Then display "more...".
propsArray.push(span({
key: "more",
className: "more-ellipsis",
title: "more…"
}, "…"));
}
return propsArray;
}
/**
* Get props ordered by index.
*
* @param {Object} componentProps Grip Component props.
* @param {Object} properties Properties of the object the Grip describes.
* @param {Array} indexes Indexes of properties.
* @param {Boolean} suppressQuotes true if we should suppress quotes
* on property names.
* @return {Array} Props.
*/
function getProps(componentProps, properties, indexes, suppressQuotes) {
// Make indexes ordered by ascending.
indexes.sort(function (a, b) {
return a - b;
});
const propertiesKeys = Object.keys(properties);
return indexes.map(i => {
const name = propertiesKeys[i];
const value = getPropValue(properties[name]);
return PropRep({
...componentProps,
mode: MODE.TINY,
name,
object: value,
equal: ": ",
defaultRep: Grip,
title: null,
suppressQuotes
});
});
}
/**
* Get the indexes of props in the object.
*
* @param {Object} properties Props object.
* @param {Number} max The maximum length of indexes array.
* @param {Function} filter Filter the props you want.
* @return {Array} Indexes of interesting props in the object.
*/
function getPropIndexes(properties, max, filter) {
const indexes = [];
try {
let i = 0;
for (const name in properties) {
if (indexes.length >= max) {
return indexes;
}
// Type is specified in grip's "class" field and for primitive
// values use typeof.
const value = getPropValue(properties[name]);
let type = value.class || typeof value;
type = type.toLowerCase();
if (filter(type, value, name)) {
indexes.push(i);
}
i++;
}
} catch (err) {
console.error(err);
}
return indexes;
}
/**
* Get the actual value of a property.
*
* @param {Object} property
* @return {Object} Value of the property.
*/
function getPropValue(property) {
let value = property;
if (typeof property === "object") {
const keys = Object.keys(property);
if (keys.includes("value")) {
value = property.value;
} else if (keys.includes("getterValue")) {
value = property.getterValue;
}
}
return value;
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
if (object.class === "DeadObject") {
return true;
}
return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
}
const maxLengthMap = new Map();
maxLengthMap.set(MODE.SHORT, 3);
maxLengthMap.set(MODE.LONG, 10);
// Grip is used in propIterator and has to be defined here.
const Grip = {
rep: wrapRender(GripRep),
supportsObject,
maxLengthMap
};
// Exports from this module
module.exports = Grip;
/***/ }),
/***/ 59:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const { maybeEscapePropertyName } = __webpack_require__(4);
const ArrayRep = __webpack_require__(38);
const GripArrayRep = __webpack_require__(96);
const GripMap = __webpack_require__(98);
const GripMapEntryRep = __webpack_require__(99);
const ErrorRep = __webpack_require__(95);
const { isLongString } = __webpack_require__(18);
const MAX_NUMERICAL_PROPERTIES = 100;
const NODE_TYPES = {
BUCKET: Symbol("[n…m]"),
DEFAULT_PROPERTIES: Symbol("<default properties>"),
ENTRIES: Symbol("<entries>"),
GET: Symbol("<get>"),
GRIP: Symbol("GRIP"),
MAP_ENTRY_KEY: Symbol("<key>"),
MAP_ENTRY_VALUE: Symbol("<value>"),
PROMISE_REASON: Symbol("<reason>"),
PROMISE_STATE: Symbol("<state>"),
PROMISE_VALUE: Symbol("<value>"),
PROXY_HANDLER: Symbol("<handler>"),
PROXY_TARGET: Symbol("<target>"),
SET: Symbol("<set>"),
PROTOTYPE: Symbol("<prototype>"),
BLOCK: Symbol("☲")
};
let WINDOW_PROPERTIES = {};
if (typeof window === "object") {
WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
}
function getType(item) {
return item.type;
}
function getValue(item) {
if (nodeHasValue(item)) {
return item.contents.value;
}
if (nodeHasGetterValue(item)) {
return item.contents.getterValue;
}
if (nodeHasAccessors(item)) {
return item.contents;
}
return undefined;
}
function getActor(item, roots) {
const isRoot = isNodeRoot(item, roots);
const value = getValue(item);
return isRoot || !value ? null : value.actor;
}
function isNodeRoot(item, roots) {
const gripItem = getClosestGripNode(item);
const value = getValue(gripItem);
return value && roots.some(root => {
const rootValue = getValue(root);
return rootValue && rootValue.actor === value.actor;
});
}
function nodeIsBucket(item) {
return getType(item) === NODE_TYPES.BUCKET;
}
function nodeIsEntries(item) {
return getType(item) === NODE_TYPES.ENTRIES;
}
function nodeIsMapEntry(item) {
return GripMapEntryRep.supportsObject(getValue(item));
}
function nodeHasChildren(item) {
return Array.isArray(item.contents);
}
function nodeHasValue(item) {
return item && item.contents && item.contents.hasOwnProperty("value");
}
function nodeHasGetterValue(item) {
return item && item.contents && item.contents.hasOwnProperty("getterValue");
}
function nodeIsObject(item) {
const value = getValue(item);
return value && value.type === "object";
}
function nodeIsArrayLike(item) {
const value = getValue(item);
return GripArrayRep.supportsObject(value) || ArrayRep.supportsObject(value);
}
function nodeIsFunction(item) {
const value = getValue(item);
return value && value.class === "Function";
}
function nodeIsOptimizedOut(item) {
const value = getValue(item);
return !nodeHasChildren(item) && value && value.optimizedOut;
}
function nodeIsUninitializedBinding(item) {
const value = getValue(item);
return value && value.uninitialized;
}
// Used to check if an item represents a binding that exists in a sourcemap's
// original file content, but does not match up with a binding found in the
// generated code.
function nodeIsUnmappedBinding(item) {
const value = getValue(item);
return value && value.unmapped;
}
// Used to check if an item represents a binding that exists in the debugger's
// parser result, but does not match up with a binding returned by the
// debugger server.
function nodeIsUnscopedBinding(item) {
const value = getValue(item);
return value && value.unscoped;
}
function nodeIsMissingArguments(item) {
const value = getValue(item);
return !nodeHasChildren(item) && value && value.missingArguments;
}
function nodeHasProperties(item) {
return !nodeHasChildren(item) && nodeIsObject(item);
}
function nodeIsPrimitive(item) {
return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(item) && !nodeIsLongString(item);
}
function nodeIsDefaultProperties(item) {
return getType(item) === NODE_TYPES.DEFAULT_PROPERTIES;
}
function isDefaultWindowProperty(name) {
return WINDOW_PROPERTIES.includes(name);
}
function nodeIsPromise(item) {
const value = getValue(item);
if (!value) {
return false;
}
return value.class == "Promise";
}
function nodeIsProxy(item) {
const value = getValue(item);
if (!value) {
return false;
}
return value.class == "Proxy";
}
function nodeIsPrototype(item) {
return getType(item) === NODE_TYPES.PROTOTYPE;
}
function nodeIsWindow(item) {
const value = getValue(item);
if (!value) {
return false;
}
return value.class == "Window";
}
function nodeIsGetter(item) {
return getType(item) === NODE_TYPES.GET;
}
function nodeIsSetter(item) {
return getType(item) === NODE_TYPES.SET;
}
function nodeIsBlock(item) {
return getType(item) === NODE_TYPES.BLOCK;
}
function nodeIsError(item) {
return ErrorRep.supportsObject(getValue(item));
}
function nodeIsLongString(item) {
return isLongString(getValue(item));
}
function nodeHasFullText(item) {
const value = getValue(item);
return nodeIsLongString(item) && value.hasOwnProperty("fullText");
}
function nodeHasGetter(item) {
const getter = getNodeGetter(item);
return getter && getter.type !== "undefined";
}
function nodeHasSetter(item) {
const setter = getNodeSetter(item);
return setter && setter.type !== "undefined";
}
function nodeHasAccessors(item) {
return nodeHasGetter(item) || nodeHasSetter(item);
}
function nodeSupportsNumericalBucketing(item) {
// We exclude elements with entries since it's the <entries> node
// itself that can have buckets.
return nodeIsArrayLike(item) && !nodeHasEntries(item) || nodeIsEntries(item) || nodeIsBucket(item);
}
function nodeHasEntries(item) {
const value = getValue(item);
if (!value) {
return false;
}
return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet" || value.class === "Storage";
}
function nodeHasAllEntriesInPreview(item) {
const { preview } = getValue(item) || {};
if (!preview) {
return false;
}
const { entries, items, length, size } = preview;
if (!entries && !items) {
return false;
}
return entries ? entries.length === size : items.length === length;
}
function nodeNeedsNumericalBuckets(item) {
return nodeSupportsNumericalBucketing(item) && getNumericalPropertiesCount(item) > MAX_NUMERICAL_PROPERTIES;
}
function makeNodesForPromiseProperties(item) {
const {
promiseState: { reason, value, state }
} = getValue(item);
const properties = [];
if (state) {
properties.push(createNode({
parent: item,
name: "<state>",
contents: { value: state },
type: NODE_TYPES.PROMISE_STATE
}));
}
if (reason) {
properties.push(createNode({
parent: item,
name: "<reason>",
contents: { value: reason },
type: NODE_TYPES.PROMISE_REASON
}));
}
if (value) {
properties.push(createNode({
parent: item,
name: "<value>",
contents: { value: value },
type: NODE_TYPES.PROMISE_VALUE
}));
}
return properties;
}
function makeNodesForProxyProperties(item) {
const { proxyHandler, proxyTarget } = getValue(item);
return [createNode({
parent: item,
name: "<target>",
contents: { value: proxyTarget },
type: NODE_TYPES.PROXY_TARGET
}), createNode({
parent: item,
name: "<handler>",
contents: { value: proxyHandler },
type: NODE_TYPES.PROXY_HANDLER
})];
}
function makeNodesForEntries(item) {
const nodeName = "<entries>";
const entriesPath = "<entries>";
if (nodeHasAllEntriesInPreview(item)) {
let entriesNodes = [];
const { preview } = getValue(item);
if (preview.entries) {
entriesNodes = preview.entries.map(([key, value], index) => {
return createNode({
parent: item,
name: index,
path: `${entriesPath}/${index}`,
contents: { value: GripMapEntryRep.createGripMapEntry(key, value) }
});
});
} else if (preview.items) {
entriesNodes = preview.items.map((value, index) => {
return createNode({
parent: item,
name: index,
path: `${entriesPath}/${index}`,
contents: { value }
});
});
}
return createNode({
parent: item,
name: nodeName,
contents: entriesNodes,
type: NODE_TYPES.ENTRIES
});
}
return createNode({
parent: item,
name: nodeName,
contents: null,
type: NODE_TYPES.ENTRIES
});
}
function makeNodesForMapEntry(item) {
const nodeValue = getValue(item);
if (!nodeValue || !nodeValue.preview) {
return [];
}
const { key, value } = nodeValue.preview;
return [createNode({
parent: item,
name: "<key>",
contents: { value: key },
type: NODE_TYPES.MAP_ENTRY_KEY
}), createNode({
parent: item,
name: "<value>",
contents: { value },
type: NODE_TYPES.MAP_ENTRY_VALUE
})];
}
function getNodeGetter(item) {
return item && item.contents ? item.contents.get : undefined;
}
function getNodeSetter(item) {
return item && item.contents ? item.contents.set : undefined;
}
function sortProperties(properties) {
return properties.sort((a, b) => {
// Sort numbers in ascending order and sort strings lexicographically
const aInt = parseInt(a, 10);
const bInt = parseInt(b, 10);
if (isNaN(aInt) || isNaN(bInt)) {
return a > b ? 1 : -1;
}
return aInt - bInt;
});
}
function makeNumericalBuckets(parent) {
const numProperties = getNumericalPropertiesCount(parent);
// We want to have at most a hundred slices.
const bucketSize = 10 ** Math.max(2, Math.ceil(Math.log10(numProperties)) - 2);
const numBuckets = Math.ceil(numProperties / bucketSize);
const buckets = [];
for (let i = 1; i <= numBuckets; i++) {
const minKey = (i - 1) * bucketSize;
const maxKey = Math.min(i * bucketSize - 1, numProperties - 1);
const startIndex = nodeIsBucket(parent) ? parent.meta.startIndex : 0;
const minIndex = startIndex + minKey;
const maxIndex = startIndex + maxKey;
const bucketName = `[${minIndex}${maxIndex}]`;
buckets.push(createNode({
parent,
name: bucketName,
contents: null,
type: NODE_TYPES.BUCKET,
meta: {
startIndex: minIndex,
endIndex: maxIndex
}
}));
}
return buckets;
}
function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
const userPropertiesNames = [];
const defaultProperties = [];
propertiesNames.forEach(name => {
if (isDefaultWindowProperty(name)) {
defaultProperties.push(name);
} else {
userPropertiesNames.push(name);
}
});
const nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);
if (defaultProperties.length > 0) {
const defaultPropertiesNode = createNode({
parent,
name: "<default properties>",
contents: null,
type: NODE_TYPES.DEFAULT_PROPERTIES
});
const defaultNodes = defaultProperties.map((name, index) => createNode({
parent: defaultPropertiesNode,
name: maybeEscapePropertyName(name),
path: `${index}/${name}`,
contents: ownProperties[name]
}));
nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
}
return nodes;
}
function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
return propertiesNames.map(name => createNode({
parent,
name: maybeEscapePropertyName(name),
contents: ownProperties[name]
}));
}
function makeNodesForProperties(objProps, parent) {
const {
ownProperties = {},
ownSymbols,
prototype,
safeGetterValues
} = objProps;
const parentValue = getValue(parent);
const allProperties = { ...ownProperties, ...safeGetterValues };
// Ignore properties that are neither non-concrete nor getters/setters.
const propertiesNames = sortProperties(Object.keys(allProperties)).filter(name => {
if (!allProperties[name]) {
return false;
}
const properties = Object.getOwnPropertyNames(allProperties[name]);
return properties.some(property => ["value", "getterValue", "get", "set"].includes(property));
});
let nodes = [];
if (parentValue && parentValue.class == "Window") {
nodes = makeDefaultPropsBucket(propertiesNames, parent, allProperties);
} else {
nodes = makeNodesForOwnProps(propertiesNames, parent, allProperties);
}
if (Array.isArray(ownSymbols)) {
ownSymbols.forEach((ownSymbol, index) => {
nodes.push(createNode({
parent,
name: ownSymbol.name,
path: `symbol-${index}`,
contents: ownSymbol.descriptor || null
}));
}, this);
}
if (nodeIsPromise(parent)) {
nodes.push(...makeNodesForPromiseProperties(parent));
}
if (nodeHasEntries(parent)) {
nodes.push(makeNodesForEntries(parent));
}
// Add accessor nodes if needed
for (const name of propertiesNames) {
const property = allProperties[name];
if (property.get && property.get.type !== "undefined") {
nodes.push(createGetterNode({ parent, property, name }));
}
if (property.set && property.set.type !== "undefined") {
nodes.push(createSetterNode({ parent, property, name }));
}
}
// Add the prototype if it exists and is not null
if (prototype && prototype.type !== "null") {
nodes.push(makeNodeForPrototype(objProps, parent));
}
return nodes;
}
function setNodeFullText(loadedProps, node) {
if (nodeHasFullText(node) || !nodeIsLongString(node)) {
return node;
}
const { fullText } = loadedProps;
if (nodeHasValue(node)) {
node.contents.value.fullText = fullText;
} else if (nodeHasGetterValue(node)) {
node.contents.getterValue.fullText = fullText;
}
return node;
}
function makeNodeForPrototype(objProps, parent) {
const { prototype } = objProps || {};
// Add the prototype if it exists and is not null
if (prototype && prototype.type !== "null") {
return createNode({
parent,
name: "<prototype>",
contents: { value: prototype },
type: NODE_TYPES.PROTOTYPE
});
}
return null;
}
function createNode(options) {
const {
parent,
name,
path,
contents,
type = NODE_TYPES.GRIP,
meta
} = options;
if (contents === undefined) {
return null;
}
// The path is important to uniquely identify the item in the entire
// tree. This helps debugging & optimizes React's rendering of large
// lists. The path will be separated by property name, wrapped in a Symbol
// to avoid name clashing,
// i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of Symbol(`foo/bar/baz`)
// for the inner object.
return {
parent,
name,
path: parent ? Symbol(`${getSymbolDescriptor(parent.path)}/${path || name}`) : Symbol(path || name),
contents,
type,
meta
};
}
function createGetterNode({ parent, property, name }) {
return createNode({
parent,
name: `<get ${name}()>`,
contents: { value: property.get },
type: NODE_TYPES.GET
});
}
function createSetterNode({ parent, property, name }) {
return createNode({
parent,
name: `<set ${name}()>`,
contents: { value: property.set },
type: NODE_TYPES.SET
});
}
function getSymbolDescriptor(symbol) {
return symbol.toString().replace(/^(Symbol\()(.*)(\))$/, "$2");
}
function setNodeChildren(node, children) {
node.contents = children;
return node;
}
function getEvaluatedItem(item, evaluations) {
if (!evaluations.has(item.path)) {
return item;
}
return {
...item,
contents: evaluations.get(item.path)
};
}
function getChildrenWithEvaluations(options) {
const { item, loadedProperties, cachedNodes, evaluations } = options;
const children = getChildren({
loadedProperties,
cachedNodes,
item
});
if (Array.isArray(children)) {
return children.map(i => getEvaluatedItem(i, evaluations));
}
if (children) {
return getEvaluatedItem(children, evaluations);
}
return [];
}
function getChildren(options) {
const { cachedNodes, item, loadedProperties = new Map() } = options;
const key = item.path;
if (cachedNodes && cachedNodes.has(key)) {
return cachedNodes.get(key);
}
const loadedProps = loadedProperties.get(key);
const hasLoadedProps = loadedProperties.has(key);
// Because we are dynamically creating the tree as the user
// expands it (not precalculated tree structure), we cache child
// arrays. This not only helps performance, but is necessary
// because the expanded state depends on instances of nodes
// being the same across renders. If we didn't do this, each
// node would be a new instance every render.
// If the node needs properties, we only add children to
// the cache if the properties are loaded.
const addToCache = children => {
if (cachedNodes) {
cachedNodes.set(item.path, children);
}
return children;
};
// Nodes can either have children already, or be an object with
// properties that we need to go and fetch.
if (nodeHasChildren(item)) {
return addToCache(item.contents);
}
if (nodeIsMapEntry(item)) {
return addToCache(makeNodesForMapEntry(item));
}
if (nodeIsProxy(item)) {
return addToCache(makeNodesForProxyProperties(item));
}
if (nodeIsLongString(item) && hasLoadedProps) {
// Set longString object's fullText to fetched one.
return addToCache(setNodeFullText(loadedProps, item));
}
if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
// Even if we have numerical buckets, we should have loaded non indexed
// properties.
const bucketNodes = makeNumericalBuckets(item);
return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
}
if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
return [];
}
if (!hasLoadedProps) {
return [];
}
return addToCache(makeNodesForProperties(loadedProps, item));
}
function getParent(item) {
return item.parent;
}
function getNumericalPropertiesCount(item) {
if (nodeIsBucket(item)) {
return item.meta.endIndex - item.meta.startIndex + 1;
}
const value = getValue(getClosestGripNode(item));
if (!value) {
return 0;
}
if (GripArrayRep.supportsObject(value)) {
return GripArrayRep.getLength(value);
}
if (GripMap.supportsObject(value)) {
return GripMap.getLength(value);
}
// TODO: We can also have numerical properties on Objects, but at the
// moment we don't have a way to distinguish them from non-indexed properties,
// as they are all computed in a ownPropertiesLength property.
return 0;
}
function getClosestGripNode(item) {
const type = getType(item);
if (type !== NODE_TYPES.BUCKET && type !== NODE_TYPES.DEFAULT_PROPERTIES && type !== NODE_TYPES.ENTRIES) {
return item;
}
const parent = getParent(item);
if (!parent) {
return null;
}
return getClosestGripNode(parent);
}
function getClosestNonBucketNode(item) {
const type = getType(item);
if (type !== NODE_TYPES.BUCKET) {
return item;
}
const parent = getParent(item);
if (!parent) {
return null;
}
return getClosestNonBucketNode(parent);
}
function getParentGripNode(item) {
const parentNode = getParent(item);
if (!parentNode) {
return null;
}
return getClosestGripNode(parentNode);
}
function getParentGripValue(item) {
const parentGripNode = getParentGripNode(item);
if (!parentGripNode) {
return null;
}
return getValue(parentGripNode);
}
function getNonPrototypeParentGripValue(item) {
const parentGripNode = getParentGripNode(item);
if (!parentGripNode) {
return null;
}
if (getType(parentGripNode) === NODE_TYPES.PROTOTYPE) {
return getNonPrototypeParentGripValue(parentGripNode);
}
return getValue(parentGripNode);
}
module.exports = {
createNode,
createGetterNode,
createSetterNode,
getActor,
getChildren,
getChildrenWithEvaluations,
getClosestGripNode,
getClosestNonBucketNode,
getParent,
getParentGripValue,
getNonPrototypeParentGripValue,
getNumericalPropertiesCount,
getValue,
makeNodesForEntries,
makeNodesForPromiseProperties,
makeNodesForProperties,
makeNumericalBuckets,
nodeHasAccessors,
nodeHasAllEntriesInPreview,
nodeHasChildren,
nodeHasEntries,
nodeHasProperties,
nodeHasGetter,
nodeHasSetter,
nodeIsBlock,
nodeIsBucket,
nodeIsDefaultProperties,
nodeIsEntries,
nodeIsError,
nodeIsLongString,
nodeHasFullText,
nodeIsFunction,
nodeIsGetter,
nodeIsMapEntry,
nodeIsMissingArguments,
nodeIsObject,
nodeIsOptimizedOut,
nodeIsPrimitive,
nodeIsPromise,
nodeIsPrototype,
nodeIsProxy,
nodeIsSetter,
nodeIsUninitializedBinding,
nodeIsUnmappedBinding,
nodeIsUnscopedBinding,
nodeIsWindow,
nodeNeedsNumericalBuckets,
nodeSupportsNumericalBucketing,
setNodeChildren,
sortProperties,
NODE_TYPES
};
/***/ }),
/***/ 6:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
Copyright (c) 2017 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/* global define */
(function () {
'use strict';
var hasOwn = {}.hasOwnProperty;
function classNames () {
var classes = [];
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (!arg) continue;
var argType = typeof arg;
if (argType === 'string' || argType === 'number') {
classes.push(arg);
} else if (Array.isArray(arg) && arg.length) {
var inner = classNames.apply(null, arg);
if (inner) {
classes.push(inner);
}
} else if (argType === 'object') {
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
}
}
return classes.join(' ');
}
if (typeof module !== 'undefined' && module.exports) {
classNames.default = classNames;
module.exports = classNames;
} else if (true) {
// register as 'classnames', consistent with npm package name
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () {
return classNames;
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else {
window.classNames = classNames;
}
}());
/***/ }),
/***/ 60:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function initialState() {
return {
expandedPaths: new Set(),
loadedProperties: new Map(),
evaluations: new Map(),
actors: new Set()
};
} /* 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/>. */
function reducer(state = initialState(), action = {}) {
const { type, data } = action;
const cloneState = overrides => ({ ...state, ...overrides });
if (type === "NODE_EXPAND") {
return cloneState({
expandedPaths: new Set(state.expandedPaths).add(data.node.path)
});
}
if (type === "NODE_COLLAPSE") {
const expandedPaths = new Set(state.expandedPaths);
expandedPaths.delete(data.node.path);
return cloneState({ expandedPaths });
}
if (type === "NODE_PROPERTIES_LOADED") {
return cloneState({
actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
});
}
if (type === "ROOTS_CHANGED") {
return cloneState();
}
if (type === "GETTER_INVOKED") {
return cloneState({
actors: data.actor ? new Set(state.actors || []).add(data.result.from) : state.actors,
evaluations: new Map(state.evaluations).set(data.node.path, {
getterValue: data.result && data.result.value && (data.result.value.return || data.result.value.throw)
})
});
}
// NOTE: we clear the state on resume because otherwise the scopes pane
// would be out of date. Bug 1514760
if (type === "RESUME" || type == "NAVIGATE") {
return initialState();
}
return state;
}
function getObjectInspectorState(state) {
return state.objectInspector;
}
function getExpandedPaths(state) {
return getObjectInspectorState(state).expandedPaths;
}
function getExpandedPathKeys(state) {
return [...getExpandedPaths(state).keys()];
}
function getActors(state) {
return getObjectInspectorState(state).actors;
}
function getLoadedProperties(state) {
return getObjectInspectorState(state).loadedProperties;
}
function getLoadedPropertyKeys(state) {
return [...getLoadedProperties(state).keys()];
}
function getEvaluations(state) {
return getObjectInspectorState(state).evaluations;
}
const selectors = {
getActors,
getEvaluations,
getExpandedPathKeys,
getExpandedPaths,
getLoadedProperties,
getLoadedPropertyKeys
};
Object.defineProperty(module.exports, "__esModule", {
value: true
});
module.exports = selectors;
module.exports.default = reducer;
/***/ }),
/***/ 61:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const client = __webpack_require__(104);
const loadProperties = __webpack_require__(103);
const node = __webpack_require__(59);
const { nodeIsError, nodeIsPrimitive } = node;
const selection = __webpack_require__(202);
const { MODE } = __webpack_require__(5);
const {
REPS: { Rep, Grip }
} = __webpack_require__(17);
function shouldRenderRootsInReps(roots) {
if (roots.length > 1) {
return false;
}
const root = roots[0];
const name = root && root.name;
return (name === null || typeof name === "undefined") && (nodeIsPrimitive(root) || nodeIsError(root));
}
function renderRep(item, props) {
return Rep({
...props,
object: node.getValue(item),
mode: props.mode || MODE.TINY,
defaultRep: Grip
});
}
module.exports = {
client,
loadProperties,
node,
renderRep,
selection,
shouldRenderRootsInReps
};
/***/ }),
/***/ 73:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _tree = __webpack_require__(100);
var _tree2 = _interopRequireDefault(_tree);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
module.exports = {
Tree: _tree2.default
}; /* 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/. */
/***/ }),
/***/ 767:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(54);
/***/ }),
/***/ 93:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Reps
const { getGripType, isGrip, cropString, wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
const IGNORED_SOURCE_URLS = ["debugger eval code"];
/**
* This component represents a template for Function objects.
*/
FunctionRep.propTypes = {
object: PropTypes.object.isRequired,
parameterNames: PropTypes.array,
onViewSourceInDebugger: PropTypes.func
};
function FunctionRep(props) {
const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
let jumpToDefinitionButton;
if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
jumpToDefinitionButton = dom.button({
className: "jump-definition",
draggable: false,
title: "Jump to definition",
onClick: e => {
// Stop the event propagation so we don't trigger ObjectInspector
// expand/collapse.
e.stopPropagation();
if (recordTelemetryEvent) {
recordTelemetryEvent("jump_to_definition");
}
onViewSourceInDebugger(grip.location);
}
});
}
return span({
"data-link-actor-id": grip.actor,
className: "objectBox objectBox-function",
// Set dir="ltr" to prevent function parentheses from
// appearing in the wrong direction
dir: "ltr"
}, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
}
function getTitle(grip, props) {
const { mode } = props;
if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
return null;
}
let title = mode === MODE.TINY ? "" : "function ";
if (grip.isGenerator) {
title = mode === MODE.TINY ? "* " : "function* ";
}
if (grip.isAsync) {
title = `${"async" + " "}${title}`;
}
return span({
className: "objectTitle"
}, title);
}
/**
* Returns a ReactElement representing the function name.
*
* @param {Object} grip : Function grip
* @param {Object} props: Function rep props
*/
function getFunctionName(grip, props = {}) {
let { functionName } = props;
let name;
if (functionName) {
const end = functionName.length - 1;
functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
}
if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
name = `${functionName}:${grip.displayName}`;
} else {
name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
}
return cropString(name, 100);
}
const objectProperty = /([\w\d\$]+)$/;
const arrayProperty = /\[(.*?)\]$/;
const functionProperty = /([\w\d]+)[\/\.<]*?$/;
const annonymousProperty = /([\w\d]+)\(\^\)$/;
/**
* Decodes an anonymous naming scheme that
* spider monkey implements based on "Naming Anonymous JavaScript Functions"
* http://johnjbarton.github.io/nonymous/index.html
*
* @param {String} name : Function name to clean up
* @returns String
*/
function cleanFunctionName(name) {
for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
const match = reg.exec(name);
if (match) {
return match[1];
}
}
return name;
}
function renderParams(props) {
const { parameterNames = [] } = props;
return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
res.push(span({ className: "param" }, param));
if (index < arr.length - 1) {
res.push(span({ className: "delimiter" }, ", "));
}
return res;
}, []);
}
// Registration
function supportsObject(grip, noGrip = false) {
const type = getGripType(grip, noGrip);
if (noGrip === true || !isGrip(grip)) {
return type == "function";
}
return type == "Function";
}
// Exports from this module
module.exports = {
rep: wrapRender(FunctionRep),
supportsObject,
cleanFunctionName,
// exported for testing purpose.
getFunctionName
};
/***/ }),
/***/ 94:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
module.exports = {
ELEMENT_NODE: 1,
ATTRIBUTE_NODE: 2,
TEXT_NODE: 3,
CDATA_SECTION_NODE: 4,
ENTITY_REFERENCE_NODE: 5,
ENTITY_NODE: 6,
PROCESSING_INSTRUCTION_NODE: 7,
COMMENT_NODE: 8,
DOCUMENT_NODE: 9,
DOCUMENT_TYPE_NODE: 10,
DOCUMENT_FRAGMENT_NODE: 11,
NOTATION_NODE: 12,
// DocumentPosition
DOCUMENT_POSITION_DISCONNECTED: 0x01,
DOCUMENT_POSITION_PRECEDING: 0x02,
DOCUMENT_POSITION_FOLLOWING: 0x04,
DOCUMENT_POSITION_CONTAINS: 0x08,
DOCUMENT_POSITION_CONTAINED_BY: 0x10,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
};
/***/ }),
/***/ 95:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// ReactJS
const PropTypes = __webpack_require__(0);
// Utils
const { getGripType, isGrip, wrapRender } = __webpack_require__(4);
const { cleanFunctionName } = __webpack_require__(93);
const { isLongString } = __webpack_require__(18);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
const IGNORED_SOURCE_URLS = ["debugger eval code"];
/**
* Renders Error objects.
*/
ErrorRep.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
// An optional function that will be used to render the Error stacktrace.
renderStacktrace: PropTypes.func
};
function ErrorRep(props) {
const object = props.object;
const preview = object.preview;
let name;
if (preview && preview.name && preview.kind) {
switch (preview.kind) {
case "Error":
name = preview.name;
break;
case "DOMException":
name = preview.kind;
break;
default:
throw new Error("Unknown preview kind for the Error rep.");
}
} else {
name = "Error";
}
const content = [];
if (props.mode === MODE.TINY) {
content.push(name);
} else {
content.push(`${name}: "${preview.message}"`);
}
if (preview.stack && props.mode !== MODE.TINY) {
const stacktrace = props.renderStacktrace ? props.renderStacktrace(parseStackString(preview.stack)) : getStacktraceElements(props, preview);
content.push(stacktrace);
}
return span({
"data-link-actor-id": object.actor,
className: "objectBox-stackTrace"
}, content);
}
/**
* Returns a React element reprensenting the Error stacktrace, i.e.
* transform error.stack from:
*
* semicolon@debugger eval code:1:109
* jkl@debugger eval code:1:63
* asdf@debugger eval code:1:28
* @debugger eval code:1:227
*
* Into a column layout:
*
* semicolon (<anonymous>:8:10)
* jkl (<anonymous>:5:10)
* asdf (<anonymous>:2:10)
* (<anonymous>:11:1)
*/
function getStacktraceElements(props, preview) {
const stack = [];
if (!preview.stack) {
return stack;
}
parseStackString(preview.stack).forEach((frame, index, frames) => {
let onLocationClick;
const {
filename,
lineNumber,
columnNumber,
functionName,
location
} = frame;
if (props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename)) {
onLocationClick = e => {
// Don't trigger ObjectInspector expand/collapse.
e.stopPropagation();
props.onViewSourceInDebugger({
url: filename,
line: lineNumber,
column: columnNumber
});
};
}
stack.push("\t", span({
key: `fn${index}`,
className: "objectBox-stackTrace-fn"
}, cleanFunctionName(functionName)), " ", span({
key: `location${index}`,
className: "objectBox-stackTrace-location",
onClick: onLocationClick,
title: onLocationClick ? `View source in debugger → ${location}` : undefined
}, location), "\n");
});
return span({
key: "stack",
className: "objectBox-stackTrace-grid"
}, stack);
}
/**
* Parse a string that should represent a stack trace and returns an array of
* the frames. The shape of the frames are extremely important as they can then
* be processed here or in the toolbox by other components.
* @param {String} stack
* @returns {Array} Array of frames, which are object with the following shape:
* - {String} filename
* - {String} functionName
* - {String} location
* - {Number} columnNumber
* - {Number} lineNumber
*/
function parseStackString(stack) {
const res = [];
if (!stack) {
return res;
}
const isStacktraceALongString = isLongString(stack);
const stackString = isStacktraceALongString ? stack.initial : stack;
stackString.split("\n").forEach((frame, index, frames) => {
if (!frame) {
// Skip any blank lines
return;
}
// If the stacktrace is a longString, don't include the last frame in the
// array, since it is certainly incomplete.
// Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
// is fixed.
if (isStacktraceALongString && index === frames.length - 1) {
return;
}
let functionName;
let location;
// Given the input: "functionName@scriptLocation:2:100"
// Result: [
// "functionName@scriptLocation:2:100",
// "functionName",
// "scriptLocation:2:100"
// ]
const result = frame.match(/^(.*)@(.*)$/);
if (result && result.length === 3) {
functionName = result[1];
// If the resource was loaded by base-loader.js, the location looks like:
// resource://devtools/shared/base-loader.js -> resource://path/to/file.js .
// What's needed is only the last part after " -> ".
location = result[2].split(" -> ").pop();
}
if (!functionName) {
functionName = "<anonymous>";
}
// Given the input: "scriptLocation:2:100"
// Result:
// ["scriptLocation:2:100", "scriptLocation", "2", "100"]
const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
if (location && locationParts) {
const [, filename, line, column] = locationParts;
res.push({
filename,
functionName,
location,
columnNumber: Number(column),
lineNumber: Number(line)
});
}
});
return res;
}
// Registration
function supportsObject(object, noGrip = false) {
if (noGrip === true || !isGrip(object)) {
return false;
}
return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
}
// Exports from this module
module.exports = {
rep: wrapRender(ErrorRep),
supportsObject
};
/***/ }),
/***/ 96:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
const { lengthBubble } = __webpack_require__(97);
const {
interleave,
getGripType,
isGrip,
wrapRender,
ellipsisElement
} = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const dom = __webpack_require__(2);
const { span } = dom;
const { ModePropType } = __webpack_require__(38);
const DEFAULT_TITLE = "Array";
/**
* Renders an array. The array is enclosed by left and right bracket
* and the max number of rendered items depends on the current mode.
*/
GripArray.propTypes = {
object: PropTypes.object.isRequired,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: ModePropType,
provider: PropTypes.object,
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function GripArray(props) {
const { object, mode = MODE.SHORT } = props;
let brackets;
const needSpace = function (space) {
return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
};
const config = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-array"
};
const title = getTitle(props, object);
if (mode === MODE.TINY) {
const isEmpty = getLength(object) === 0;
// Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
if (!isEmpty && object.class !== "Array") {
return span(config, title);
}
brackets = needSpace(false);
return span(config, title, span({
className: "arrayLeftBracket"
}, brackets.left), isEmpty ? null : ellipsisElement, span({
className: "arrayRightBracket"
}, brackets.right));
}
const max = maxLengthMap.get(mode);
const items = arrayIterator(props, object, max);
brackets = needSpace(items.length > 0);
return span({
"data-link-actor-id": object.actor,
className: "objectBox objectBox-array"
}, title, span({
className: "arrayLeftBracket"
}, brackets.left), ...interleave(items, ", "), span({
className: "arrayRightBracket"
}, brackets.right), span({
className: "arrayProperties",
role: "group"
}));
}
function getLength(grip) {
if (!grip.preview) {
return 0;
}
return grip.preview.length || grip.preview.childNodesLength || 0;
}
function getTitle(props, object) {
const objectLength = getLength(object);
const isEmpty = objectLength === 0;
let title = props.title || object.class || DEFAULT_TITLE;
const length = lengthBubble({
object,
mode: props.mode,
maxLengthMap,
getLength
});
if (props.mode === MODE.TINY) {
if (isEmpty) {
if (object.class === DEFAULT_TITLE) {
return null;
}
return span({ className: "objectTitle" }, `${title} `);
}
let trailingSpace;
if (object.class === DEFAULT_TITLE) {
title = null;
trailingSpace = " ";
}
return span({ className: "objectTitle" }, title, length, trailingSpace);
}
return span({ className: "objectTitle" }, title, length, " ");
}
function getPreviewItems(grip) {
if (!grip.preview) {
return null;
}
return grip.preview.items || grip.preview.childNodes || [];
}
function arrayIterator(props, grip, max) {
const { Rep } = __webpack_require__(17);
let items = [];
const gripLength = getLength(grip);
if (!gripLength) {
return items;
}
const previewItems = getPreviewItems(grip);
const provider = props.provider;
let emptySlots = 0;
let foldedEmptySlots = 0;
items = previewItems.reduce((res, itemGrip) => {
if (res.length >= max) {
return res;
}
let object;
try {
if (!provider && itemGrip === null) {
emptySlots++;
return res;
}
object = provider ? provider.getValue(itemGrip) : itemGrip;
} catch (exc) {
object = exc;
}
if (emptySlots > 0) {
res.push(getEmptySlotsElement(emptySlots));
foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
emptySlots = 0;
}
if (res.length < max) {
res.push(Rep({
...props,
object,
mode: MODE.TINY,
// Do not propagate title to array items reps
title: undefined
}));
}
return res;
}, []);
// Handle trailing empty slots if there are some.
if (items.length < max && emptySlots > 0) {
items.push(getEmptySlotsElement(emptySlots));
foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
}
const itemsShown = items.length + foldedEmptySlots;
if (gripLength > itemsShown) {
items.push(ellipsisElement);
}
return items;
}
function getEmptySlotsElement(number) {
// TODO: Use l10N - See https://github.com/firefox-devtools/reps/issues/141
return `<${number} empty slot${number > 1 ? "s" : ""}>`;
}
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
}
const maxLengthMap = new Map();
maxLengthMap.set(MODE.SHORT, 3);
maxLengthMap.set(MODE.LONG, 10);
// Exports from this module
module.exports = {
rep: wrapRender(GripArray),
supportsObject,
maxLengthMap,
getLength
};
/***/ }),
/***/ 97:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const PropTypes = __webpack_require__(0);
const { wrapRender } = __webpack_require__(4);
const { MODE } = __webpack_require__(5);
const { ModePropType } = __webpack_require__(38);
const dom = __webpack_require__(2);
const { span } = dom;
GripLengthBubble.propTypes = {
object: PropTypes.object.isRequired,
maxLengthMap: PropTypes.instanceOf(Map).isRequired,
getLength: PropTypes.func.isRequired,
mode: ModePropType,
visibilityThreshold: PropTypes.number
};
function GripLengthBubble(props) {
const {
object,
mode = MODE.SHORT,
visibilityThreshold = 2,
maxLengthMap,
getLength,
showZeroLength = false
} = props;
const length = getLength(object);
const isEmpty = length === 0;
const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
if (isEmpty && !showZeroLength || isObvious) {
return "";
}
return span({
className: "objectLengthBubble"
}, `(${length})`);
}
module.exports = {
lengthBubble: wrapRender(GripLengthBubble)
};
/***/ }),
/***/ 98:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const { lengthBubble } = __webpack_require__(97);
const PropTypes = __webpack_require__(0);
const {
interleave,
isGrip,
wrapRender,
ellipsisElement
} = __webpack_require__(4);
const PropRep = __webpack_require__(39);
const { MODE } = __webpack_require__(5);
const { ModePropType } = __webpack_require__(38);
const { span } = __webpack_require__(2);
/**
* Renders an map. A map is represented by a list of its
* entries enclosed in curly brackets.
*/
GripMap.propTypes = {
object: PropTypes.object,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: ModePropType,
isInterestingEntry: PropTypes.func,
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func,
title: PropTypes.string
};
function GripMap(props) {
const { mode, object } = props;
const config = {
"data-link-actor-id": object.actor,
className: "objectBox objectBox-object"
};
const title = getTitle(props, object);
const isEmpty = getLength(object) === 0;
if (isEmpty || mode === MODE.TINY) {
return span(config, title);
}
const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
return span(config, title, span({
className: "objectLeftBrace"
}, " { "), ...interleave(propsArray, ", "), span({
className: "objectRightBrace"
}, " }"));
}
function getTitle(props, object) {
const title = props.title || (object && object.class ? object.class : "Map");
return span({
className: "objectTitle"
}, title, lengthBubble({
object,
mode: props.mode,
maxLengthMap,
getLength,
showZeroLength: true
}));
}
function safeEntriesIterator(props, object, max) {
max = typeof max === "undefined" ? 3 : max;
try {
return entriesIterator(props, object, max);
} catch (err) {
console.error(err);
}
return [];
}
function entriesIterator(props, object, max) {
// Entry filter. Show only interesting entries to the user.
const isInterestingEntry = props.isInterestingEntry || ((type, value) => {
return type == "boolean" || type == "number" || type == "string" && value.length != 0;
});
const mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
if (indexes.length < max && indexes.length < mapEntries.length) {
// There are not enough entries yet, so we add uninteresting entries.
indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
return !isInterestingEntry(t, value, name);
}));
}
const entries = getEntries(props, mapEntries, indexes);
if (entries.length < getLength(object)) {
// There are some undisplayed entries. Then display "…".
entries.push(ellipsisElement);
}
return entries;
}
/**
* Get entries ordered by index.
*
* @param {Object} props Component props.
* @param {Array} entries Entries array.
* @param {Array} indexes Indexes of entries.
* @return {Array} Array of PropRep.
*/
function getEntries(props, entries, indexes) {
const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
// Make indexes ordered by ascending.
indexes.sort(function (a, b) {
return a - b;
});
return indexes.map((index, i) => {
const [key, entryValue] = entries[index];
const value = entryValue.value !== undefined ? entryValue.value : entryValue;
return PropRep({
name: key,
equal: " \u2192 ",
object: value,
mode: MODE.TINY,
onDOMNodeMouseOver,
onDOMNodeMouseOut,
onInspectIconClick
});
});
}
/**
* Get the indexes of entries in the map.
*
* @param {Array} entries Entries array.
* @param {Number} max The maximum length of indexes array.
* @param {Function} filter Filter the entry you want.
* @return {Array} Indexes of filtered entries in the map.
*/
function getEntriesIndexes(entries, max, filter) {
return entries.reduce((indexes, [key, entry], i) => {
if (indexes.length < max) {
const value = entry && entry.value !== undefined ? entry.value : entry;
// Type is specified in grip's "class" field and for primitive
// values use typeof.
const type = (value && value.class ? value.class : typeof value).toLowerCase();
if (filter(type, value, key)) {
indexes.push(i);
}
}
return indexes;
}, []);
}
function getLength(grip) {
return grip.preview.size || 0;
}
function supportsObject(grip, noGrip = false) {
if (noGrip === true || !isGrip(grip)) {
return false;
}
return grip.preview && grip.preview.kind == "MapLike";
}
const maxLengthMap = new Map();
maxLengthMap.set(MODE.SHORT, 3);
maxLengthMap.set(MODE.LONG, 10);
// Exports from this module
module.exports = {
rep: wrapRender(GripMap),
supportsObject,
maxLengthMap,
getLength
};
/***/ }),
/***/ 99:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
// Dependencies
const PropTypes = __webpack_require__(0);
// Shortcuts
const dom = __webpack_require__(2);
const { span } = dom;
const { wrapRender } = __webpack_require__(4);
const PropRep = __webpack_require__(39);
const { MODE } = __webpack_require__(5);
/**
* Renders an map entry. A map entry is represented by its key,
* a column and its value.
*/
GripMapEntry.propTypes = {
object: PropTypes.object,
// @TODO Change this to Object.values when supported in Node's version of V8
mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
onDOMNodeMouseOver: PropTypes.func,
onDOMNodeMouseOut: PropTypes.func,
onInspectIconClick: PropTypes.func
};
function GripMapEntry(props) {
const { object } = props;
const { key, value } = object.preview;
return span({
className: "objectBox objectBox-map-entry"
}, PropRep({
...props,
name: key,
object: value,
equal: " \u2192 ",
title: null,
suppressQuotes: false
}));
}
function supportsObject(grip, noGrip = false) {
if (noGrip === true) {
return false;
}
return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
}
function createGripMapEntry(key, value) {
return {
type: "mapEntry",
preview: {
key,
value
}
};
}
// Exports from this module
module.exports = {
rep: wrapRender(GripMapEntry),
createGripMapEntry,
supportsObject
};
/***/ })
/******/ });
});