| // 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.innerHTML = 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; |
| } |
| } |
| } |
| } |