OLD | NEW |
(Empty) | |
| 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 |
| 7 class 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, func
tion(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, func
tion(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" + pattern
Set); |
| 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 } |
OLD | NEW |