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

META-INF.dirigible.dev-tools.source_frame.XMLView.js Maven / Gradle / Ivy

There is a newer version: 10.6.27
Show newest version
// Copyright 2014 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 Platform from '../platform/platform.js';
import * as TextUtils from '../text_utils/text_utils.js';
import * as UI from '../ui/ui.js';

/**
 * @implements {UI.SearchableView.Searchable}
 * @unrestricted
 */
export class XMLView extends UI.Widget.Widget {
  /**
   * @param {!Document} parsedXML
   */
  constructor(parsedXML) {
    super(true);
    this.registerRequiredCSS('source_frame/xmlView.css');
    this.contentElement.classList.add('shadow-xml-view', 'source-code');
    this._treeOutline = new UI.TreeOutline.TreeOutlineInShadow();
    this._treeOutline.registerRequiredCSS('source_frame/xmlTree.css');
    this.contentElement.appendChild(this._treeOutline.element);

    /** @type {?UI.SearchableView.SearchableView} */
    this._searchableView;
    /** @type {number} */
    this._currentSearchFocusIndex = 0;
    /** @type {!Array.} */
    this._currentSearchTreeElements = [];
    /** @type {?UI.SearchableView.SearchConfig} */
    this._searchConfig;

    XMLViewNode.populate(this._treeOutline, parsedXML, this);
    this._treeOutline.firstChild().select(true /* omitFocus */, false /* selectedByUser */);
  }

  /**
   * @param {!Document} parsedXML
   * @return {!UI.SearchableView.SearchableView}
   */
  static createSearchableView(parsedXML) {
    const xmlView = new XMLView(parsedXML);
    const searchableView = new UI.SearchableView.SearchableView(xmlView);
    searchableView.setPlaceholder(Common.UIString.UIString('Find'));
    xmlView._searchableView = searchableView;
    xmlView.show(searchableView.element);
    return searchableView;
  }

  /**
   * @param {string} text
   * @param {string} mimeType
   * @return {?Document}
   */
  static parseXML(text, mimeType) {
    let parsedXML;
    try {
      parsedXML = (new DOMParser()).parseFromString(text, mimeType);
    } catch (e) {
      return null;
    }
    if (parsedXML.body) {
      return null;
    }
    return parsedXML;
  }

  /**
   * @param {number} index
   * @param {boolean} shouldJump
   */
  _jumpToMatch(index, shouldJump) {
    if (!this._searchConfig) {
      return;
    }
    const regex = this._searchConfig.toSearchRegex(true);
    const previousFocusElement = this._currentSearchTreeElements[this._currentSearchFocusIndex];
    if (previousFocusElement) {
      previousFocusElement.setSearchRegex(regex);
    }

    const newFocusElement = this._currentSearchTreeElements[index];
    if (newFocusElement) {
      this._updateSearchIndex(index);
      if (shouldJump) {
        newFocusElement.reveal(true);
      }
      newFocusElement.setSearchRegex(regex, UI.UIUtils.highlightedCurrentSearchResultClassName);
    } else {
      this._updateSearchIndex(0);
    }
  }

  /**
   * @param {number} count
   */
  _updateSearchCount(count) {
    if (!this._searchableView) {
      return;
    }
    this._searchableView.updateSearchMatchesCount(count);
  }

  /**
   * @param {number} index
   */
  _updateSearchIndex(index) {
    this._currentSearchFocusIndex = index;
    if (!this._searchableView) {
      return;
    }
    this._searchableView.updateCurrentMatchIndex(index);
  }

