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.sortedPositionList = []; 12 view.nodePositionMap = []; 13 view.positionNodeMap = []; 14 view.textListNode = view.divNode.getElementsByTagName('ul')[0]; 15 view.fillerSvgElement = view.divElement.append("svg").attr('version','1.1').attr("width", "0"); 16 view.patterns = patterns; 17 view.allowSpanSelection = allowSpanSelection; 18 view.nodeToLineMap = []; 19 var selectionHandler = { 20 clear: function() { 21 broker.clear(selectionHandler); 22 }, 23 select: function(items, selected) { 24 for (let i of items) { 25 if (selected) { 26 i.classList.add("selected"); 27 } else { 28 i.classList.remove("selected"); 29 } 30 } 31 broker.select(selectionHandler, view.getRanges(items), selected); 32 }, 33 selectionDifference: function(span1, inclusive1, span2, inclusive2) { 34 return null; 35 }, 36 brokeredSelect: function(ranges, selected) { 37 let locations = view.rangesToLocations(ranges); 38 view.selectLocations(locations, selected, true); 39 }, 40 brokeredClear: function() { 41 view.selection.clear(); 42 } 43 }; 44 view.selection = new Selection(selectionHandler); 45 broker.addSelectionHandler(selectionHandler); 46 } 47 48 setPatterns(patterns) { 49 let view = this; 50 view.patterns = patterns; 51 } 52 53 clearText() { 54 let view = this; 55 while (view.textListNode.firstChild) { 56 view.textListNode.removeChild(view.textListNode.firstChild); 57 } 58 } 59 60 rangeToLocation(range) { 61 return range; 62 } 63 64 rangesToLocations(ranges) { 65 let view = this; 66 let nodes = new Set(); 67 let result = []; 68 for (let range of ranges) { 69 let start = range[0]; 70 let end = range[1]; 71 let location = { pos_start: start, pos_end: end }; 72 if (range[2] !== null && range[2] != -1) { 73 location.node_id = range[2]; 74 if (range[0] == -1 && range[1] == -1) { 75 location.pos_start = view.nodePositionMap[location.node_id]; 76 location.pos_end = location.pos_start + 1; 77 } 78 } else { 79 if (range[0] != undefined) { 80 location.pos_start = range[0]; 81 location.pos_end = range[1]; 82 } 83 } 84 result.push(location); 85 } 86 return result; 87 } 88 89 sameLocation(l1, l2) { 90 let view = this; 91 if (l1.block_id != undefined && l2.block_id != undefined && 92 l1.block_id == l2.block_id && l1.node_id === undefined) { 93 return true; 94 } 95 96 if (l1.address != undefined && l1.address == l2.address) { 97 return true; 98 } 99 100 let node1 = l1.node_id; 101 let node2 = l2.node_id; 102 103 if (node1 === undefined && node2 == undefined) { 104 if (l1.pos_start === undefined || l2.pos_start == undefined) { 105 return false; 106 } 107 if (l1.pos_start == -1 || l2.pos_start == -1) { 108 return false; 109 } 110 if (l1.pos_start < l2.pos_start) { 111 return l1.pos_end > l2.pos_start; 112 } { 113 return l1.pos_start < l2.pos_end; 114 } 115 } 116 117 if (node1 === undefined) { 118 let lower = lowerBound(view.positionNodeMap, l1.pos_start, undefined, function(a, b) { 119 var node = a[b]; 120 return view.nodePositionMap[node]; 121 } ); 122 while (++lower < view.positionNodeMap.length && 123 view.nodePositionMap[view.positionNodeMap[lower]] < l1.pos_end) { 124 if (view.positionNodeMap[lower] == node2) { 125 return true; 126 } 127 } 128 return false; 129 } 130 131 if (node2 === undefined) { 132 let lower = lowerBound(view.positionNodeMap, l2.pos_start, undefined, function(a, b) { 133 var node = a[b]; 134 return view.nodePositionMap[node]; 135 } ); 136 while (++lower < view.positionNodeMap.length && 137 view.nodePositionMap[view.positionNodeMap[lower]] < l2.pos_end) { 138 if (view.positionNodeMap[lower] == node1) { 139 return true; 140 } 141 } 142 return false; 143 } 144 145 return l1.node_id == l2.node_id; 146 } 147 148 setNodePositionMap(map) { 149 let view = this; 150 view.nodePositionMap = map; 151 view.positionNodeMap = []; 152 view.sortedPositionList = []; 153 let next = 0; 154 for (let i in view.nodePositionMap) { 155 view.sortedPositionList[next] = Number(view.nodePositionMap[i]); 156 view.positionNodeMap[next++] = i; 157 } 158 view.sortedPositionList = sortUnique(view.sortedPositionList, 159 function(a,b) { return a - b; }); 160 this.positionNodeMap.sort(function(a,b) { 161 let result = view.nodePositionMap[a] - view.nodePositionMap[b]; 162 if (result != 0) return result; 163 return a - b; 164 }); 165 } 166 167 selectLocations(locations, selected, makeVisible) { 168 let view = this; 169 for (let l of locations) { 170 for (let i = 0; i < view.textListNode.children.length; ++i) { 171 let child = view.textListNode.children[i]; 172 if (child.location != undefined && view.sameLocation(l, child.location)) { 173 view.selectCommon(child, selected, makeVisible); 174 } 175 } 176 } 177 } 178 179 getRanges(items) { 180 let result = []; 181 let lastObject = null; 182 for (let i of items) { 183 if (i.location) { 184 let location = i.location; 185 let start = -1; 186 let end = -1; 187 let node_id = -1; 188 if (location.node_id !== undefined) { 189 node_id = location.node_id; 190 } 191 if (location.pos_start !== undefined) { 192 start = location.pos_start; 193 end = location.pos_end; 194 } else { 195 if (this.nodePositionMap && this.nodePositionMap[node_id]) { 196 start = this.nodePositionMap[node_id]; 197 end = start + 1; 198 } 199 } 200 if (lastObject == null || 201 (lastObject[2] != node_id || 202 lastObject[0] != start || 203 lastObject[1] != end)) { 204 lastObject = [start, end, node_id]; 205 result.push(lastObject); 206 } 207 } 208 } 209 return result; 210 } 211 212 createFragment(text, style) { 213 let view = this; 214 let span = document.createElement("SPAN"); 215 span.onmousedown = function(e) { 216 view.mouseDownSpan(span, e); 217 } 218 if (style != undefined) { 219 span.classList.add(style); 220 } 221 span.innerText = text; 222 return span; 223 } 224 225 appendFragment(li, fragment) { 226 li.appendChild(fragment); 227 } 228 229 processLine(line) { 230 let view = this; 231 let result = []; 232 let patternSet = 0; 233 while (true) { 234 let beforeLine = line; 235 for (let pattern of view.patterns[patternSet]) { 236 let matches = line.match(pattern[0]); 237 if (matches != null) { 238 if (matches[0] != '') { 239 let style = pattern[1] != null ? pattern[1] : {}; 240 let text = matches[0]; 241 if (text != '') { 242 let fragment = view.createFragment(matches[0], style.css); 243 if (style.link) { 244 fragment.classList.add('linkable-text'); 245 fragment.link = style.link; 246 } 247 result.push(fragment); 248 if (style.location != undefined) { 249 let location = style.location(text); 250 if (location != undefined) { 251 fragment.location = location; 252 } 253 } 254 } 255 line = line.substr(matches[0].length); 256 } 257 let nextPatternSet = patternSet; 258 if (pattern.length > 2) { 259 nextPatternSet = pattern[2]; 260 } 261 if (line == "") { 262 if (nextPatternSet != -1) { 263 throw("illegal parsing state in text-view in patternSet" + patternSet); 264 } 265 return result; 266 } 267 patternSet = nextPatternSet; 268 break; 269 } 270 } 271 if (beforeLine == line) { 272 throw("input not consumed in text-view in patternSet" + patternSet); 273 } 274 } 275 } 276 277 select(s, selected, makeVisible) { 278 let view = this; 279 view.selection.clear(); 280 view.selectCommon(s, selected, makeVisible); 281 } 282 283 selectCommon(s, selected, makeVisible) { 284 let view = this; 285 let firstSelect = makeVisible && view.selection.isEmpty(); 286 if ((typeof s) === 'function') { 287 for (let i = 0; i < view.textListNode.children.length; ++i) { 288 let child = view.textListNode.children[i]; 289 if (child.location && s(child.location)) { 290 if (firstSelect) { 291 makeContainerPosVisible(view.parentNode, child.offsetTop); 292 firstSelect = false; 293 } 294 view.selection.select(child, selected); 295 } 296 } 297 } else if (s.length) { 298 for (let i of s) { 299 if (firstSelect) { 300 makeContainerPosVisible(view.parentNode, i.offsetTop); 301 firstSelect = false; 302 } 303 view.selection.select(i, selected); 304 } 305 } else { 306 if (firstSelect) { 307 makeContainerPosVisible(view.parentNode, s.offsetTop); 308 firstSelect = false; 309 } 310 view.selection.select(s, selected); 311 } 312 } 313 314 mouseDownLine(li, e) { 315 let view = this; 316 e.stopPropagation(); 317 if (!e.shiftKey) { 318 view.selection.clear(); 319 } 320 if (li.location != undefined) { 321 view.selectLocations([li.location], true, false); 322 } 323 } 324 325 mouseDownSpan(span, e) { 326 let view = this; 327 if (view.allowSpanSelection) { 328 e.stopPropagation(); 329 if (!e.shiftKey) { 330 view.selection.clear(); 331 } 332 select(li, true); 333 } else if (span.link) { 334 span.link(span.textContent); 335 e.stopPropagation(); 336 } 337 } 338 339 processText(text) { 340 let view = this; 341 let textLines = text.split(/[\n]/); 342 let lineNo = 0; 343 for (let line of textLines) { 344 let li = document.createElement("LI"); 345 li.onmousedown = function(e) { 346 view.mouseDownLine(li, e); 347 } 348 li.className = "nolinenums"; 349 li.lineNo = lineNo++; 350 let fragments = view.processLine(line); 351 for (let fragment of fragments) { 352 view.appendFragment(li, fragment); 353 } 354 let lineLocation = view.lineLocation(li); 355 if (lineLocation != undefined) { 356 li.location = lineLocation; 357 } 358 view.textListNode.appendChild(li); 359 } 360 } 361 362 initializeContent(data, rememberedSelection) { 363 let view = this; 364 view.clearText(); 365 view.processText(data); 366 var fillerSize = document.documentElement.clientHeight - 367 view.textListNode.clientHeight; 368 if (fillerSize < 0) { 369 fillerSize = 0; 370 } 371 view.fillerSvgElement.attr("height", fillerSize); 372 } 373 374 deleteContent() { 375 } 376 377 isScrollable() { 378 return true; 379 } 380 381 detachSelection() { 382 return null; 383 } 384 385 lineLocation(li) { 386 let view = this; 387 for (let i = 0; i < li.children.length; ++i) { 388 let fragment = li.children[i]; 389 if (fragment.location != undefined && !view.allowSpanSelection) { 390 return fragment.location; 391 } 392 } 393 } 394} 395