325 lines
11 KiB
JavaScript
325 lines
11 KiB
JavaScript
/*
|
|
* Copyright (C) 2015 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
WI.FormattedValue = {};
|
|
|
|
WI.FormattedValue.MaxPreviewStringLength = 140;
|
|
|
|
WI.FormattedValue.isSimpleString = function(string)
|
|
{
|
|
return string.length <= WI.FormattedValue.MaxPreviewStringLength
|
|
&& !string.slice(0, WI.FormattedValue.MaxPreviewStringLength).includes("\n");
|
|
};
|
|
|
|
WI.FormattedValue.hasSimpleDisplay = function(object)
|
|
{
|
|
switch (object.type) {
|
|
case "boolean":
|
|
case "number":
|
|
case "symbol":
|
|
case "bigint":
|
|
case "undefined":
|
|
return true;
|
|
|
|
case "string":
|
|
return WI.FormattedValue.isSimpleString(object.description);
|
|
|
|
case "function":
|
|
return false;
|
|
|
|
case "object":
|
|
var subtype = object.subtype;
|
|
return subtype === "null" || subtype === "regexp" || subtype === "date";
|
|
}
|
|
|
|
console.assert(false, "All RemoteObject types should be handled above");
|
|
return false;
|
|
};
|
|
|
|
WI.FormattedValue.classNameForTypes = function(type, subtype)
|
|
{
|
|
return "formatted-" + (subtype ? subtype : type);
|
|
};
|
|
|
|
WI.FormattedValue.classNameForObject = function(object)
|
|
{
|
|
return WI.FormattedValue.classNameForTypes(object.type, object.subtype);
|
|
};
|
|
|
|
WI.FormattedValue.createLinkifiedElementString = function(string)
|
|
{
|
|
var span = document.createElement("span");
|
|
span.className = "formatted-string";
|
|
span.append("\"", WI.linkifyStringAsFragment(string.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")), "\"");
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForNode = function(object)
|
|
{
|
|
var span = document.createElement("span");
|
|
span.className = "formatted-node";
|
|
|
|
object.pushNodeToFrontend(function(nodeId) {
|
|
if (!nodeId) {
|
|
span.textContent = object.description;
|
|
return;
|
|
}
|
|
|
|
var treeOutline = new WI.DOMTreeOutline;
|
|
treeOutline.setVisible(true);
|
|
treeOutline.rootDOMNode = WI.domManager.nodeForId(nodeId);
|
|
if (!treeOutline.children[0].hasChildren)
|
|
treeOutline.element.classList.add("single-node");
|
|
span.appendChild(treeOutline.element);
|
|
});
|
|
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForError = function(object)
|
|
{
|
|
var span = document.createElement("span");
|
|
span.classList.add("formatted-error");
|
|
span.textContent = object.description;
|
|
|
|
if (!object.preview)
|
|
return span;
|
|
|
|
function previewToObject(preview)
|
|
{
|
|
var result = {};
|
|
for (var property of preview.propertyPreviews)
|
|
result[property.name] = property.value;
|
|
|
|
return result;
|
|
}
|
|
|
|
var preview = previewToObject(object.preview);
|
|
if (!preview.sourceURL)
|
|
return span;
|
|
|
|
var sourceLinkWithPrefix = WI.ErrorObjectView.makeSourceLinkWithPrefix(preview.sourceURL, preview.line, preview.column);
|
|
span.append(sourceLinkWithPrefix);
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForNodePreview = function(preview, {remoteObjectAccessor} = {})
|
|
{
|
|
var value = preview.value || preview.description;
|
|
var span = document.createElement("span");
|
|
span.className = "formatted-node-preview syntax-highlighted";
|
|
|
|
if (remoteObjectAccessor) {
|
|
let domNode = null;
|
|
|
|
span.addEventListener("mouseenter", (event) => {
|
|
if (domNode) {
|
|
domNode.highlight();
|
|
return;
|
|
}
|
|
|
|
remoteObjectAccessor((remoteObject) => {
|
|
remoteObject.pushNodeToFrontend((nodeId) => {
|
|
domNode = WI.domManager.nodeForId(nodeId);
|
|
if (domNode)
|
|
domNode.highlight();
|
|
});
|
|
});
|
|
});
|
|
|
|
span.addEventListener("mouseleave", (event) => {
|
|
WI.domManager.hideDOMNodeHighlight();
|
|
});
|
|
|
|
span.addEventListener("contextmenu", (event) => {
|
|
if (!domNode)
|
|
return;
|
|
|
|
let contextMenu = WI.ContextMenu.createFromEvent(event);
|
|
|
|
WI.appendContextMenuItemsForDOMNode(contextMenu, domNode);
|
|
});
|
|
}
|
|
|
|
// Comment node preview.
|
|
if (value.startsWith("<!--")) {
|
|
var comment = span.appendChild(document.createElement("span"));
|
|
comment.className = "html-comment";
|
|
comment.textContent = value;
|
|
return span;
|
|
}
|
|
|
|
// Doctype node preview.
|
|
if (value.startsWith("<!DOCTYPE")) {
|
|
var doctype = span.appendChild(document.createElement("span"));
|
|
doctype.className = "html-doctype";
|
|
doctype.textContent = value;
|
|
return span;
|
|
}
|
|
|
|
// Element node previews have a very strict format, with at most a single attribute.
|
|
// We can style it up like a DOMNode without interactivity.
|
|
var matches = value.match(/^<(\S+?)(?: (\S+?)="(.*?)")?>$/);
|
|
|
|
// Remaining node types are often #text, #document, etc, with attribute nodes potentially being any string.
|
|
if (!matches) {
|
|
console.assert(!value.startsWith("<"), "Unexpected node preview format: " + value);
|
|
span.textContent = value;
|
|
return span;
|
|
}
|
|
|
|
var tag = document.createElement("span");
|
|
tag.className = "html-tag";
|
|
tag.append("<");
|
|
|
|
var tagName = tag.appendChild(document.createElement("span"));
|
|
tagName.className = "html-tag-name";
|
|
tagName.textContent = matches[1];
|
|
|
|
if (matches[2]) {
|
|
tag.append(" ");
|
|
var attribute = tag.appendChild(document.createElement("span"));
|
|
attribute.className = "html-attribute";
|
|
var attributeName = attribute.appendChild(document.createElement("span"));
|
|
attributeName.className = "html-attribute-name";
|
|
attributeName.textContent = matches[2];
|
|
attribute.append("=\"");
|
|
var attributeValue = attribute.appendChild(document.createElement("span"));
|
|
attributeValue.className = "html-attribute-value";
|
|
attributeValue.textContent = matches[3];
|
|
attribute.append("\"");
|
|
}
|
|
|
|
tag.append(">");
|
|
span.appendChild(tag);
|
|
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForFunctionWithName = function(description)
|
|
{
|
|
var span = document.createElement("span");
|
|
span.classList.add("formatted-function");
|
|
span.textContent = description.substring(0, description.indexOf("("));
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForTypesAndValue = function(type, subtype, displayString, size, isPreview, hadException)
|
|
{
|
|
var span = document.createElement("span");
|
|
span.classList.add(WI.FormattedValue.classNameForTypes(type, subtype));
|
|
|
|
// Exception.
|
|
if (hadException) {
|
|
span.textContent = "[Exception: " + displayString + "]";
|
|
return span;
|
|
}
|
|
|
|
// String: quoted and replace newlines as nice unicode symbols.
|
|
if (type === "string") {
|
|
displayString = displayString.truncate(WI.FormattedValue.MaxPreviewStringLength);
|
|
span.textContent = doubleQuotedString(displayString.replace(/\n/g, "\u21B5"));
|
|
return span;
|
|
}
|
|
|
|
// Function: if class, show the description, otherwise elide in previews.
|
|
if (type === "function") {
|
|
if (subtype === "class")
|
|
span.textContent = displayString;
|
|
else
|
|
span.textContent = isPreview ? "function" : displayString;
|
|
return span;
|
|
}
|
|
|
|
// Everything else, the description/value string.
|
|
span.textContent = displayString;
|
|
|
|
// If there is a size, include it.
|
|
if (size !== undefined && (subtype === "array" || subtype === "set" || subtype === "map" || subtype === "weakmap" || subtype === "weakset")) {
|
|
var sizeElement = span.appendChild(document.createElement("span"));
|
|
sizeElement.className = "size";
|
|
sizeElement.textContent = " (" + size + ")";
|
|
}
|
|
|
|
return span;
|
|
};
|
|
|
|
WI.FormattedValue.createElementForRemoteObject = function(object, hadException)
|
|
{
|
|
return WI.FormattedValue.createElementForTypesAndValue(object.type, object.subtype, object.description, object.size, false, hadException);
|
|
};
|
|
|
|
WI.FormattedValue.createElementForObjectPreview = function(objectPreview)
|
|
{
|
|
return WI.FormattedValue.createElementForTypesAndValue(objectPreview.type, objectPreview.subtype, objectPreview.description, objectPreview.size, true, false);
|
|
};
|
|
|
|
WI.FormattedValue.createElementForPropertyPreview = function(propertyPreview)
|
|
{
|
|
return WI.FormattedValue.createElementForTypesAndValue(propertyPreview.type, propertyPreview.subtype, propertyPreview.value, undefined, true, false);
|
|
};
|
|
|
|
WI.FormattedValue.createObjectPreviewOrFormattedValueForObjectPreview = function(objectPreview, previewViewMode)
|
|
{
|
|
if (objectPreview.subtype === "node")
|
|
return WI.FormattedValue.createElementForNodePreview(objectPreview);
|
|
|
|
if (objectPreview.type === "function")
|
|
return WI.FormattedValue.createElementForFunctionWithName(objectPreview.description);
|
|
|
|
const object = null;
|
|
return new WI.ObjectPreviewView(object, objectPreview, previewViewMode).element;
|
|
};
|
|
|
|
WI.FormattedValue.createObjectPreviewOrFormattedValueForRemoteObject = function(object, previewViewMode)
|
|
{
|
|
if (object.subtype === "node")
|
|
return WI.FormattedValue.createElementForNode(object);
|
|
|
|
if (object.subtype === "error")
|
|
return WI.FormattedValue.createElementForError(object);
|
|
|
|
if (object.preview)
|
|
return new WI.ObjectPreviewView(object, object.preview, previewViewMode);
|
|
|
|
return WI.FormattedValue.createElementForRemoteObject(object);
|
|
};
|
|
|
|
WI.FormattedValue.createObjectTreeOrFormattedValueForRemoteObject = function(object, propertyPath, forceExpanding)
|
|
{
|
|
if (object.subtype === "node")
|
|
return WI.FormattedValue.createElementForNode(object);
|
|
|
|
if (object.subtype === "null")
|
|
return WI.FormattedValue.createElementForRemoteObject(object);
|
|
|
|
if (object.type === "object" || object.subtype === "class") {
|
|
var objectTree = new WI.ObjectTreeView(object, null, propertyPath, forceExpanding);
|
|
return objectTree.element;
|
|
}
|
|
|
|
return WI.FormattedValue.createElementForRemoteObject(object);
|
|
};
|