d3905561b9
Also convert CodeView to a class and fix a host of selection bugs in the process, as well as move the logic and data to "enrich" location with one type of location data with location data known globally to the whole graph in the selection broker. Review-Url: https://codereview.chromium.org/2230083004 Cr-Commit-Position: refs/heads/master@{#38544}
297 lines
7.8 KiB
JavaScript
297 lines
7.8 KiB
JavaScript
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
"use strict";
|
|
|
|
class TextView extends View {
|
|
constructor(id, broker, patterns, allowSpanSelection) {
|
|
super(id, broker);
|
|
let view = this;
|
|
view.hide();
|
|
view.textListNode = view.divNode.getElementsByTagName('ul')[0];
|
|
view.fillerSvgElement = view.divElement.append("svg").attr('version','1.1').attr("width", "0");
|
|
view.patterns = patterns;
|
|
view.allowSpanSelection = allowSpanSelection;
|
|
view.nodeToLineMap = [];
|
|
var selectionHandler = {
|
|
clear: function() {
|
|
broker.clear(selectionHandler);
|
|
},
|
|
select: function(items, selected) {
|
|
for (let i of items) {
|
|
if (selected) {
|
|
i.classList.add("selected");
|
|
} else {
|
|
i.classList.remove("selected");
|
|
}
|
|
}
|
|
broker.clear(selectionHandler);
|
|
broker.select(selectionHandler, view.getLocations(items), selected);
|
|
},
|
|
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
|
|
return null;
|
|
},
|
|
brokeredSelect: function(locations, selected) {
|
|
view.selectLocations(locations, selected, true);
|
|
},
|
|
brokeredClear: function() {
|
|
view.selection.clear();
|
|
}
|
|
};
|
|
view.selection = new Selection(selectionHandler);
|
|
broker.addSelectionHandler(selectionHandler);
|
|
}
|
|
|
|
setPatterns(patterns) {
|
|
let view = this;
|
|
view.patterns = patterns;
|
|
}
|
|
|
|
clearText() {
|
|
let view = this;
|
|
while (view.textListNode.firstChild) {
|
|
view.textListNode.removeChild(view.textListNode.firstChild);
|
|
}
|
|
}
|
|
|
|
sameLocation(l1, l2) {
|
|
let view = this;
|
|
if (l1.block_id != undefined && l2.block_id != undefined &&
|
|
l1.block_id == l2.block_id && l1.node_id === undefined) {
|
|
return true;
|
|
}
|
|
|
|
if (l1.address != undefined && l1.address == l2.address) {
|
|
return true;
|
|
}
|
|
|
|
let node1 = l1.node_id;
|
|
let node2 = l2.node_id;
|
|
|
|
if (node1 === undefined || node2 == undefined) {
|
|
if (l1.pos_start === undefined || l2.pos_start == undefined) {
|
|
return false;
|
|
}
|
|
if (l1.pos_start == -1 || l2.pos_start == -1) {
|
|
return false;
|
|
}
|
|
if (l1.pos_start < l2.pos_start) {
|
|
return l1.pos_end > l2.pos_start;
|
|
} {
|
|
return l1.pos_start < l2.pos_end;
|
|
}
|
|
}
|
|
|
|
return l1.node_id == l2.node_id;
|
|
}
|
|
|
|
selectLocations(locations, selected, makeVisible) {
|
|
let view = this;
|
|
let s = new Set();
|
|
for (let l of locations) {
|
|
for (let i = 0; i < view.textListNode.children.length; ++i) {
|
|
let child = view.textListNode.children[i];
|
|
if (child.location != undefined && view.sameLocation(l, child.location)) {
|
|
s.add(child);
|
|
}
|
|
}
|
|
}
|
|
view.selectCommon(s, selected, makeVisible);
|
|
}
|
|
|
|
getLocations(items) {
|
|
let result = [];
|
|
let lastObject = null;
|
|
for (let i of items) {
|
|
if (i.location) {
|
|
result.push(i.location);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
createFragment(text, style) {
|
|
let view = this;
|
|
let span = document.createElement("SPAN");
|
|
span.onmousedown = function(e) {
|
|
view.mouseDownSpan(span, e);
|
|
}
|
|
if (style != undefined) {
|
|
span.classList.add(style);
|
|
}
|
|
span.innerText = text;
|
|
return span;
|
|
}
|
|
|
|
appendFragment(li, fragment) {
|
|
li.appendChild(fragment);
|
|
}
|
|
|
|
processLine(line) {
|
|
let view = this;
|
|
let result = [];
|
|
let patternSet = 0;
|
|
while (true) {
|
|
let beforeLine = line;
|
|
for (let pattern of view.patterns[patternSet]) {
|
|
let matches = line.match(pattern[0]);
|
|
if (matches != null) {
|
|
if (matches[0] != '') {
|
|
let style = pattern[1] != null ? pattern[1] : {};
|
|
let text = matches[0];
|
|
if (text != '') {
|
|
let fragment = view.createFragment(matches[0], style.css);
|
|
if (style.link) {
|
|
fragment.classList.add('linkable-text');
|
|
fragment.link = style.link;
|
|
}
|
|
result.push(fragment);
|
|
if (style.location != undefined) {
|
|
let location = style.location(text);
|
|
if (location != undefined) {
|
|
fragment.location = location;
|
|
}
|
|
}
|
|
}
|
|
line = line.substr(matches[0].length);
|
|
}
|
|
let nextPatternSet = patternSet;
|
|
if (pattern.length > 2) {
|
|
nextPatternSet = pattern[2];
|
|
}
|
|
if (line == "") {
|
|
if (nextPatternSet != -1) {
|
|
throw("illegal parsing state in text-view in patternSet" + patternSet);
|
|
}
|
|
return result;
|
|
}
|
|
patternSet = nextPatternSet;
|
|
break;
|
|
}
|
|
}
|
|
if (beforeLine == line) {
|
|
throw("input not consumed in text-view in patternSet" + patternSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
select(s, selected, makeVisible) {
|
|
let view = this;
|
|
view.selection.clear();
|
|
view.selectCommon(s, selected, makeVisible);
|
|
}
|
|
|
|
selectCommon(s, selected, makeVisible) {
|
|
let view = this;
|
|
let firstSelect = makeVisible && view.selection.isEmpty();
|
|
if ((typeof s) === 'function') {
|
|
for (let i = 0; i < view.textListNode.children.length; ++i) {
|
|
let child = view.textListNode.children[i];
|
|
if (child.location && s(child.location)) {
|
|
if (firstSelect) {
|
|
makeContainerPosVisible(view.parentNode, child.offsetTop);
|
|
firstSelect = false;
|
|
}
|
|
view.selection.select(child, selected);
|
|
}
|
|
}
|
|
} else if (typeof s[Symbol.iterator] === 'function') {
|
|
if (firstSelect) {
|
|
for (let i of s) {
|
|
makeContainerPosVisible(view.parentNode, i.offsetTop);
|
|
break;
|
|
}
|
|
}
|
|
view.selection.select(s, selected);
|
|
} else {
|
|
if (firstSelect) {
|
|
makeContainerPosVisible(view.parentNode, s.offsetTop);
|
|
}
|
|
view.selection.select(s, selected);
|
|
}
|
|
}
|
|
|
|
mouseDownLine(li, e) {
|
|
let view = this;
|
|
e.stopPropagation();
|
|
if (!e.shiftKey) {
|
|
view.selection.clear();
|
|
}
|
|
if (li.location != undefined) {
|
|
view.selectLocations([li.location], true, false);
|
|
}
|
|
}
|
|
|
|
mouseDownSpan(span, e) {
|
|
let view = this;
|
|
if (view.allowSpanSelection) {
|
|
e.stopPropagation();
|
|
if (!e.shiftKey) {
|
|
view.selection.clear();
|
|
}
|
|
select(li, true);
|
|
} else if (span.link) {
|
|
span.link(span.textContent);
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
|
|
processText(text) {
|
|
let view = this;
|
|
let textLines = text.split(/[\n]/);
|
|
let lineNo = 0;
|
|
for (let line of textLines) {
|
|
let li = document.createElement("LI");
|
|
li.onmousedown = function(e) {
|
|
view.mouseDownLine(li, e);
|
|
}
|
|
li.className = "nolinenums";
|
|
li.lineNo = lineNo++;
|
|
let fragments = view.processLine(line);
|
|
for (let fragment of fragments) {
|
|
view.appendFragment(li, fragment);
|
|
}
|
|
let lineLocation = view.lineLocation(li);
|
|
if (lineLocation != undefined) {
|
|
li.location = lineLocation;
|
|
}
|
|
view.textListNode.appendChild(li);
|
|
}
|
|
}
|
|
|
|
initializeContent(data, rememberedSelection) {
|
|
let view = this;
|
|
view.selection.clear();
|
|
view.clearText();
|
|
view.processText(data);
|
|
var fillerSize = document.documentElement.clientHeight -
|
|
view.textListNode.clientHeight;
|
|
if (fillerSize < 0) {
|
|
fillerSize = 0;
|
|
}
|
|
view.fillerSvgElement.attr("height", fillerSize);
|
|
}
|
|
|
|
deleteContent() {
|
|
}
|
|
|
|
isScrollable() {
|
|
return true;
|
|
}
|
|
|
|
detachSelection() {
|
|
return null;
|
|
}
|
|
|
|
lineLocation(li) {
|
|
let view = this;
|
|
for (let i = 0; i < li.children.length; ++i) {
|
|
let fragment = li.children[i];
|
|
if (fragment.location != undefined && !view.allowSpanSelection) {
|
|
return fragment.location;
|
|
}
|
|
}
|
|
}
|
|
}
|