| Index: tools/turbolizer/text-view.js
|
| diff --git a/tools/turbolizer/text-view.js b/tools/turbolizer/text-view.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c41c25b768253e6d9c1b1a3cc744bcf6553bbfc4
|
| --- /dev/null
|
| +++ b/tools/turbolizer/text-view.js
|
| @@ -0,0 +1,394 @@
|
| +// 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.sortedPositionList = [];
|
| + view.nodePositionMap = [];
|
| + view.positionNodeMap = [];
|
| + 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.select(selectionHandler, view.getRanges(items), selected);
|
| + },
|
| + selectionDifference: function(span1, inclusive1, span2, inclusive2) {
|
| + return null;
|
| + },
|
| + brokeredSelect: function(ranges, selected) {
|
| + let locations = view.rangesToLocations(ranges);
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + rangeToLocation(range) {
|
| + return range;
|
| + }
|
| +
|
| + rangesToLocations(ranges) {
|
| + let view = this;
|
| + let nodes = new Set();
|
| + let result = [];
|
| + for (let range of ranges) {
|
| + let start = range[0];
|
| + let end = range[1];
|
| + let location = { pos_start: start, pos_end: end };
|
| + if (range[2] !== null && range[2] != -1) {
|
| + location.node_id = range[2];
|
| + if (range[0] == -1 && range[1] == -1) {
|
| + location.pos_start = view.nodePositionMap[location.node_id];
|
| + location.pos_end = location.pos_start + 1;
|
| + }
|
| + } else {
|
| + if (range[0] != undefined) {
|
| + location.pos_start = range[0];
|
| + location.pos_end = range[1];
|
| + }
|
| + }
|
| + result.push(location);
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + 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;
|
| + }
|
| + }
|
| +
|
| + if (node1 === undefined) {
|
| + let lower = lowerBound(view.positionNodeMap, l1.pos_start, undefined, function(a, b) {
|
| + var node = a[b];
|
| + return view.nodePositionMap[node];
|
| + } );
|
| + while (++lower < view.positionNodeMap.length &&
|
| + view.nodePositionMap[view.positionNodeMap[lower]] < l1.pos_end) {
|
| + if (view.positionNodeMap[lower] == node2) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + if (node2 === undefined) {
|
| + let lower = lowerBound(view.positionNodeMap, l2.pos_start, undefined, function(a, b) {
|
| + var node = a[b];
|
| + return view.nodePositionMap[node];
|
| + } );
|
| + while (++lower < view.positionNodeMap.length &&
|
| + view.nodePositionMap[view.positionNodeMap[lower]] < l2.pos_end) {
|
| + if (view.positionNodeMap[lower] == node1) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + return l1.node_id == l2.node_id;
|
| + }
|
| +
|
| + setNodePositionMap(map) {
|
| + let view = this;
|
| + view.nodePositionMap = map;
|
| + view.positionNodeMap = [];
|
| + view.sortedPositionList = [];
|
| + let next = 0;
|
| + for (let i in view.nodePositionMap) {
|
| + view.sortedPositionList[next] = Number(view.nodePositionMap[i]);
|
| + view.positionNodeMap[next++] = i;
|
| + }
|
| + view.sortedPositionList = sortUnique(view.sortedPositionList,
|
| + function(a,b) { return a - b; });
|
| + this.positionNodeMap.sort(function(a,b) {
|
| + let result = view.nodePositionMap[a] - view.nodePositionMap[b];
|
| + if (result != 0) return result;
|
| + return a - b;
|
| + });
|
| + }
|
| +
|
| + selectLocations(locations, selected, makeVisible) {
|
| + let view = this;
|
| + 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)) {
|
| + view.selectCommon(child, selected, makeVisible);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + getRanges(items) {
|
| + let result = [];
|
| + let lastObject = null;
|
| + for (let i of items) {
|
| + if (i.location) {
|
| + let location = i.location;
|
| + let start = -1;
|
| + let end = -1;
|
| + let node_id = -1;
|
| + if (location.node_id !== undefined) {
|
| + node_id = location.node_id;
|
| + }
|
| + if (location.pos_start !== undefined) {
|
| + start = location.pos_start;
|
| + end = location.pos_end;
|
| + } else {
|
| + if (this.nodePositionMap && this.nodePositionMap[node_id]) {
|
| + start = this.nodePositionMap[node_id];
|
| + end = start + 1;
|
| + }
|
| + }
|
| + if (lastObject == null ||
|
| + (lastObject[2] != node_id ||
|
| + lastObject[0] != start ||
|
| + lastObject[1] != end)) {
|
| + lastObject = [start, end, node_id];
|
| + result.push(lastObject);
|
| + }
|
| + }
|
| + }
|
| + 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 (s.length) {
|
| + for (let i of s) {
|
| + if (firstSelect) {
|
| + makeContainerPosVisible(view.parentNode, i.offsetTop);
|
| + firstSelect = false;
|
| + }
|
| + view.selection.select(i, selected);
|
| + }
|
| + } else {
|
| + if (firstSelect) {
|
| + makeContainerPosVisible(view.parentNode, s.offsetTop);
|
| + firstSelect = false;
|
| + }
|
| + 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.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;
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|