1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5"use strict"; 6 7class TextView extends View { 8 constructor(id, broker, patterns, allowSpanSelection) { 9 super(id, broker); 10 let view = this; 11 view.hide(); 12 view.textListNode = view.divNode.getElementsByTagName('ul')[0]; 13 view.fillerSvgElement = view.divElement.append("svg").attr('version','1.1').attr("width", "0"); 14 view.patterns = patterns; 15 view.allowSpanSelection = allowSpanSelection; 16 view.nodeToLineMap = []; 17 var selectionHandler = { 18 clear: function() { 19 broker.clear(selectionHandler); 20 }, 21 select: function(items, selected) { 22 for (let i of items) { 23 if (selected) { 24 i.classList.add("selected"); 25 } else { 26 i.classList.remove("selected"); 27 } 28 } 29 broker.clear(selectionHandler); 30 broker.select(selectionHandler, view.getLocations(items), selected); 31 }, 32 selectionDifference: function(span1, inclusive1, span2, inclusive2) { 33 return null; 34 }, 35 brokeredSelect: function(locations, selected) { 36 view.selectLocations(locations, selected, true); 37 }, 38 brokeredClear: function() { 39 view.selection.clear(); 40 } 41 }; 42 view.selection = new Selection(selectionHandler); 43 broker.addSelectionHandler(selectionHandler); 44 } 45 46 setPatterns(patterns) { 47 let view = this; 48 view.patterns = patterns; 49 } 50 51 clearText() { 52 let view = this; 53 while (view.textListNode.firstChild) { 54 view.textListNode.removeChild(view.textListNode.firstChild); 55 } 56 } 57 58 sameLocation(l1, l2) { 59 let view = this; 60 if (l1.block_id != undefined && l2.block_id != undefined && 61 l1.block_id == l2.block_id && l1.node_id === undefined) { 62 return true; 63 } 64 65 if (l1.address != undefined && l1.address == l2.address) { 66 return true; 67 } 68 69 let node1 = l1.node_id; 70 let node2 = l2.node_id; 71 72 if (node1 === undefined || node2 == undefined) { 73 if (l1.pos_start === undefined || l2.pos_start == undefined) { 74 return false; 75 } 76 if (l1.pos_start == -1 || l2.pos_start == -1) { 77 return false; 78 } 79 if (l1.pos_start < l2.pos_start) { 80 return l1.pos_end > l2.pos_start; 81 } { 82 return l1.pos_start < l2.pos_end; 83 } 84 } 85 86 return l1.node_id == l2.node_id; 87 } 88 89 selectLocations(locations, selected, makeVisible) { 90 let view = this; 91 let s = new Set(); 92 for (let l of locations) { 93 for (let i = 0; i < view.textListNode.children.length; ++i) { 94 let child = view.textListNode.children[i]; 95 if (child.location != undefined && view.sameLocation(l, child.location)) { 96 s.add(child); 97 } 98 } 99 } 100 view.selectCommon(s, selected, makeVisible); 101 } 102 103 getLocations(items) { 104 let result = []; 105 let lastObject = null; 106 for (let i of items) { 107 if (i.location) { 108 result.push(i.location); 109 } 110 } 111 return result; 112 } 113 114 createFragment(text, style) { 115 let view = this; 116 let span = document.createElement("SPAN"); 117 span.onmousedown = function(e) { 118 view.mouseDownSpan(span, e); 119 } 120 if (style != undefined) { 121 span.classList.add(style); 122 } 123 span.innerHTML = text; 124 return span; 125 } 126 127 appendFragment(li, fragment) { 128 li.appendChild(fragment); 129 } 130 131 processLine(line) { 132 let view = this; 133 let result = []; 134 let patternSet = 0; 135 while (true) { 136 let beforeLine = line; 137 for (let pattern of view.patterns[patternSet]) { 138 let matches = line.match(pattern[0]); 139 if (matches != null) { 140 if (matches[0] != '') { 141 let style = pattern[1] != null ? pattern[1] : {}; 142 let text = matches[0]; 143 if (text != '') { 144 let fragment = view.createFragment(matches[0], style.css); 145 if (style.link) { 146 fragment.classList.add('linkable-text'); 147 fragment.link = style.link; 148 } 149 result.push(fragment); 150 if (style.location != undefined) { 151 let location = style.location(text); 152 if (location != undefined) { 153 fragment.location = location; 154 } 155 } 156 } 157 line = line.substr(matches[0].length); 158 } 159 let nextPatternSet = patternSet; 160 if (pattern.length > 2) { 161 nextPatternSet = pattern[2]; 162 } 163 if (line == "") { 164 if (nextPatternSet != -1) { 165 throw("illegal parsing state in text-view in patternSet" + patternSet); 166 } 167 return result; 168 } 169 patternSet = nextPatternSet; 170 break; 171 } 172 } 173 if (beforeLine == line) { 174 throw("input not consumed in text-view in patternSet" + patternSet); 175 } 176 } 177 } 178 179 select(s, selected, makeVisible) { 180 let view = this; 181 view.selection.clear(); 182 view.selectCommon(s, selected, makeVisible); 183 } 184 185 selectCommon(s, selected, makeVisible) { 186 let view = this; 187 let firstSelect = makeVisible && view.selection.isEmpty(); 188 if ((typeof s) === 'function') { 189 for (let i = 0; i < view.textListNode.children.length; ++i) { 190 let child = view.textListNode.children[i]; 191 if (child.location && s(child.location)) { 192 if (firstSelect) { 193 makeContainerPosVisible(view.parentNode, child.offsetTop); 194 firstSelect = false; 195 } 196 view.selection.select(child, selected); 197 } 198 } 199 } else if (typeof s[Symbol.iterator] === 'function') { 200 if (firstSelect) { 201 for (let i of s) { 202 makeContainerPosVisible(view.parentNode, i.offsetTop); 203 break; 204 } 205 } 206 view.selection.select(s, selected); 207 } else { 208 if (firstSelect) { 209 makeContainerPosVisible(view.parentNode, s.offsetTop); 210 } 211 view.selection.select(s, selected); 212 } 213 } 214 215 mouseDownLine(li, e) { 216 let view = this; 217 e.stopPropagation(); 218 if (!e.shiftKey) { 219 view.selection.clear(); 220 } 221 if (li.location != undefined) { 222 view.selectLocations([li.location], true, false); 223 } 224 } 225 226 mouseDownSpan(span, e) { 227 let view = this; 228 if (view.allowSpanSelection) { 229 e.stopPropagation(); 230 if (!e.shiftKey) { 231 view.selection.clear(); 232 } 233 select(li, true); 234 } else if (span.link) { 235 span.link(span.textContent); 236 e.stopPropagation(); 237 } 238 } 239 240 processText(text) { 241 let view = this; 242 let textLines = text.split(/[\n]/); 243 let lineNo = 0; 244 for (let line of textLines) { 245 let li = document.createElement("LI"); 246 li.onmousedown = function(e) { 247 view.mouseDownLine(li, e); 248 } 249 li.className = "nolinenums"; 250 li.lineNo = lineNo++; 251 let fragments = view.processLine(line); 252 for (let fragment of fragments) { 253 view.appendFragment(li, fragment); 254 } 255 let lineLocation = view.lineLocation(li); 256 if (lineLocation != undefined) { 257 li.location = lineLocation; 258 } 259 view.textListNode.appendChild(li); 260 } 261 } 262 263 initializeContent(data, rememberedSelection) { 264 let view = this; 265 view.selection.clear(); 266 view.clearText(); 267 view.processText(data); 268 var fillerSize = document.documentElement.clientHeight - 269 view.textListNode.clientHeight; 270 if (fillerSize < 0) { 271 fillerSize = 0; 272 } 273 view.fillerSvgElement.attr("height", fillerSize); 274 } 275 276 deleteContent() { 277 } 278 279 isScrollable() { 280 return true; 281 } 282 283 detachSelection() { 284 return null; 285 } 286 287 lineLocation(li) { 288 let view = this; 289 for (let i = 0; i < li.children.length; ++i) { 290 let fragment = li.children[i]; 291 if (fragment.location != undefined && !view.allowSpanSelection) { 292 return fragment.location; 293 } 294 } 295 } 296} 297