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 CodeView extends View {
8  constructor(divID, PR, sourceText, sourcePosition, broker) {
9    super(divID, broker, null, false);
10    let view = this;
11    view.PR = PR;
12    view.mouseDown = false;
13    view.broker = broker;
14    view.allSpans = [];
15
16    var selectionHandler = {
17      clear: function() { broker.clear(selectionHandler); },
18      select: function(items, selected) {
19        var handler = this;
20        var broker = view.broker;
21        for (let span of items) {
22          if (selected) {
23            span.classList.add("selected");
24          } else {
25            span.classList.remove("selected");
26          }
27        }
28        var locations = [];
29        for (var span of items) {
30          locations.push({pos_start: span.start, pos_end: span.end});
31        }
32        broker.clear(selectionHandler);
33        broker.select(selectionHandler, locations, selected);
34      },
35      selectionDifference: function(span1, inclusive1, span2, inclusive2) {
36        var pos1 = span1.start;
37        var pos2 = span2.start;
38        var result = [];
39        var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
40        for (var i = 0; i < lineListDiv.length; i++) {
41          var currentLineElement = lineListDiv[i];
42          var spans = currentLineElement.childNodes;
43          for (var j = 0; j < spans.length; ++j) {
44            var currentSpan = spans[j];
45            if (currentSpan.start > pos1 ||
46                (inclusive1 && currentSpan.start == pos1)) {
47              if (currentSpan.start < pos2 ||
48                  (inclusive2 && currentSpan.start == pos2)) {
49                result.push(currentSpan);
50              }
51            }
52          }
53        }
54        return result;
55      },
56      brokeredSelect: function(locations, selected) {
57        let firstSelect = view.selection.isEmpty();
58        for (let location of locations) {
59          let start = location.pos_start;
60          let end = location.pos_end;
61          if (start && end) {
62            let lower = 0;
63            let upper = view.allSpans.length;
64            if (upper > 0) {
65              while ((upper - lower) > 1) {
66                var middle = Math.floor((upper + lower) / 2);
67                var lineStart = view.allSpans[middle].start;
68                if (lineStart < start) {
69                  lower = middle;
70                } else if (lineStart > start) {
71                  upper = middle;
72                } else {
73                  lower = middle;
74                  break;
75                }
76              }
77              var currentSpan = view.allSpans[lower];
78              var currentLineElement = currentSpan.parentNode;
79              if ((currentSpan.start <= start && start < currentSpan.end) ||
80                  (currentSpan.start <= end && end < currentSpan.end)) {
81                if (firstSelect) {
82                  makeContainerPosVisible(
83                      view.divNode, currentLineElement.offsetTop);
84                  firstSelect = false;
85                }
86                view.selection.select(currentSpan, selected);
87              }
88            }
89          }
90        }
91      },
92      brokeredClear: function() { view.selection.clear(); },
93    };
94    view.selection = new Selection(selectionHandler);
95    broker.addSelectionHandler(selectionHandler);
96
97    view.handleSpanMouseDown = function(e) {
98      e.stopPropagation();
99      if (!e.shiftKey) {
100        view.selection.clear();
101      }
102      view.selection.select(this, true);
103      view.mouseDown = true;
104    }
105
106    view.handleSpanMouseMove = function(e) {
107      if (view.mouseDown) {
108        view.selection.extendTo(this);
109      }
110    }
111
112    view.handleCodeMouseDown = function(e) { view.selection.clear(); }
113
114    document.addEventListener('mouseup', function(e) {
115      view.mouseDown = false;
116    }, false);
117
118    view.initializeCode(sourceText, sourcePosition);
119  }
120
121  initializeContent(data, rememberedSelection) { this.data = data; }
122
123  initializeCode(sourceText, sourcePosition) {
124    var view = this;
125    if (sourceText == "") {
126      var newHtml = "<pre class=\"prettyprint\"</pre>";
127      view.divNode.innerHTML = newHtml;
128    } else {
129      var newHtml =
130          "<pre class=\"prettyprint linenums\">" + sourceText + "</pre>";
131      view.divNode.innerHTML = newHtml;
132      try {
133        // Wrap in try to work when offline.
134        view.PR.prettyPrint();
135      } catch (e) {
136      }
137
138      view.divNode.onmousedown = this.handleCodeMouseDown;
139
140      var base = sourcePosition;
141      var current = 0;
142      var lineListDiv = view.divNode.firstChild.firstChild.childNodes;
143      for (let i = 0; i < lineListDiv.length; i++) {
144        var currentLineElement = lineListDiv[i];
145        currentLineElement.id = "li" + i;
146        var pos = base + current;
147        currentLineElement.pos = pos;
148        var spans = currentLineElement.childNodes;
149        for (let j = 0; j < spans.length; ++j) {
150          var currentSpan = spans[j];
151          if (currentSpan.nodeType == 1) {
152            currentSpan.start = pos;
153            currentSpan.end = pos + currentSpan.textContent.length;
154            currentSpan.onmousedown = this.handleSpanMouseDown;
155            currentSpan.onmousemove = this.handleSpanMouseMove;
156            view.allSpans.push(currentSpan);
157          }
158          current += currentSpan.textContent.length;
159          pos = base + current;
160        }
161        while ((current < sourceText.length) &&
162               (sourceText[current] == '\n' || sourceText[current] == '\r')) {
163          ++current;
164        }
165      }
166    }
167
168    view.resizeToParent();
169  }
170
171  deleteContent() {}
172}
173