All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.dirigible.dev-tools.object_ui.RemoteObjectPreviewFormatter.js Maven / Gradle / Ivy

There is a newer version: 10.6.27
Show newest version
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Common from '../common/common.js';

import * as SDK from '../sdk/sdk.js';

/**
 * @unrestricted
 */
export class RemoteObjectPreviewFormatter {
  /**
   * @param {!Protocol.Runtime.PropertyPreview} a
   * @param {!Protocol.Runtime.PropertyPreview} b
   * @return {number}
   */
  static _objectPropertyComparator(a, b) {
    return sortValue(a) - sortValue(b);

    /**
     * @param {!Protocol.Runtime.PropertyPreview} property
     * @return {number}
     */
    function sortValue(property) {
      // TODO(einbinder) expose whether preview properties are actually internal.
      const internalName = _internalName;
      if (property.name === internalName.PromiseStatus) {
        return 1;
      }
      if (property.name === internalName.PromiseValue) {
        return 2;
      }
      if (property.name === internalName.GeneratorStatus || property.name === internalName.PrimitiveValue) {
        return 3;
      }
      // TODO(einbinder) expose whether preview properties are actually private.
      if (property.type !== 'function' && !property.name.startsWith('#')) {
        return 4;
      }
      return 5;
    }
  }

  /**
   * @param {!DocumentFragment|!Element} parentElement
   * @param {!Protocol.Runtime.ObjectPreview} preview
   * @param {boolean} isEntry
   */
  appendObjectPreview(parentElement, preview, isEntry) {
    const description = preview.description;
    const subTypesWithoutValuePreview = new Set(['null', 'regexp', 'error', 'internal#entry']);
    if (preview.type !== 'object' || subTypesWithoutValuePreview.has(preview.subtype) || isEntry) {
      parentElement.appendChild(this.renderPropertyPreview(preview.type, preview.subtype, description));
      return;
    }
    const isArrayOrTypedArray = preview.subtype === 'array' || preview.subtype === 'typedarray';
    if (description) {
      let text;
      if (isArrayOrTypedArray) {
        const arrayLength = SDK.RemoteObject.RemoteObject.arrayLength(preview);
        const arrayLengthText = arrayLength > 1 ? ('(' + arrayLength + ')') : '';
        const arrayName = SDK.RemoteObject.RemoteObject.arrayNameFromDescription(description);
        text = arrayName === 'Array' ? arrayLengthText : (arrayName + arrayLengthText);
      } else {
        const hideDescription = description === 'Object';
        text = hideDescription ? '' : description;
      }
      if (text.length > 0) {
        parentElement.createChild('span', 'object-description').textContent = text + '\xA0';
      }
    }

    const propertiesElement = parentElement.createChild('span', 'object-properties-preview');
    propertiesElement.createTextChild(isArrayOrTypedArray ? '[' : '{');
    if (preview.entries) {
      this._appendEntriesPreview(propertiesElement, preview);
    } else if (isArrayOrTypedArray) {
      this._appendArrayPropertiesPreview(propertiesElement, preview);
    } else {
      this._appendObjectPropertiesPreview(propertiesElement, preview);
    }
    if (preview.overflow) {
      const ellipsisText = propertiesElement.textContent.length > 1 ? ',\xA0…' : '…';
      propertiesElement.createChild('span').textContent = ellipsisText;
    }
    propertiesElement.createTextChild(isArrayOrTypedArray ? ']' : '}');
  }

  /**
   * @param {string} description
   * @return {string}
   */
  _abbreviateFullQualifiedClassName(description) {
    const abbreviatedDescription = description.split('.');
    for (let i = 0; i < abbreviatedDescription.length - 1; ++i) {
      abbreviatedDescription[i] = abbreviatedDescription[i].trimMiddle(3);
    }
    return abbreviatedDescription.join('.');
  }

  /**
   * @param {!Element} parentElement
   * @param {!Protocol.Runtime.ObjectPreview} preview
   */
  _appendObjectPropertiesPreview(parentElement, preview) {
    const internalName = _internalName;
    const properties = preview.properties.filter(p => p.type !== 'accessor')
                           .sort(RemoteObjectPreviewFormatter._objectPropertyComparator);
    for (let i = 0; i < properties.length; ++i) {
      if (i > 0) {
        parentElement.createTextChild(', ');
      }

      const property = properties[i];
      const name = property.name;
      // Internal properties are given special formatting, e.g. Promises `: 123`.
      if (preview.subtype === 'promise' && name === internalName.PromiseStatus) {
        parentElement.appendChild(this._renderDisplayName('<' + property.value + '>'));
        const nextProperty = i + 1 < properties.length ? properties[i + 1] : null;
        if (nextProperty && nextProperty.name === internalName.PromiseValue) {
          if (property.value !== 'pending') {
            parentElement.createTextChild(': ');
            parentElement.appendChild(this._renderPropertyPreviewOrAccessor([nextProperty]));
          }
          i++;
        }
      } else if (preview.subtype === 'generator' && name === internalName.GeneratorStatus) {
        parentElement.appendChild(this._renderDisplayName('<' + property.value + '>'));
      } else if (name === internalName.PrimitiveValue) {
        parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]));
      } else {
        parentElement.appendChild(this._renderDisplayName(name));
        parentElement.createTextChild(': ');
        parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]));
      }
    }
  }

  /**
   * @param {!Element} parentElement
   * @param {!Protocol.Runtime.ObjectPreview} preview
   */
  _appendArrayPropertiesPreview(parentElement, preview) {
    const arrayLength = SDK.RemoteObject.RemoteObject.arrayLength(preview);
    const indexProperties = preview.properties.filter(p => toArrayIndex(p.name) !== -1).sort(arrayEntryComparator);
    const otherProperties = preview.properties.filter(p => toArrayIndex(p.name) === -1)
                                .sort(RemoteObjectPreviewFormatter._objectPropertyComparator);

    /**
     * @param {!Protocol.Runtime.PropertyPreview} a
     * @param {!Protocol.Runtime.PropertyPreview} b
     * @return {number}
     */
    function arrayEntryComparator(a, b) {
      return toArrayIndex(a.name) - toArrayIndex(b.name);
    }

    /**
     * @param {string} name
     * @return {number}
     */
    function toArrayIndex(name) {
      const index = name >>> 0;
      if (String(index) === name && index < arrayLength) {
        return index;
      }
      return -1;
    }

    // Gaps can be shown when all properties are guaranteed to be in the preview.
    const canShowGaps = !preview.overflow;
    let lastNonEmptyArrayIndex = -1;
    let elementsAdded = false;
    for (let i = 0; i < indexProperties.length; ++i) {
      if (elementsAdded) {
        parentElement.createTextChild(', ');
      }

      const property = indexProperties[i];
      const index = toArrayIndex(property.name);
      if (canShowGaps && index - lastNonEmptyArrayIndex > 1) {
        appendUndefined(index);
        parentElement.createTextChild(', ');
      }
      if (!canShowGaps && i !== index) {
        parentElement.appendChild(this._renderDisplayName(property.name));
        parentElement.createTextChild(': ');
      }
      parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]));
      lastNonEmptyArrayIndex = index;
      elementsAdded = true;
    }

    if (canShowGaps && arrayLength - lastNonEmptyArrayIndex > 1) {
      if (elementsAdded) {
        parentElement.createTextChild(', ');
      }
      appendUndefined(arrayLength);
    }

    for (let i = 0; i < otherProperties.length; ++i) {
      if (elementsAdded) {
        parentElement.createTextChild(', ');
      }

      const property = otherProperties[i];
      parentElement.appendChild(this._renderDisplayName(property.name));
      parentElement.createTextChild(': ');
      parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property]));
      elementsAdded = true;
    }

    /**
     * @param {number} index
     */
    function appendUndefined(index) {
      const span = parentElement.createChild('span', 'object-value-undefined');
      const count = index - lastNonEmptyArrayIndex - 1;
      span.textContent =
          count !== 1 ? Common.UIString.UIString('empty × %d', count) : Common.UIString.UIString('empty');
      elementsAdded = true;
    }
  }

  /**
   * @param {!Element} parentElement
   * @param {!Protocol.Runtime.ObjectPreview} preview
   */
  _appendEntriesPreview(parentElement, preview) {
    for (let i = 0; i < preview.entries.length; ++i) {
      if (i > 0) {
        parentElement.createTextChild(', ');
      }

      const entry = preview.entries[i];
      if (entry.key) {
        this.appendObjectPreview(parentElement, entry.key, true /* isEntry */);
        parentElement.createTextChild(' => ');
      }
      this.appendObjectPreview(parentElement, entry.value, true /* isEntry */);
    }
  }

  /**
   * @param {string} name
   * @return {!Element}
   */
  _renderDisplayName(name) {
    const result = createElementWithClass('span', 'name');
    const needsQuotes = /^\s|\s$|^$|\n/.test(name);
    result.textContent = needsQuotes ? '"' + name.replace(/\n/g, '\u21B5') + '"' : name;
    return result;
  }

  /**
   * @param {!Array.} propertyPath
   * @return {!Element}
   */
  _renderPropertyPreviewOrAccessor(propertyPath) {
    const property = propertyPath.peekLast();
    return this.renderPropertyPreview(property.type, /** @type {string} */ (property.subtype), property.value);
  }

  /**
   * @param {string} type
   * @param {string=} subtype
   * @param {string=} description
   * @return {!Element}
   */
  renderPropertyPreview(type, subtype, description) {
    const span = createElementWithClass('span', 'object-value-' + (subtype || type));
    description = description || '';

    if (type === 'accessor') {
      span.textContent = '(...)';
      span.title = Common.UIString.UIString('The property is computed with a getter');
      return span;
    }

    if (type === 'function') {
      span.textContent = '\u0192';
      return span;
    }

    if (type === 'object' && subtype === 'node' && description) {
      createSpansForNodeTitle(span, description);
      return span;
    }

    if (type === 'string') {
      span.createTextChildren('"', description.replace(/\n/g, '\u21B5'), '"');
      return span;
    }

    if (type === 'object' && !subtype) {
      let preview = this._abbreviateFullQualifiedClassName(description);
      if (preview === 'Object') {
        preview = '{…}';
      }
      span.textContent = preview;
      span.title = description;
      return span;
    }

    span.textContent = description;
    return span;
  }
}

/** @enum {string} */
const _internalName = {
  GeneratorStatus: '[[GeneratorStatus]]',
  PrimitiveValue: '[[PrimitiveValue]]',
  PromiseStatus: '[[PromiseStatus]]',
  PromiseValue: '[[PromiseValue]]'
};

/**
 * @param {!Element} container
 * @param {string} nodeTitle
 */
export const createSpansForNodeTitle = function(container, nodeTitle) {
  const match = nodeTitle.match(/([^#.]+)(#[^.]+)?(\..*)?/);
  container.createChild('span', 'webkit-html-tag-name').textContent = match[1];
  if (match[2]) {
    container.createChild('span', 'webkit-html-attribute-value').textContent = match[2];
  }
  if (match[3]) {
    container.createChild('span', 'webkit-html-attribute-name').textContent = match[3];
  }
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy