mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 21:54:47 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
376 lines
11 KiB
JavaScript
376 lines
11 KiB
JavaScript
// 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.
|
|
/**
|
|
* @implements {UI.Searchable}
|
|
* @unrestricted
|
|
*/
|
|
SourceFrame.XMLView = class extends UI.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.TreeOutlineInShadow();
|
|
this._treeOutline.registerRequiredCSS('source_frame/xmlTree.css');
|
|
this.contentElement.appendChild(this._treeOutline.element);
|
|
|
|
/** @type {?UI.SearchableView} */
|
|
this._searchableView;
|
|
/** @type {number} */
|
|
this._currentSearchFocusIndex = 0;
|
|
/** @type {!Array.<!UI.TreeElement>} */
|
|
this._currentSearchTreeElements = [];
|
|
/** @type {?UI.SearchableView.SearchConfig} */
|
|
this._searchConfig;
|
|
|
|
SourceFrame.XMLView.Node.populate(this._treeOutline, parsedXML, this);
|
|
}
|
|
|
|
/**
|
|
* @param {!Document} parsedXML
|
|
* @return {!UI.SearchableView}
|
|
*/
|
|
static createSearchableView(parsedXML) {
|
|
const xmlView = new SourceFrame.XMLView(parsedXML);
|
|
const searchableView = new UI.SearchableView(xmlView);
|
|
searchableView.setPlaceholder(Common.UIString('Find'));
|
|
xmlView._searchableView = searchableView;
|
|
xmlView.show(searchableView.element);
|
|
xmlView.contentElement.setAttribute('tabIndex', 0);
|
|
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.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 SourceFrame.XMLView.Node))
|
|
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 = mod(newIndex, this._currentSearchTreeElements.length);
|
|
|
|
this._jumpToMatch(newIndex, shouldJump);
|
|
}
|
|
|
|
_innerSearchCanceled() {
|
|
for (let element = this._treeOutline.rootElement(); element; element = element.traverseNextTreeElement(false)) {
|
|
if (!(element instanceof SourceFrame.XMLView.Node))
|
|
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 = mod(this._currentSearchFocusIndex + 1, this._currentSearchTreeElements.length);
|
|
this._jumpToMatch(newIndex, true);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
jumpToPreviousSearchResult() {
|
|
if (!this._currentSearchTreeElements.length)
|
|
return;
|
|
|
|
const newIndex = mod(this._currentSearchFocusIndex - 1, this._currentSearchTreeElements.length);
|
|
this._jumpToMatch(newIndex, true);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {boolean}
|
|
*/
|
|
supportsCaseSensitiveSearch() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @return {boolean}
|
|
*/
|
|
supportsRegexSearch() {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
SourceFrame.XMLView.Node = class extends UI.TreeElement {
|
|
/**
|
|
* @param {!Node} node
|
|
* @param {boolean} closeTag
|
|
* @param {!SourceFrame.XMLView} xmlView
|
|
*/
|
|
constructor(node, closeTag, xmlView) {
|
|
super('', !closeTag && !!node.childElementCount);
|
|
this._node = node;
|
|
this._closeTag = closeTag;
|
|
this.selectable = false;
|
|
/** @type {!Array.<!Object>} */
|
|
this._highlightChanges = [];
|
|
this._xmlView = xmlView;
|
|
this._updateTitle();
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.TreeOutline|!UI.TreeElement} root
|
|
* @param {!Node} xmlNode
|
|
* @param {!SourceFrame.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 SourceFrame.XMLView.Node(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.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.SourceRange(match.index, match[0].length));
|
|
match = regex.exec(content);
|
|
}
|
|
if (ranges.length)
|
|
UI.highlightRangesWithStyleClass(this.listItemElement, ranges, cssClasses, this._highlightChanges);
|
|
return !!this._highlightChanges.length;
|
|
}
|
|
|
|
revertHighlightChanges() {
|
|
UI.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(['</' + tag + '>', '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(
|
|
'\u00a0', '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', '\u2026', 'shadow-xml-view-comment', '</' + tag, 'shadow-xml-view-tag');
|
|
} else if (this._node.textContent) {
|
|
titleItems.push(
|
|
'>', 'shadow-xml-view-tag', node.textContent, 'shadow-xml-view-text', '</' + tag,
|
|
'shadow-xml-view-tag');
|
|
} else {
|
|
titleItems.push(' /', 'shadow-xml-view-tag');
|
|
}
|
|
}
|
|
titleItems.push('>', '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([
|
|
'<![CDATA[', 'shadow-xml-view-cdata', node.nodeValue, 'shadow-xml-view-text', ']]>', 'shadow-xml-view-cdata'
|
|
]);
|
|
return;
|
|
case 7: // PROCESSING_INSTRUCTION
|
|
this._setTitle(['<?' + node.nodeName + ' ' + node.nodeValue + '?>', 'shadow-xml-view-processing-instruction']);
|
|
return;
|
|
case 8: // COMMENT
|
|
this._setTitle(['<!--' + node.nodeValue + '-->', 'shadow-xml-view-comment']);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<string>} 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
|
|
*/
|
|
onpopulate() {
|
|
SourceFrame.XMLView.Node.populate(this, this._node, this._xmlView);
|
|
this.appendChild(new SourceFrame.XMLView.Node(this._node, true, this._xmlView));
|
|
}
|
|
};
|