  /**
   * @param {boolean} shouldJump
   * @param {boolean=} jumpBackwards
   */
  _innerPerformSearch(shouldJump, jumpBackwards) {
    if (!this._searchConfig) {
      return;
    }
    let newIndex = this._currentSearchFocusIndex;
    const previousSearchFocusElement = this._currentSearchTreeElements[newIndex];
    this._innerSearchCanceled();
    this._currentSearchTreeElements = [];
    const regex = this._searchConfig.toSearchRegex(true);

    for (let element = this._treeOutline.rootElement(); element; element = element.traverseNextTreeElement(false)) {
      if (!(element instanceof XMLViewNode)) {
        continue;
      }
      const hasMatch = element.setSearchRegex(regex);
      if (hasMatch) {
        this._currentSearchTreeElements.push(element);
      }
      if (previousSearchFocusElement === element) {
        const currentIndex = this._currentSearchTreeElements.length - 1;
        if (hasMatch || jumpBackwards) {
          newIndex = currentIndex;
        } else {
          newIndex = currentIndex + 1;
        }
      }
    }
    this._updateSearchCount(this._currentSearchTreeElements.length);

    if (!this._currentSearchTreeElements.length) {
      this._updateSearchIndex(0);
      return;
    }
    newIndex = Platform.NumberUtilities.mod(newIndex, this._currentSearchTreeElements.length);

    this._jumpToMatch(newIndex, shouldJump);
  }

  _innerSearchCanceled() {
    for (let element = this._treeOutline.rootElement(); element; element = element.traverseNextTreeElement(false)) {
      if (!(element instanceof XMLViewNode)) {
        continue;
      }
      element.revertHighlightChanges();
    }
    this._updateSearchCount(0);
    this._updateSearchIndex(0);
  }

  /**
   * @override
   */
  searchCanceled() {
    this._searchConfig = null;
    this._currentSearchTreeElements = [];
    this._innerSearchCanceled();
  }

  /**
   * @override
   * @param {!UI.SearchableView.SearchConfig} searchConfig
   * @param {boolean} shouldJump
   * @param {boolean=} jumpBackwards
   */
  performSearch(searchConfig, shouldJump, jumpBackwards) {
    this._searchConfig = searchConfig;
    this._innerPerformSearch(shouldJump, jumpBackwards);
  }

  /**
   * @override
   */
  jumpToNextSearchResult() {
    if (!this._currentSearchTreeElements.length) {
      return;
    }

    const newIndex =
        Platform.NumberUtilities.mod(this._currentSearchFocusIndex + 1, this._currentSearchTreeElements.length);
    this._jumpToMatch(newIndex, true);
  }

  /**
   * @override
   */
  jumpToPreviousSearchResult() {
    if (!this._currentSearchTreeElements.length) {
      return;
    }

    const newIndex =
        Platform.NumberUtilities.mod(this._currentSearchFocusIndex - 1, this._currentSearchTreeElements.length);
    this._jumpToMatch(newIndex, true);
  }

  /**
   * @override
   * @return {boolean}
   */
  supportsCaseSensitiveSearch() {
    return true;
  }

  /**
   * @override
   * @return {boolean}
   */
  supportsRegexSearch() {
    return true;
  }
}


/**
 * @unrestricted
 */
export class XMLViewNode extends UI.TreeOutline.TreeElement {
  /**
   * @param {!Node} node
   * @param {boolean} closeTag
   * @param {!XMLView} xmlView
   */
  constructor(node, closeTag, xmlView) {
    super('', !closeTag && !!node.childElementCount);
    this._node = node;
    this._closeTag = closeTag;
    this.selectable = true;
    /** @type {!Array.} */
    this._highlightChanges = [];
    this._xmlView = xmlView;
    this._updateTitle();
  }

  /**
   * @param {!UI.TreeOutline.TreeOutline|!UI.TreeOutline.TreeElement} root
   * @param {!Node} xmlNode
   * @param {!XMLView} xmlView
   */
  static populate(root, xmlNode, xmlView) {
    let node = xmlNode.firstChild;
    while (node) {
      const currentNode = node;
      node = node.nextSibling;
      const nodeType = currentNode.nodeType;
      // ignore empty TEXT
      if (nodeType === 3 && currentNode.nodeValue.match(/\s+/)) {
        continue;
      }
      // ignore ATTRIBUTE, ENTITY_REFERENCE, ENTITY, DOCUMENT, DOCUMENT_TYPE, DOCUMENT_FRAGMENT, NOTATION
      if ((nodeType !== 1) && (nodeType !== 3) && (nodeType !== 4) && (nodeType !== 7) && (nodeType !== 8)) {
        continue;
      }
      root.appendChild(new XMLViewNode(currentNode, false, xmlView));
    }
  }

