| // 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. |
| |
| var CodeView = function(divID, PR, sourceText, sourcePosition, broker) { |
| "use strict"; |
| var view = this; |
| |
| view.divElement = document.getElementById(divID); |
| view.broker = broker; |
| view.codeSelection = null; |
| view.allSpans = []; |
| |
| var selectionHandler = { |
| clear: function() { |
| broker.clear(selectionHandler); |
| }, |
| select: function(items, selected) { |
| var handler = this; |
| var divElement = view.divElement; |
| var broker = view.broker; |
| for (let span of items) { |
| if (selected) { |
| span.classList.add("selected"); |
| } else { |
| span.classList.remove("selected"); |
| } |
| } |
| var ranges = []; |
| for (var span of items) { |
| ranges.push([span.start, span.end, null]); |
| } |
| broker.select(selectionHandler, ranges, selected); |
| }, |
| selectionDifference: function(span1, inclusive1, span2, inclusive2) { |
| var pos1 = span1.start; |
| var pos2 = span2.start; |
| var result = []; |
| var lineListDiv = view.divElement.firstChild.firstChild.childNodes; |
| for (var i=0; i < lineListDiv.length; i++) { |
| var currentLineElement = lineListDiv[i]; |
| var spans = currentLineElement.childNodes; |
| for (var j=0; j < spans.length; ++j) { |
| var currentSpan = spans[j]; |
| if (currentSpan.start > pos1 || (inclusive1 && currentSpan.start == pos1)) { |
| if (currentSpan.start < pos2 || (inclusive2 && currentSpan.start == pos2)) { |
| result.push(currentSpan); |
| } |
| } |
| } |
| } |
| return result; |
| }, |
| brokeredSelect: function(ranges, selected) { |
| var firstSelect = view.codeSelection.isEmpty(); |
| for (var range of ranges) { |
| var start = range[0]; |
| var end = range[1]; |
| var lower = 0; |
| var upper = view.allSpans.length; |
| if (upper > 0) { |
| while ((upper - lower) > 1) { |
| var middle = Math.floor((upper + lower) / 2); |
| var lineStart = view.allSpans[middle].start; |
| if (lineStart < start) { |
| lower = middle; |
| } else if (lineStart > start) { |
| upper = middle; |
| } else { |
| lower = middle; |
| break; |
| } |
| } |
| var currentSpan = view.allSpans[lower]; |
| var currentLineElement = currentSpan.parentNode; |
| if ((currentSpan.start <= start && start < currentSpan.end) || |
| (currentSpan.start <= end && end < currentSpan.end)) { |
| if (firstSelect) { |
| makeContainerPosVisible(view.divElement, currentLineElement.offsetTop); |
| firstSelect = false; |
| } |
| view.codeSelection.select(currentSpan, selected); |
| } |
| } |
| } |
| }, |
| brokeredClear: function() { |
| view.codeSelection.clear(); |
| }, |
| }; |
| |
| view.codeSelection = new Selection(selectionHandler); |
| broker.addSelectionHandler(selectionHandler); |
| |
| var mouseDown = false; |
| |
| this.handleSpanMouseDown = function(e) { |
| e.stopPropagation(); |
| if (!e.shiftKey) { |
| view.codeSelection.clear(); |
| } |
| view.codeSelection.select(this, true); |
| mouseDown = true; |
| } |
| |
| this.handleSpanMouseMove = function(e) { |
| if (mouseDown) { |
| view.codeSelection.extendTo(this); |
| } |
| } |
| |
| this.handleCodeMouseDown = function(e) { |
| view.codeSelection.clear(); |
| } |
| |
| document.addEventListener('mouseup', function(e){ |
| mouseDown = false; |
| }, false); |
| |
| this.initializeCode(sourceText, sourcePosition); |
| } |
| |
| CodeView.prototype.initializeCode = function(sourceText, sourcePosition) { |
| var view = this; |
| if (sourceText == "") { |
| var newHtml = "<pre class=\"prettyprint\"</pre>"; |
| view.divElement.innerHTML = newHtml; |
| } else { |
| var newHtml = "<pre class=\"prettyprint linenums\">" |
| + sourceText + "</pre>"; |
| view.divElement.innerHTML = newHtml; |
| try { |
| // Wrap in try to work when offline. |
| PR.prettyPrint(); |
| } catch (e) { |
| } |
| |
| view.divElement.onmousedown = this.handleCodeMouseDown; |
| |
| var base = sourcePosition; |
| var current = 0; |
| var lineListDiv = view.divElement.firstChild.firstChild.childNodes; |
| for (i=0; i < lineListDiv.length; i++) { |
| var currentLineElement = lineListDiv[i]; |
| currentLineElement.id = "li" + i; |
| var pos = base + current; |
| currentLineElement.pos = pos; |
| var spans = currentLineElement.childNodes; |
| for (j=0; j < spans.length; ++j) { |
| var currentSpan = spans[j]; |
| if (currentSpan.nodeType == 1) { |
| currentSpan.start = pos; |
| currentSpan.end = pos + currentSpan.textContent.length; |
| currentSpan.onmousedown = this.handleSpanMouseDown; |
| currentSpan.onmousemove = this.handleSpanMouseMove; |
| view.allSpans.push(currentSpan); |
| } |
| current += currentSpan.textContent.length; |
| pos = base + current; |
| } |
| while ((current < sourceText.length) && ( |
| sourceText[current] == '\n' || |
| sourceText[current] == '\r')) { |
| ++current; |
| } |
| } |
| } |
| |
| this.resizeToParent(); |
| } |
| |
| CodeView.prototype.resizeToParent = function() { |
| var view = this; |
| var documentElement = document.documentElement; |
| var y = view.divElement.parentNode.clientHeight || documentElement.clientHeight; |
| view.divElement.style.height = y + "px"; |
| } |