| /* |
| * 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.ObjectPreviewView = class ObjectPreviewView extends WI.Object |
| { |
| constructor(object, preview, mode) |
| { |
| console.assert(!object || object instanceof WI.RemoteObject); |
| console.assert(preview instanceof WI.ObjectPreview); |
| |
| super(); |
| |
| this._object = object || null; |
| this._preview = preview; |
| this._mode = mode || WI.ObjectPreviewView.Mode.Full; |
| |
| this._element = document.createElement("span"); |
| this._element.className = "object-preview"; |
| |
| this._previewElement = this._element.appendChild(document.createElement("span")); |
| this._previewElement.className = "preview"; |
| this._lossless = this._appendPreview(this._previewElement, this._preview); |
| |
| this._titleElement = this._element.appendChild(document.createElement("span")); |
| this._titleElement.className = "title"; |
| this._titleElement.hidden = true; |
| this._initTitleElement(); |
| |
| if (this._preview.hasSize()) { |
| var sizeElement = this._element.appendChild(document.createElement("span")); |
| sizeElement.className = "size"; |
| sizeElement.textContent = " (" + this._preview.size + ")"; |
| } |
| |
| if (this._lossless) |
| this._element.classList.add("lossless"); |
| } |
| |
| // Public |
| |
| get preview() |
| { |
| return this._preview; |
| } |
| |
| get element() |
| { |
| return this._element; |
| } |
| |
| get mode() |
| { |
| return this._mode; |
| } |
| |
| get lossless() |
| { |
| return this._lossless; |
| } |
| |
| showTitle() |
| { |
| this._titleElement.hidden = false; |
| this._previewElement.hidden = true; |
| } |
| |
| showPreview() |
| { |
| this._titleElement.hidden = true; |
| this._previewElement.hidden = false; |
| } |
| |
| setOriginatingObjectInfo(remoteObject, propertyPath) |
| { |
| console.assert(!this._remoteObject); |
| console.assert(remoteObject instanceof WI.RemoteObject); |
| console.assert(!propertyPath || propertyPath instanceof WI.PropertyPath); |
| |
| this._remoteObject = remoteObject; |
| this._propertyPath = propertyPath || null; |
| |
| this.element.addEventListener("contextmenu", this._contextMenuHandler.bind(this)); |
| } |
| |
| // Private |
| |
| _initTitleElement() |
| { |
| // Display null / regexps as simple formatted values even in title. |
| if (this._preview.subtype === "regexp" || this._preview.subtype === "null") |
| this._titleElement.appendChild(WI.FormattedValue.createElementForObjectPreview(this._preview)); |
| else if (this._preview.subtype === "node") |
| this._titleElement.appendChild(WI.FormattedValue.createElementForNodePreview(this._preview, {remoteObjectAccessor: (callback) => callback(this._object)})); |
| else |
| this._titleElement.textContent = this._preview.description || ""; |
| } |
| |
| _numberOfPropertiesToShowInMode() |
| { |
| return this._mode === WI.ObjectPreviewView.Mode.Brief ? 3 : Infinity; |
| } |
| |
| _appendPreview(element, preview) |
| { |
| var displayObjectAsValue = false; |
| if (preview.type === "object") { |
| if (preview.subtype === "regexp" || preview.subtype === "null" || preview.subtype === "node") { |
| // Display null / regexps / nodes as simple formatted values. |
| displayObjectAsValue = true; |
| } else if ((preview.subtype === "array" && preview.description !== "Array") || (preview.subtype !== "array" && preview.description !== "Object")) { |
| // Class names for other non-basic-Array / non-basic-Object types. |
| var nameElement = element.appendChild(document.createElement("span")); |
| nameElement.className = "object-preview-name"; |
| nameElement.textContent = preview.description + " "; |
| } |
| } |
| |
| // Content. |
| var bodyElement = element.appendChild(document.createElement("span")); |
| bodyElement.className = "object-preview-body"; |
| if (!displayObjectAsValue) { |
| if (preview.collectionEntryPreviews) |
| return this._appendEntryPreviews(bodyElement, preview); |
| if (preview.propertyPreviews) |
| return this._appendPropertyPreviews(bodyElement, preview); |
| } |
| return this._appendValuePreview(bodyElement, preview); |
| } |
| |
| _appendEntryPreviews(element, preview) |
| { |
| var lossless = preview.lossless && !preview.propertyPreviews.length; |
| var overflow = preview.overflow; |
| |
| var isIterator = preview.subtype === "iterator"; |
| |
| element.append(isIterator ? "[" : "{"); |
| |
| var limit = Math.min(preview.collectionEntryPreviews.length, this._numberOfPropertiesToShowInMode()); |
| for (var i = 0; i < limit; ++i) { |
| if (i > 0) |
| element.append(", "); |
| |
| var keyPreviewLossless = true; |
| var entry = preview.collectionEntryPreviews[i]; |
| if (entry.keyPreview) { |
| keyPreviewLossless = this._appendPreview(element, entry.keyPreview); |
| element.append(" => "); |
| } |
| |
| var valuePreviewLossless = this._appendPreview(element, entry.valuePreview); |
| |
| if (!keyPreviewLossless || !valuePreviewLossless) |
| lossless = false; |
| } |
| |
| if (preview.collectionEntryPreviews.length > limit) { |
| lossless = false; |
| overflow = true; |
| } |
| |
| if (overflow) { |
| if (limit > 0) |
| element.append(", "); |
| element.append(ellipsis); |
| } |
| |
| element.append(isIterator ? "]" : "}"); |
| |
| return lossless; |
| } |
| |
| _appendPropertyPreviews(element, preview) |
| { |
| // Do not show Error properties in previews. They are more useful in full views. |
| if (preview.subtype === "error") |
| return false; |
| |
| // Do not show Date properties in previews. If there are any properties, show them in full view. |
| if (preview.subtype === "date") |
| return !preview.propertyPreviews.length; |
| |
| var lossless = preview.lossless; |
| var overflow = preview.overflow; |
| |
| // FIXME: Array previews should have better sparse support. |
| var isArray = preview.subtype === "array"; |
| |
| element.append(isArray ? "[" : "{"); |
| |
| var numberAdded = 0; |
| var limit = this._numberOfPropertiesToShowInMode(); |
| for (let i = 0; i < preview.propertyPreviews.length && numberAdded < limit; ++i) { |
| let property = preview.propertyPreviews[i]; |
| |
| // FIXME: Better handle getter/setter accessors. Should we show getters in previews? |
| if (property.type === "accessor") |
| continue; |
| |
| // Constructor name is often already visible, so don't show it as a property. |
| if (property.name === "constructor") |
| continue; |
| |
| if (numberAdded++ > 0) |
| element.append(", "); |
| |
| if (!isArray || property.name != i) { |
| let nameElement = element.appendChild(document.createElement("span")); |
| nameElement.className = "name"; |
| nameElement.textContent = property.name; |
| element.append(": "); |
| } |
| |
| if (property.valuePreview) |
| this._appendPreview(element, property.valuePreview); |
| else if (property.subtype === "node") { |
| let options = {}; |
| if (preview === this._preview && this._object) { |
| options.remoteObjectAccessor = (callback) => { |
| this._object.getProperty(property.name, (error, remoteObject, wasThrown) => { |
| if (!error && remoteObject && !wasThrown) { |
| WI.consoleManager.releaseRemoteObjectWithConsoleClear(remoteObject); |
| callback(remoteObject); |
| } |
| }); |
| }; |
| } |
| element.appendChild(WI.FormattedValue.createElementForNodePreview(property, options)); |
| } else |
| element.appendChild(WI.FormattedValue.createElementForPropertyPreview(property)); |
| } |
| |
| if (numberAdded === limit && preview.propertyPreviews.length > limit) { |
| lossless = false; |
| overflow = true; |
| } |
| |
| if (overflow) { |
| if (limit > 0) |
| element.append(", "); |
| element.append(ellipsis); |
| } |
| |
| element.append(isArray ? "]" : "}"); |
| |
| return lossless; |
| } |
| |
| _appendValuePreview(element, preview) |
| { |
| if (preview.subtype === "node") { |
| let options = {}; |
| if (preview === this._preview && this._object) |
| options.remoteObjectAccessor = (callback) => callback(this._object); |
| element.appendChild(WI.FormattedValue.createElementForNodePreview(preview, options)); |
| return false; |
| } |
| |
| element.appendChild(WI.FormattedValue.createElementForObjectPreview(preview)); |
| return true; |
| } |
| |
| _contextMenuHandler(event) |
| { |
| let contextMenu = WI.ContextMenu.createFromEvent(event); |
| |
| event.__addedObjectPreviewContextMenuItems = true; |
| |
| contextMenu.appendItem(WI.UIString("Log Value"), () => { |
| let isImpossible = !this._propertyPath || this._propertyPath.isFullPathImpossible(); |
| let text = isImpossible ? WI.UIString("Selected Value") : this._propertyPath.displayPath(WI.PropertyPath.Type.Value); |
| |
| if (!isImpossible) |
| WI.quickConsole.prompt.pushHistoryItem(text); |
| |
| WI.consoleLogViewController.appendImmediateExecutionWithResult(text, this._remoteObject, isImpossible); |
| }); |
| } |
| }; |
| |
| WI.ObjectPreviewView.Mode = { |
| Brief: Symbol("object-preview-brief"), |
| Full: Symbol("object-preview-full"), |
| }; |