  /**
   * @param {?RegExp} regex
   * @param {string=} additionalCssClassName
   * @return {boolean}
   */
  setSearchRegex(regex, additionalCssClassName) {
    this.revertHighlightChanges();
    if (!regex) {
      return false;
    }
    if (this._closeTag && this.parent && !this.parent.expanded) {
      return false;
    }
    regex.lastIndex = 0;
    let cssClasses = UI.UIUtils.highlightedSearchResultClassName;
    if (additionalCssClassName) {
      cssClasses += ' ' + additionalCssClassName;
    }
    const content = this.listItemElement.textContent.replace(/\xA0/g, ' ');
    let match = regex.exec(content);
    const ranges = [];
    while (match) {
      ranges.push(new TextUtils.TextRange.SourceRange(match.index, match[0].length));
      match = regex.exec(content);
    }
    if (ranges.length) {
      UI.UIUtils.highlightRangesWithStyleClass(this.listItemElement, ranges, cssClasses, this._highlightChanges);
    }
    return !!this._highlightChanges.length;
  }

  revertHighlightChanges() {
    UI.UIUtils.revertDomChanges(this._highlightChanges);
    this._highlightChanges = [];
  }

  _updateTitle() {
    const node = this._node;
    switch (node.nodeType) {
      case 1: {  // ELEMENT
        const tag = node.tagName;
        if (this._closeTag) {
          this._setTitle(['', 'shadow-xml-view-tag']);
          return;
        }
        const titleItems = ['<' + tag, 'shadow-xml-view-tag'];
        const attributes = node.attributes;
        for (let i = 0; i < attributes.length; ++i) {
          const attributeNode = attributes.item(i);
          titleItems.push(
              '\xA0', 'shadow-xml-view-tag', attributeNode.name, 'shadow-xml-view-attribute-name', '="',
              'shadow-xml-view-tag', attributeNode.value, 'shadow-xml-view-attribute-value', '"',
              'shadow-xml-view-tag');
        }
        if (!this.expanded) {
          if (node.childElementCount) {
            titleItems.push(
                '>', 'shadow-xml-view-tag', '…', 'shadow-xml-view-comment', '', 'shadow-xml-view-tag', node.textContent, 'shadow-xml-view-text', '', 'shadow-xml-view-tag');
        this._setTitle(titleItems);
        return;
      }
      case 3: {  // TEXT
        this._setTitle([node.nodeValue, 'shadow-xml-view-text']);
        return;
      }
      case 4: {  // CDATA
        this._setTitle([
          '', 'shadow-xml-view-cdata'
        ]);
        return;
      }
      case 7: {  // PROCESSING_INSTRUCTION
        this._setTitle(['', 'shadow-xml-view-processing-instruction']);
        return;
      }
      case 8: {  // COMMENT
        this._setTitle(['', 'shadow-xml-view-comment']);
        return;
      }
    }
  }

  /**
   * @param {!Array.} items
   */
  _setTitle(items) {
    const titleFragment = createDocumentFragment();
    for (let i = 0; i < items.length; i += 2) {
      titleFragment.createChild('span', items[i + 1]).textContent = items[i];
    }
    this.title = titleFragment;
    this._xmlView._innerPerformSearch(false, false);
  }

  /**
   * @override
   */
  onattach() {
    this.listItemElement.classList.toggle('shadow-xml-view-close-tag', this._closeTag);
  }

  /**
   * @override
   */
  onexpand() {
    this._updateTitle();
  }

  /**
   * @override
   */
  oncollapse() {
    this._updateTitle();
  }

  /**
   * @override
   * @returns {!Promise}
   */
  async onpopulate() {
    XMLViewNode.populate(this, this._node, this._xmlView);
    this.appendChild(new XMLViewNode(this._node, true, this._xmlView));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy