Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Side by Side Diff: chrome/browser/resources/hterm/js/scrollport.js

Issue 8680034: Initial landing of Screen, Terminal, and VT100 classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * A 'viewport' view of fixed-height rows with support for selection and 6 * A 'viewport' view of fixed-height rows with support for selection and
7 * copy-to-clipboard. 7 * copy-to-clipboard.
8 * 8 *
9 * 'Viewport' in this case means that only the visible rows are in the DOM. 9 * 'Viewport' in this case means that only the visible rows are in the DOM.
10 * If the rowProvider has 100,000 rows, but the ScrollPort is only 25 rows 10 * If the rowProvider has 100,000 rows, but the ScrollPort is only 25 rows
11 * tall, then only 25 dom nodes are created. The ScrollPort will ask the 11 * tall, then only 25 dom nodes are created. The ScrollPort will ask the
12 * RowProvider to create new visible rows on demand as they are scrolled in 12 * RowProvider to create new visible rows on demand as they are scrolled in
13 * to the visible area. 13 * to the visible area.
14 * 14 *
15 * This viewport is designed so that select and copy-to-clipboard still works, 15 * This viewport is designed so that select and copy-to-clipboard still works,
16 * even when all or part of the selection is scrolled off screen. 16 * even when all or part of the selection is scrolled off screen.
17 * 17 *
18 * Note that the X11 mouse clipboard does not work properly when all or part 18 * Note that the X11 mouse clipboard does not work properly when all or part
19 * of the selection is off screen. It would be difficult to fix this without 19 * of the selection is off screen. It would be difficult to fix this without
20 * adding significant overhead to pathologically large selection cases. 20 * adding significant overhead to pathologically large selection cases.
21 * 21 *
22 * The RowProvider should return rows rooted by the custom tag name 'x-row'.
23 * This ensures that we can quickly assign the correct display height
24 * to the rows with css.
25 *
22 * @param {RowProvider} rowProvider An object capable of providing rows as 26 * @param {RowProvider} rowProvider An object capable of providing rows as
23 * raw text or row nodes. 27 * raw text or row nodes.
24 * @param {integer} fontSize The css font-size, in pixels. 28 * @param {integer} fontSize The css font-size, in pixels.
25 * @param {integer} opt_lineHeight Optional css line-height in pixels. 29 * @param {integer} opt_lineHeight Optional css line-height in pixels.
26 * If omitted it will be computed based on the fontSize. 30 * If omitted it will be computed based on the fontSize.
27 */ 31 */
28 function ScrollPort(rowProvider, fontSize, opt_lineHeight) { 32 hterm.ScrollPort = function(rowProvider, fontSize, opt_lineHeight) {
29 PubSub.addBehavior(this); 33 PubSub.addBehavior(this);
30 34
31 this.rowProvider_ = rowProvider; 35 this.rowProvider_ = rowProvider;
32 this.fontSize_ = fontSize; 36 this.fontSize_ = fontSize;
33 this.rowHeight_ = opt_lineHeight || fontSize + 2; 37 this.rowHeight_ = opt_lineHeight || fontSize + 2;
34 38
35 this.selection_ = new ScrollPort.Selection(this); 39 this.selection_ = new hterm.ScrollPort.Selection(this);
36 40
37 // A map of rowIndex => rowNode for each row that is drawn as part of a 41 // A map of rowIndex => rowNode for each row that is drawn as part of a
38 // pending redraw_() call. Null if there is no pending redraw_ call. 42 // pending redraw_() call. Null if there is no pending redraw_ call.
39 this.currentRowNodeCache_ = null; 43 this.currentRowNodeCache_ = null;
40 44
41 // A map of rowIndex => rowNode for each row that was drawn as part of the 45 // A map of rowIndex => rowNode for each row that was drawn as part of the
42 // previous redraw_() call. 46 // previous redraw_() call.
43 this.previousRowNodeCache_ = {}; 47 this.previousRowNodeCache_ = {};
44 48
49 // The css rule that we use to control the height of a row.
50 this.xrowCssRule_ = null;
51
45 this.div_ = null; 52 this.div_ = null;
46 this.document_ = null; 53 this.document_ = null;
47 54
48 this.observers_ = {}; 55 this.observers_ = {};
49 56
50 this.DEBUG_ = false; 57 this.DEBUG_ = false;
51 } 58 }
52 59
53 /** 60 /**
54 * Proxy for the native selection object which understands how to walk up the 61 * Proxy for the native selection object which understands how to walk up the
55 * DOM to find the containing row node and sort out which comes first. 62 * DOM to find the containing row node and sort out which comes first.
56 * 63 *
57 * @param {ScrollPort} scrollPort The parent ScrollPort instance. 64 * @param {hterm.ScrollPort} scrollPort The parent hterm.ScrollPort instance.
58 */ 65 */
59 ScrollPort.Selection = function(scrollPort) { 66 hterm.ScrollPort.Selection = function(scrollPort) {
60 this.scrollPort_ = scrollPort; 67 this.scrollPort_ = scrollPort;
61 this.selection_ = null; 68 this.selection_ = null;
62 69
63 /** 70 /**
64 * The row containing the start of the selection. 71 * The row containing the start of the selection.
65 * 72 *
66 * This may be partially or fully selected. It may be the selection anchor 73 * This may be partially or fully selected. It may be the selection anchor
67 * or the focus, but its rowIndex is guaranteed to be less-than-or-equal-to 74 * or the focus, but its rowIndex is guaranteed to be less-than-or-equal-to
68 * that of the endRow. 75 * that of the endRow.
69 * 76 *
(...skipping 24 matching lines...) Expand all
94 */ 101 */
95 this.isCollapsed = null; 102 this.isCollapsed = null;
96 }; 103 };
97 104
98 /** 105 /**
99 * Synchronize this object with the current DOM selection. 106 * Synchronize this object with the current DOM selection.
100 * 107 *
101 * This is a one-way synchronization, the DOM selection is copied to this 108 * This is a one-way synchronization, the DOM selection is copied to this
102 * object, not the other way around. 109 * object, not the other way around.
103 */ 110 */
104 ScrollPort.Selection.prototype.sync = function() { 111 hterm.ScrollPort.Selection.prototype.sync = function() {
105 var selection = this.scrollPort_.getDocument().getSelection(); 112 var selection = this.scrollPort_.getDocument().getSelection();
106 113
107 this.startRow = null; 114 this.startRow = null;
108 this.endRow = null; 115 this.endRow = null;
109 this.isMultiline = null; 116 this.isMultiline = null;
110 this.isCollapsed = !selection || selection.isCollapsed; 117 this.isCollapsed = !selection || selection.isCollapsed;
111 118
112 if (this.isCollapsed) 119 if (this.isCollapsed)
113 return; 120 return;
114 121
(...skipping 24 matching lines...) Expand all
139 this.endRow = focusRow; 146 this.endRow = focusRow;
140 } else { 147 } else {
141 this.startRow = focusRow; 148 this.startRow = focusRow;
142 this.endRow = anchorRow; 149 this.endRow = anchorRow;
143 } 150 }
144 151
145 this.isMultiline = anchorRow.rowIndex != focusRow.rowIndex; 152 this.isMultiline = anchorRow.rowIndex != focusRow.rowIndex;
146 }; 153 };
147 154
148 /** 155 /**
149 * Turn a div into this ScrollPort. 156 * Turn a div into this hterm.ScrollPort.
150 */ 157 */
151 ScrollPort.prototype.decorate = function(div) { 158 hterm.ScrollPort.prototype.decorate = function(div) {
152 this.div_ = div; 159 this.div_ = div;
153 160
154 this.iframe_ = div.ownerDocument.createElement('iframe'); 161 this.iframe_ = div.ownerDocument.createElement('iframe');
155 this.iframe_.style.cssText = ( 162 this.iframe_.style.cssText = (
156 'border: 0;' + 163 'border: 0;' +
157 'height: 100%;' + 164 'height: 100%;' +
158 'position: absolute;' + 165 'position: absolute;' +
159 'width: 100%'); 166 'width: 100%');
160 167
161 div.appendChild(this.iframe_); 168 div.appendChild(this.iframe_);
162 169
163 this.iframe_.contentWindow.addEventListener('resize', 170 this.iframe_.contentWindow.addEventListener('resize',
164 this.onResize.bind(this)); 171 this.onResize.bind(this));
165 172
166 var doc = this.document_ = this.iframe_.contentDocument; 173 var doc = this.document_ = this.iframe_.contentDocument;
167 doc.body.style.cssText = ( 174 doc.body.style.cssText = (
168 'background-color: black;' + 175 'background-color: black;' +
169 'color: white;' + 176 'color: white;' +
170 'font-family: monospace;' + 177 'font-family: monospace;' +
171 'margin: 0px;' + 178 'margin: 0px;' +
172 'padding: 0px;' + 179 'padding: 0px;' +
173 'white-space: pre;' + 180 'white-space: pre;' +
174 '-webkit-user-select: none;'); 181 '-webkit-user-select: none;');
175 182
183 var style = doc.createElement('style');
184 style.textContent = 'x-row {}';
185 doc.head.appendChild(style);
186
187 this.xrowCssRule_ = doc.styleSheets[0].cssRules[0];
188 this.xrowCssRule_.style.display = 'block';
189 this.xrowCssRule_.style.height = this.rowHeight_ + 'px';
190
176 this.screen_ = doc.createElement('x-screen'); 191 this.screen_ = doc.createElement('x-screen');
177 this.screen_.style.cssText = ( 192 this.screen_.style.cssText = (
178 'display: block;' + 193 'display: block;' +
179 'height: 100%;' + 194 'height: 100%;' +
180 'overflow-y: scroll; overflow-x: hidden;' + 195 'overflow-y: scroll; overflow-x: hidden;' +
181 'width: 100%;'); 196 'width: 100%;');
182 197
183 doc.body.appendChild(this.screen_); 198 doc.body.appendChild(this.screen_);
184 199
185 this.screen_.addEventListener('scroll', this.onScroll_.bind(this)); 200 this.screen_.addEventListener('scroll', this.onScroll_.bind(this));
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 // it in the selection when a user 'drag selects' upwards (drag the mouse to 237 // it in the selection when a user 'drag selects' upwards (drag the mouse to
223 // select and scroll at the same time). Without this, the selection gets 238 // select and scroll at the same time). Without this, the selection gets
224 // out of whack. 239 // out of whack.
225 this.scrollArea_ = doc.createElement('div'); 240 this.scrollArea_ = doc.createElement('div');
226 this.scrollArea_.style.cssText = 'visibility: hidden'; 241 this.scrollArea_.style.cssText = 'visibility: hidden';
227 this.screen_.appendChild(this.scrollArea_); 242 this.screen_.appendChild(this.scrollArea_);
228 243
229 this.setRowMetrics(this.fontSize_, this.rowHeight_); 244 this.setRowMetrics(this.fontSize_, this.rowHeight_);
230 }; 245 };
231 246
232 ScrollPort.prototype.getRowHeight = function() { 247 hterm.ScrollPort.prototype.getForegroundColor = function() {
248 return this.document_.body.style.color;
249 };
250
251 hterm.ScrollPort.prototype.setForegroundColor = function(color) {
252 this.document_.body.style.color = color;
253 };
254
255 hterm.ScrollPort.prototype.getBackgroundColor = function() {
256 return this.document_.body.style.backgroundColor;
257 };
258
259 hterm.ScrollPort.prototype.setBackgroundColor = function(color) {
260 this.document_.body.style.backgroundColor = color;
261 };
262
263 hterm.ScrollPort.prototype.getRowHeight = function() {
233 return this.rowHeight_; 264 return this.rowHeight_;
234 }; 265 };
235 266
236 ScrollPort.prototype.getScreenWidth = function() { 267 hterm.ScrollPort.prototype.getScreenWidth = function() {
237 return this.screen_.clientWidth; 268 return this.screen_.clientWidth;
238 }; 269 };
239 270
240 ScrollPort.prototype.getScreenWidth = function() { 271 hterm.ScrollPort.prototype.getScreenHeight = function() {
241 return this.screen_.clientHeight; 272 return this.screen_.clientHeight;
242 }; 273 };
243 274
244 ScrollPort.prototype.getCharacterWidth = function() { 275 hterm.ScrollPort.prototype.getCharacterWidth = function() {
245 var span = this.document_.createElement('span'); 276 var span = this.document_.createElement('span');
246 span.textContent = '\xa0'; //   277 span.textContent = '\xa0'; //  
247 this.rowNodes_.appendChild(span); 278 this.rowNodes_.appendChild(span);
248 var width = span.offsetWidth; 279 var width = span.offsetWidth;
249 this.rowNodes_.removeChild(span); 280 this.rowNodes_.removeChild(span);
250 return width; 281 return width;
251 }; 282 };
252 283
253 /** 284 /**
254 * Return the document that holds the visible rows of this ScrollPort. 285 * Return the document that holds the visible rows of this hterm.ScrollPort.
255 */ 286 */
256 ScrollPort.prototype.getDocument = function() { 287 hterm.ScrollPort.prototype.getDocument = function() {
257 return this.document_; 288 return this.document_;
258 }; 289 };
259 290
260 /** 291 /**
261 * Clear out any cached rowNodes. 292 * Clear out any cached rowNodes.
262 */ 293 */
263 ScrollPort.prototype.resetCache = function() { 294 hterm.ScrollPort.prototype.resetCache = function() {
264 this.currentRowNodeCache_ = null; 295 this.currentRowNodeCache_ = null;
265 this.previousRowNodeCache_ = {}; 296 this.previousRowNodeCache_ = {};
266 }; 297 };
267 298
268 /** 299 /**
269 * Change the current rowProvider. 300 * Change the current rowProvider.
270 * 301 *
271 * This will clear the row cache and cause a redraw. 302 * This will clear the row cache and cause a redraw.
272 * 303 *
273 * @param {Object} rowProvider An object capable of providing the rows 304 * @param {Object} rowProvider An object capable of providing the rows
274 * in this ScrollPort. 305 * in this hterm.ScrollPort.
275 */ 306 */
276 ScrollPort.prototype.setRowProvider = function(rowProvider) { 307 hterm.ScrollPort.prototype.setRowProvider = function(rowProvider) {
277 this.resetCache_(); 308 this.resetCache();
278 this.rowProvider_ = rowProvider; 309 this.rowProvider_ = rowProvider;
279 this.redraw_(); 310 this.redraw_();
280 }; 311 };
281 312
282 /** 313 /**
283 * Set the fontSize and lineHeight of this ScrollPort. 314 * Inform the ScrollPort that a given range of rows is invalid.
315 *
316 * The RowProvider should call this method if the underlying x-row instance
317 * for a given rowIndex is no longer valid.
318 *
319 * Note that this is not necessary when only the *content* of the x-row has
320 * changed. It's only needed when getRowNode(N) would return a different
321 * x-row than it used to.
322 *
323 * If rows in the sepecified range are visible, they will be redrawn.
324 */
325 hterm.ScrollPort.prototype.invalidateRowRange = function(start, end) {
326 this.resetCache();
327
328 var node = this.rowNodes_.firstChild;
329 while (node) {
330 if ('rowIndex' in node &&
331 node.rowIndex >= start && node.rowIndex <= end) {
332 var nextSibling = node.nextSibling;
333 this.rowNodes_.removeChild(node);
334
335 var newNode = this.rowProvider_.getRowNode(node.rowIndex);
336
337 this.rowNodes_.insertBefore(newNode, nextSibling);
338 this.previousRowNodeCache_[node.rowIndex] = newNode;
339
340 node = nextSibling;
341 } else {
342 node = node.nextSibling;
343 }
344 }
345 };
346
347 /**
348 * Set the fontSize and lineHeight of this hterm.ScrollPort.
284 * 349 *
285 * @param {integer} fontSize The css font-size, in pixels. 350 * @param {integer} fontSize The css font-size, in pixels.
286 * @param {integer} opt_lineHeight Optional css line-height in pixels. 351 * @param {integer} opt_lineHeight Optional css line-height in pixels.
287 * If omitted it will be computed based on the fontSize. 352 * If omitted it will be computed based on the fontSize.
288 */ 353 */
289 ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) { 354 hterm.ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) {
290 this.fontSize_ = fontSize; 355 this.fontSize_ = fontSize;
291 this.rowHeight_ = opt_lineHeight || fontSize + 2; 356 this.rowHeight_ = opt_lineHeight || fontSize + 2;
292 357
293 this.screen_.style.fontSize = this.fontSize_ + 'px'; 358 this.screen_.style.fontSize = this.fontSize_ + 'px';
294 this.screen_.style.lineHeight = this.rowHeight_ + 'px'; 359 this.screen_.style.lineHeight = this.rowHeight_ + 'px';
360 this.xrowCssRule_.style.height = this.rowHeight_ + 'px';
295 361
296 this.topSelectBag_.style.height = this.rowHeight_ + 'px'; 362 this.topSelectBag_.style.height = this.rowHeight_ + 'px';
297 this.bottomSelectBag_.style.height = this.rowHeight_ + 'px'; 363 this.bottomSelectBag_.style.height = this.rowHeight_ + 'px';
298 364
299 if (this.DEBUG_) { 365 if (this.DEBUG_) {
300 // When we're debugging we add padding to the body so that the offscreen 366 // When we're debugging we add padding to the body so that the offscreen
301 // elements are visible. 367 // elements are visible.
302 this.document_.body.style.paddingTop = 3 * this.rowHeight_ + 'px'; 368 this.document_.body.style.paddingTop = 3 * this.rowHeight_ + 'px';
303 this.document_.body.style.paddingBottom = 3 * this.rowHeight_ + 'px'; 369 this.document_.body.style.paddingBottom = 3 * this.rowHeight_ + 'px';
304 } 370 }
305 371
306 this.resize(); 372 this.resize();
307 }; 373 };
308 374
309 /** 375 /**
310 * Reset dimensions and visible row count to account for a change in the 376 * Reset dimensions and visible row count to account for a change in the
311 * dimensions of the 'x-screen'. 377 * dimensions of the 'x-screen'.
312 */ 378 */
313 ScrollPort.prototype.resize = function() { 379 hterm.ScrollPort.prototype.resize = function() {
314 var screenWidth = this.screen_.clientWidth; 380 var screenWidth = this.screen_.clientWidth;
315 var screenHeight = this.screen_.clientHeight; 381 var screenHeight = this.screen_.clientHeight;
316 382
317 // We don't want to show a partial row because it would be distracting 383 // We don't want to show a partial row because it would be distracting
318 // in a terminal, so we floor any fractional row count. 384 // in a terminal, so we floor any fractional row count.
319 this.visibleRowCount = Math.floor(screenHeight / this.rowHeight_); 385 this.visibleRowCount = Math.floor(screenHeight / this.rowHeight_);
320 386
321 // Then compute the height of our integral number of rows. 387 // Then compute the height of our integral number of rows.
322 var visibleRowsHeight = this.visibleRowCount * this.rowHeight_; 388 var visibleRowsHeight = this.visibleRowCount * this.rowHeight_;
323 389
324 // Then the difference between the screen height and total row height needs to 390 // Then the difference between the screen height and total row height needs to
325 // be made up for as top margin. We need to record this value so it 391 // be made up for as top margin. We need to record this value so it
326 // can be used later to determine the topRowIndex. 392 // can be used later to determine the topRowIndex.
327 this.visibleRowTopMargin = screenHeight - visibleRowsHeight; 393 this.visibleRowTopMargin = screenHeight - visibleRowsHeight;
328 this.topFold_.style.marginBottom = this.visibleRowTopMargin + 'px'; 394 this.topFold_.style.marginBottom = this.visibleRowTopMargin + 'px';
329 395
330 // Resize the scroll area to appear as though it contains every row.
331 this.scrollArea_.style.height = (this.rowHeight_ *
332 this.rowProvider_.getRowCount() +
333 this.visibleRowTopMargin + 'px');
334
335 // Set the dimensions of the visible rows container. 396 // Set the dimensions of the visible rows container.
336 this.rowNodes_.style.width = screenWidth + 'px'; 397 this.rowNodes_.style.width = screenWidth + 'px';
337 this.rowNodes_.style.height = visibleRowsHeight + 'px'; 398 this.rowNodes_.style.height = visibleRowsHeight + 'px';
338 this.rowNodes_.style.left = this.screen_.offsetLeft + 'px'; 399 this.rowNodes_.style.left = this.screen_.offsetLeft + 'px';
339 400
340 var self = this; 401 var self = this;
341 this.publish('resize', 402 this.publish
342 { scrollPort: this }, 403 ('resize', { scrollPort: this },
343 function() { self.redraw_() }); 404 function() {
405 var index = self.bottomFold_.previousSibling.rowIndex;
406 self.scrollRowToBottom(index);
407 });
408 };
409
410 hterm.ScrollPort.prototype.syncScrollHeight = function() {
411 // Resize the scroll area to appear as though it contains every row.
412 this.scrollArea_.style.height = (this.rowHeight_ *
413 this.rowProvider_.getRowCount() +
414 this.visibleRowTopMargin + 'px');
344 }; 415 };
345 416
346 /** 417 /**
347 * Redraw the current ScrollPort based on the current scrollbar position. 418 * Redraw the current hterm.ScrollPort based on the current scrollbar position.
348 * 419 *
349 * When redrawing, we are careful to make sure that the rows that start or end 420 * When redrawing, we are careful to make sure that the rows that start or end
350 * the current selection are not touched in any way. Doing so would disturb 421 * the current selection are not touched in any way. Doing so would disturb
351 * the selection, and cleaning up after that would cause flashes at best and 422 * the selection, and cleaning up after that would cause flashes at best and
352 * incorrect selection at worst. Instead, we modify the DOM around these nodes. 423 * incorrect selection at worst. Instead, we modify the DOM around these nodes.
353 * We even stash the selection start/end outside of the visible area if 424 * We even stash the selection start/end outside of the visible area if
354 * they are not supposed to be visible in the ScrollPort. 425 * they are not supposed to be visible in the hterm.ScrollPort.
355 */ 426 */
356 ScrollPort.prototype.redraw_ = function() { 427 hterm.ScrollPort.prototype.redraw_ = function() {
357 this.resetSelectBags_(); 428 this.resetSelectBags_();
358 this.selection_.sync(); 429 this.selection_.sync();
359 430
431 this.syncScrollHeight();
432
360 this.currentRowNodeCache_ = {}; 433 this.currentRowNodeCache_ = {};
361 434
362 var topRowIndex = this.getTopRowIndex(); 435 var topRowIndex = this.getTopRowIndex();
363 var bottomRowIndex = this.getBottomRowIndex(topRowIndex); 436 var bottomRowIndex = this.getBottomRowIndex(topRowIndex);
364 437
365 this.drawTopFold_(topRowIndex); 438 this.drawTopFold_(topRowIndex);
366 this.drawBottomFold_(bottomRowIndex); 439 this.drawBottomFold_(bottomRowIndex);
367 this.drawVisibleRows_(topRowIndex, bottomRowIndex); 440 this.drawVisibleRows_(topRowIndex, bottomRowIndex);
368 441
369 this.syncRowNodesTop_(); 442 this.syncRowNodesTop_();
370 443
371 this.previousRowNodeCache_ = this.currentRowNodeCache_; 444 this.previousRowNodeCache_ = this.currentRowNodeCache_;
372 this.currentRowNodeCache_ = null; 445 this.currentRowNodeCache_ = null;
373 }; 446 };
374 447
375 /** 448 /**
376 * Ensure that the nodes above the top fold are as they should be. 449 * Ensure that the nodes above the top fold are as they should be.
377 * 450 *
378 * If the selection start and/or end nodes are above the visible range 451 * If the selection start and/or end nodes are above the visible range
379 * of this ScrollPort then the dom will be adjusted so that they appear before 452 * of this hterm.ScrollPort then the dom will be adjusted so that they appear
380 * the top fold (the first x-fold element, aka this.topFold). 453 * before the top fold (the first x-fold element, aka this.topFold).
381 * 454 *
382 * If not, the top fold will be the first element. 455 * If not, the top fold will be the first element.
383 * 456 *
384 * It is critical that this method does not move the selection nodes. Doing 457 * It is critical that this method does not move the selection nodes. Doing
385 * so would clear the current selection. Instead, the rest of the DOM is 458 * so would clear the current selection. Instead, the rest of the DOM is
386 * adjusted around them. 459 * adjusted around them.
387 */ 460 */
388 ScrollPort.prototype.drawTopFold_ = function(topRowIndex) { 461 hterm.ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
389 if (!this.selection_.startRow || 462 if (!this.selection_.startRow ||
390 this.selection_.startRow.rowIndex >= topRowIndex) { 463 this.selection_.startRow.rowIndex >= topRowIndex) {
391 // Selection is entirely below the top fold, just make sure the fold is 464 // Selection is entirely below the top fold, just make sure the fold is
392 // the first child. 465 // the first child.
393 if (this.rowNodes_.firstChild != this.topFold_) 466 if (this.rowNodes_.firstChild != this.topFold_)
394 this.rowNodes_.insertBefore(this.topFold_, this.rowNodes_.firstChild); 467 this.rowNodes_.insertBefore(this.topFold_, this.rowNodes_.firstChild);
395 468
396 return; 469 return;
397 } 470 }
398 471
399 if (!this.selection_.isMultiline || 472 if (!this.selection_.isMultiline ||
400 this.selection_.endRow.rowIndex >= topRowIndex) { 473 this.selection_.endRow.rowIndex >= topRowIndex) {
401 // Only the startRow is above the fold. 474 // Only the startRow is above the fold.
402 if (this.selection_.startRow.nextSibling != this.topFold_) 475 if (this.selection_.startRow.nextSibling != this.topFold_)
403 this.rowNodes_.insertBefore(this.topFold_, 476 this.rowNodes_.insertBefore(this.topFold_,
404 this.selection_.startRow.nextSibling); 477 this.selection_.startRow.nextSibling);
405 } else { 478 } else {
406 // Both rows are above the fold. 479 // Both rows are above the fold.
407 if (this.selection_.endRow.nextSibling != this.topFold_) { 480 if (this.selection_.endRow.nextSibling != this.topFold_) {
408 this.rowNodes_.insertBefore(this.topFold_, 481 this.rowNodes_.insertBefore(this.topFold_,
409 this.selection_.endRow.nextSibling); 482 this.selection_.endRow.nextSibling);
410 } 483 }
411 484
412 // Trim any intermediate lines. 485 // Trim any intermediate lines.
413 while (this.selection_.startRow.nextSibling != 486 while (this.selection_.startRow.nextSibling !=
414 this.selection_.endRow) { 487 this.selection_.endRow) {
415 this.rowNodes_.removeChild(this.selection_.startRow.nextSibling); 488 this.rowNodes_.removeChild(this.selection_.startRow.nextSibling);
416 } 489 }
417 } 490 }
418 491
419 while(this.rowNodes_.firstChild != this.selection_.startRow) { 492 while(this.rowNodes_.firstChild != this.selection_.startRow) {
420 this.rowNodes_.removeChild(this.rowNodes_.firstChild); 493 this.rowNodes_.removeChild(this.rowNodes_.firstChild);
421 } 494 }
422 }; 495 };
423 496
424 /** 497 /**
425 * Ensure that the nodes below the bottom fold are as they should be. 498 * Ensure that the nodes below the bottom fold are as they should be.
426 * 499 *
427 * If the selection start and/or end nodes are below the visible range 500 * If the selection start and/or end nodes are below the visible range
428 * of this ScrollPort then the dom will be adjusted so that they appear after 501 * of this hterm.ScrollPort then the dom will be adjusted so that they appear
429 * the bottom fold (the second x-fold element, aka this.bottomFold). 502 * after the bottom fold (the second x-fold element, aka this.bottomFold).
430 * 503 *
431 * If not, the bottom fold will be the last element. 504 * If not, the bottom fold will be the last element.
432 * 505 *
433 * It is critical that this method does not move the selection nodes. Doing 506 * It is critical that this method does not move the selection nodes. Doing
434 * so would clear the current selection. Instead, the rest of the DOM is 507 * so would clear the current selection. Instead, the rest of the DOM is
435 * adjusted around them. 508 * adjusted around them.
436 */ 509 */
437 ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) { 510 hterm.ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) {
438 if (!this.selection_.endRow || 511 if (!this.selection_.endRow ||
439 this.selection_.endRow.rowIndex <= bottomRowIndex) { 512 this.selection_.endRow.rowIndex <= bottomRowIndex) {
440 // Selection is entirely above the bottom fold, just make sure the fold is 513 // Selection is entirely above the bottom fold, just make sure the fold is
441 // the last child. 514 // the last child.
442 if (this.rowNodes_.lastChild != this.bottomFold_) 515 if (this.rowNodes_.lastChild != this.bottomFold_)
443 this.rowNodes_.appendChild(this.bottomFold_); 516 this.rowNodes_.appendChild(this.bottomFold_);
444 517
445 return; 518 return;
446 } 519 }
447 520
(...skipping 29 matching lines...) Expand all
477 * run, and that they have left any visible selection row (selection start 550 * run, and that they have left any visible selection row (selection start
478 * or selection end) between the folds. 551 * or selection end) between the folds.
479 * 552 *
480 * It recycles DOM nodes from the previous redraw where possible, but will ask 553 * It recycles DOM nodes from the previous redraw where possible, but will ask
481 * the rowSource to make new nodes if necessary. 554 * the rowSource to make new nodes if necessary.
482 * 555 *
483 * It is critical that this method does not move the selection nodes. Doing 556 * It is critical that this method does not move the selection nodes. Doing
484 * so would clear the current selection. Instead, the rest of the DOM is 557 * so would clear the current selection. Instead, the rest of the DOM is
485 * adjusted around them. 558 * adjusted around them.
486 */ 559 */
487 ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) { 560 hterm.ScrollPort.prototype.drawVisibleRows_ = function(
561 topRowIndex, bottomRowIndex) {
488 var self = this; 562 var self = this;
489 563
490 // Keep removing nodes, starting with currentNode, until we encounter 564 // Keep removing nodes, starting with currentNode, until we encounter
491 // targetNode. Throws on failure. 565 // targetNode. Throws on failure.
492 function removeUntilNode(currentNode, targetNode) { 566 function removeUntilNode(currentNode, targetNode) {
493 while (currentNode != targetNode) { 567 while (currentNode != targetNode) {
494 if (!currentNode) 568 if (!currentNode)
495 throw 'Did not encounter target node'; 569 throw 'Did not encounter target node';
496 570
497 if (currentNode == self.bottomFold_) 571 if (currentNode == self.bottomFold_)
498 throw 'Encountered bottom fold before target node'; 572 throw 'Encountered bottom fold before target node';
499 573
500 var deadNode = currentNode; 574 var deadNode = currentNode;
501 currentNode = currentNode.nextSibling; 575 currentNode = currentNode.nextSibling;
502 deadNode.parentNode.removeChild(deadNode); 576 deadNode.parentNode.removeChild(deadNode);
503 } 577 }
504 } 578 }
505 579
506 // Shorthand for things we're going to use a lot. 580 // Shorthand for things we're going to use a lot.
507 var selectionStartRow = this.selection_.startRow; 581 var selectionStartRow = this.selection_.startRow;
508 var selectionEndRow = this.selection_.endRow; 582 var selectionEndRow = this.selection_.endRow;
509 var bottomFold = this.bottomFold_; 583 var bottomFold = this.bottomFold_;
510 584
511 // The node we're examining during the current iteration. 585 // The node we're examining during the current iteration.
512 var node = this.topFold_.nextSibling; 586 var node = this.topFold_.nextSibling;
513 587
514 for (var drawCount = 0; drawCount < this.visibleRowCount; drawCount++) { 588 var targetDrawCount = Math.min(this.visibleRowCount,
589 this.rowProvider_.getRowCount());
590
591 for (var drawCount = 0; drawCount < targetDrawCount; drawCount++) {
515 var rowIndex = topRowIndex + drawCount; 592 var rowIndex = topRowIndex + drawCount;
516 593
517 if (node == bottomFold) { 594 if (node == bottomFold) {
518 // We've hit the bottom fold, we need to insert a new row. 595 // We've hit the bottom fold, we need to insert a new row.
519 var newNode = this.fetchRowNode_(rowIndex); 596 var newNode = this.fetchRowNode_(rowIndex);
597 if (!newNode) {
598 console.log("Couldn't fetch row index: " + rowIndex);
599 break;
600 }
601
520 this.rowNodes_.insertBefore(newNode, node); 602 this.rowNodes_.insertBefore(newNode, node);
521 continue; 603 continue;
522 } 604 }
523 605
524 if (node.rowIndex == rowIndex) { 606 if (node.rowIndex == rowIndex) {
525 // This node is in the right place, move along. 607 // This node is in the right place, move along.
526 node = node.nextSibling; 608 node = node.nextSibling;
527 continue; 609 continue;
528 } 610 }
529 611
(...skipping 10 matching lines...) Expand all
540 // we find it. 622 // we find it.
541 removeUntilNode(node, selectionEndRow); 623 removeUntilNode(node, selectionEndRow);
542 node = selectionEndRow.nextSibling; 624 node = selectionEndRow.nextSibling;
543 continue; 625 continue;
544 } 626 }
545 627
546 if (node == selectionStartRow || node == selectionEndRow) { 628 if (node == selectionStartRow || node == selectionEndRow) {
547 // We encountered the start/end of the selection, but we don't want it 629 // We encountered the start/end of the selection, but we don't want it
548 // yet. Insert a new row instead. 630 // yet. Insert a new row instead.
549 var newNode = this.fetchRowNode_(rowIndex); 631 var newNode = this.fetchRowNode_(rowIndex);
632 if (!newNode) {
633 console.log("Couldn't fetch row index: " + rowIndex);
634 break;
635 }
636
550 this.rowNodes_.insertBefore(newNode, node); 637 this.rowNodes_.insertBefore(newNode, node);
551 continue; 638 continue;
552 } 639 }
553 640
554 // There is nothing special about this node, but it's in our way. Replace 641 // There is nothing special about this node, but it's in our way. Replace
555 // it with the node that should be here. 642 // it with the node that should be here.
556 var newNode = this.fetchRowNode_(rowIndex); 643 var newNode = this.fetchRowNode_(rowIndex);
644 if (!newNode) {
645 console.log("Couldn't fetch row index: " + rowIndex);
646 break;
647 }
648
649 if (node == newNode) {
650 node = node.nextSibling;
651 continue;
652 }
653
557 this.rowNodes_.insertBefore(newNode, node); 654 this.rowNodes_.insertBefore(newNode, node);
655 if (!newNode.nextSibling)
656 debugger;
558 this.rowNodes_.removeChild(node); 657 this.rowNodes_.removeChild(node);
559 node = newNode.nextSibling; 658 node = newNode.nextSibling;
560 } 659 }
561 660
562 if (node != this.bottomFold_) 661 if (node != this.bottomFold_)
563 removeUntilNode(node, bottomFold); 662 removeUntilNode(node, bottomFold);
564 }; 663 };
565 664
566 /** 665 /**
567 * Empty out both select bags and remove them from the document. 666 * Empty out both select bags and remove them from the document.
568 * 667 *
569 * These nodes hold the text between the start and end of the selection 668 * These nodes hold the text between the start and end of the selection
570 * when that text is otherwise off screen. They are filled out in the 669 * when that text is otherwise off screen. They are filled out in the
571 * onCopy_ event. 670 * onCopy_ event.
572 */ 671 */
573 ScrollPort.prototype.resetSelectBags_ = function() { 672 hterm.ScrollPort.prototype.resetSelectBags_ = function() {
574 if (this.topSelectBag_.parentNode) { 673 if (this.topSelectBag_.parentNode) {
575 this.topSelectBag_.textContent = ''; 674 this.topSelectBag_.textContent = '';
576 this.topSelectBag_.parentNode.removeChild(this.topSelectBag_); 675 this.topSelectBag_.parentNode.removeChild(this.topSelectBag_);
577 } 676 }
578 677
579 if (this.bottomSelectBag_.parentNode) { 678 if (this.bottomSelectBag_.parentNode) {
580 this.bottomSelectBag_.textContent = ''; 679 this.bottomSelectBag_.textContent = '';
581 this.bottomSelectBag_.parentNode.removeChild(this.bottomSelectBag_); 680 this.bottomSelectBag_.parentNode.removeChild(this.bottomSelectBag_);
582 } 681 }
583 }; 682 };
584 683
585 /** 684 /**
586 * Set the top coordinate of the row nodes. 685 * Set the top coordinate of the row nodes.
587 * 686 *
588 * The rowNodes_ are a fixed position DOM element. When nodes are stashed 687 * The rowNodes_ are a fixed position DOM element. When nodes are stashed
589 * above the top fold, we need to adjust the top position of rowNodes_ 688 * above the top fold, we need to adjust the top position of rowNodes_
590 * so that the first node *after* the top fold is always the first visible 689 * so that the first node *after* the top fold is always the first visible
591 * DOM node. 690 * DOM node.
592 */ 691 */
593 ScrollPort.prototype.syncRowNodesTop_ = function() { 692 hterm.ScrollPort.prototype.syncRowNodesTop_ = function() {
594 var topMargin = 0; 693 var topMargin = 0;
595 var node = this.topFold_.previousSibling; 694 var node = this.topFold_.previousSibling;
596 while (node) { 695 while (node) {
597 topMargin += this.rowHeight_; 696 topMargin += this.rowHeight_;
598 node = node.previousSibling; 697 node = node.previousSibling;
599 } 698 }
600 699
601 this.rowNodes_.style.top = this.screen_.offsetTop - topMargin + 'px'; 700 this.rowNodes_.style.top = this.screen_.offsetTop - topMargin + 'px';
602 }; 701 };
603 702
604 /** 703 /**
605 * Place a row node in the cache of visible nodes. 704 * Place a row node in the cache of visible nodes.
606 * 705 *
607 * This method may only be used during a redraw_. 706 * This method may only be used during a redraw_.
608 */ 707 */
609 ScrollPort.prototype.cacheRowNode_ = function(rowNode) { 708 hterm.ScrollPort.prototype.cacheRowNode_ = function(rowNode) {
610 this.currentRowNodeCache_[rowNode.rowIndex] = rowNode; 709 this.currentRowNodeCache_[rowNode.rowIndex] = rowNode;
611 }; 710 };
612 711
613 /** 712 /**
614 * Fetch the row node for the given index. 713 * Fetch the row node for the given index.
615 * 714 *
616 * This will return a node from the cache if possible, or will request one 715 * This will return a node from the cache if possible, or will request one
617 * from the RowProvider if not. 716 * from the RowProvider if not.
618 * 717 *
619 * If a redraw_ is in progress the row will be added to the current cache. 718 * If a redraw_ is in progress the row will be added to the current cache.
620 */ 719 */
621 ScrollPort.prototype.fetchRowNode_ = function(rowIndex) { 720 hterm.ScrollPort.prototype.fetchRowNode_ = function(rowIndex) {
622 var node; 721 var node;
623 722
624 if (this.previousRowNodeCache_ && rowIndex in this.previousRowNodeCache_) { 723 if (this.previousRowNodeCache_ && rowIndex in this.previousRowNodeCache_) {
625 node = this.previousRowNodeCache_[rowIndex]; 724 node = this.previousRowNodeCache_[rowIndex];
626 } else { 725 } else {
627 node = this.rowProvider_.getRowNode(rowIndex); 726 node = this.rowProvider_.getRowNode(rowIndex);
628 } 727 }
629 728
630 if (this.currentRowNodeCache_) 729 if (this.currentRowNodeCache_)
631 this.cacheRowNode_(node); 730 this.cacheRowNode_(node);
632 731
633 return node; 732 return node;
634 }; 733 };
635 734
636 /** 735 /**
637 * Select all rows in the viewport. 736 * Select all rows in the viewport.
638 */ 737 */
639 ScrollPort.prototype.selectAll = function() { 738 hterm.ScrollPort.prototype.selectAll = function() {
640 var firstRow; 739 var firstRow;
641 740
642 if (this.topFold_.nextSibling.rowIndex != 0) { 741 if (this.topFold_.nextSibling.rowIndex != 0) {
643 while (this.topFold_.previousSibling) { 742 while (this.topFold_.previousSibling) {
644 this.rowNodes_.removeChild(this.topFold_.previousSibling); 743 this.rowNodes_.removeChild(this.topFold_.previousSibling);
645 } 744 }
646 745
647 firstRow = this.fetchRowNode_(0); 746 firstRow = this.fetchRowNode_(0);
648 this.rowNodes_.insertBefore(firstRow, this.topFold_); 747 this.rowNodes_.insertBefore(firstRow, this.topFold_);
649 this.syncRowNodesTop_(); 748 this.syncRowNodesTop_();
(...skipping 18 matching lines...) Expand all
668 var selection = this.document_.getSelection(); 767 var selection = this.document_.getSelection();
669 selection.collapse(firstRow, 0); 768 selection.collapse(firstRow, 0);
670 selection.extend(lastRow, lastRow.childNodes.length); 769 selection.extend(lastRow, lastRow.childNodes.length);
671 770
672 this.selection_.sync(); 771 this.selection_.sync();
673 }; 772 };
674 773
675 /** 774 /**
676 * Return the maximum scroll position in pixels. 775 * Return the maximum scroll position in pixels.
677 */ 776 */
678 ScrollPort.prototype.getScrollMax_ = function(e) { 777 hterm.ScrollPort.prototype.getScrollMax_ = function(e) {
679 return (this.scrollArea_.clientHeight + this.visibleRowTopMargin - 778 return (this.scrollArea_.clientHeight + this.visibleRowTopMargin -
680 this.screen_.clientHeight); 779 this.screen_.clientHeight);
681 }; 780 };
682 781
683 /** 782 /**
684 * Scroll the given rowIndex to the top of the ScrollPort. 783 * Scroll the given rowIndex to the top of the hterm.ScrollPort.
685 * 784 *
686 * @param {integer} rowIndex Index of the target row. 785 * @param {integer} rowIndex Index of the target row.
687 */ 786 */
688 ScrollPort.prototype.scrollRowToTop = function(rowIndex) { 787 hterm.ScrollPort.prototype.scrollRowToTop = function(rowIndex) {
788 this.syncScrollHeight();
789
689 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin; 790 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
690 791
691 var scrollMax = this.getScrollMax_(); 792 var scrollMax = this.getScrollMax_();
692 if (scrollTop > scrollMax) 793 if (scrollTop > scrollMax)
693 scrollTop = scrollMax; 794 scrollTop = scrollMax;
694 795
695 this.screen_.scrollTop = scrollTop; 796 this.screen_.scrollTop = scrollTop;
696 this.redraw_(); 797 this.redraw_();
697 }; 798 };
698 799
699 /** 800 /**
700 * Scroll the given rowIndex to the bottom of the ScrollPort. 801 * Scroll the given rowIndex to the bottom of the hterm.ScrollPort.
701 * 802 *
702 * @param {integer} rowIndex Index of the target row. 803 * @param {integer} rowIndex Index of the target row.
703 */ 804 */
704 ScrollPort.prototype.scrollRowToBottom = function(rowIndex) { 805 hterm.ScrollPort.prototype.scrollRowToBottom = function(rowIndex) {
806 this.syncScrollHeight();
807
705 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin; 808 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
706 scrollTop -= (this.visibleRowCount - 1) * this.rowHeight_; 809 scrollTop -= (this.visibleRowCount - 1) * this.rowHeight_;
707 810
708 if (scrollTop < 0) 811 if (scrollTop < 0)
709 scrollTop = 0; 812 scrollTop = 0;
710 813
711 this.screen_.scrollTop = scrollTop; 814 this.screen_.scrollTop = scrollTop;
712 this.redraw_(); 815 this.redraw_();
713 }; 816 };
714 817
715 /** 818 /**
716 * Return the row index of the first visible row. 819 * Return the row index of the first visible row.
717 * 820 *
718 * This is based on the scroll position. If a redraw_ is in progress this 821 * This is based on the scroll position. If a redraw_ is in progress this
719 * returns the row that *should* be at the top. 822 * returns the row that *should* be at the top.
720 */ 823 */
721 ScrollPort.prototype.getTopRowIndex = function() { 824 hterm.ScrollPort.prototype.getTopRowIndex = function() {
722 return Math.floor(this.screen_.scrollTop / this.rowHeight_); 825 return Math.floor(this.screen_.scrollTop / this.rowHeight_);
723 }; 826 };
724 827
725 /** 828 /**
726 * Return the row index of the last visible row. 829 * Return the row index of the last visible row.
727 * 830 *
728 * This is based on the scroll position. If a redraw_ is in progress this 831 * This is based on the scroll position. If a redraw_ is in progress this
729 * returns the row that *should* be at the bottom. 832 * returns the row that *should* be at the bottom.
730 */ 833 */
731 ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) { 834 hterm.ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) {
732 return topRowIndex + this.visibleRowCount - 1; 835 return topRowIndex + this.visibleRowCount - 1;
733 }; 836 };
734 837
735 /** 838 /**
736 * Handler for scroll events. 839 * Handler for scroll events.
737 * 840 *
738 * The onScroll event fires when the user moves the scrollbar associated with 841 * The onScroll event fires when scrollArea's scrollTop property changes. This
739 * this ScrollPort. 842 * may be due to the user manually move the scrollbar, or a programmatic change.
740 */ 843 */
741 ScrollPort.prototype.onScroll_ = function(e) { 844 hterm.ScrollPort.prototype.onScroll_ = function(e) {
742 this.redraw_(); 845 this.redraw_();
846 this.publish('scroll', { scrollPort: this });
743 }; 847 };
744 848
745 /** 849 /**
746 * Handler for scroll-wheel events. 850 * Handler for scroll-wheel events.
747 * 851 *
748 * The onScrollWheel event fires when the user moves their scrollwheel over this 852 * The onScrollWheel event fires when the user moves their scrollwheel over this
749 * ScrollPort. Because the frontmost element in the ScrollPort is a fixed 853 * hterm.ScrollPort. Because the frontmost element in the hterm.ScrollPort is
750 * position DIV, the scroll wheel does nothing by default. Instead, we have 854 * a fixed position DIV, the scroll wheel does nothing by default. Instead, we
751 * to handle it manually. 855 * have to handle it manually.
752 */ 856 */
753 ScrollPort.prototype.onScrollWheel_ = function(e) { 857 hterm.ScrollPort.prototype.onScrollWheel_ = function(e) {
754 var top = this.screen_.scrollTop - e.wheelDeltaY; 858 var top = this.screen_.scrollTop - e.wheelDeltaY;
755 if (top < 0) 859 if (top < 0)
756 top = 0; 860 top = 0;
757 861
758 var scrollMax = this.getScrollMax_(); 862 var scrollMax = this.getScrollMax_();
759 if (top > scrollMax) 863 if (top > scrollMax)
760 top = scrollMax; 864 top = scrollMax;
761 865
762 // Moving scrollTop causes a scroll event, which triggers the redraw. 866 // Moving scrollTop causes a scroll event, which triggers the redraw.
763 this.screen_.scrollTop = top; 867 this.screen_.scrollTop = top;
764 }; 868 };
765 869
766 /** 870 /**
767 * Handler for resize events. 871 * Handler for resize events.
768 * 872 *
769 * The browser will resize us such that the top row stays at the top, but we 873 * The browser will resize us such that the top row stays at the top, but we
770 * prefer to the bottom row to stay at the bottom. 874 * prefer to the bottom row to stay at the bottom.
771 */ 875 */
772 ScrollPort.prototype.onResize = function(e) { 876 hterm.ScrollPort.prototype.onResize = function(e) {
773 var index = this.bottomFold_.previousSibling.rowIndex;
774 this.resize(); 877 this.resize();
775 this.scrollRowToBottom(index);
776 }; 878 };
777 879
778 /** 880 /**
779 * Handler for copy-to-clipboard events. 881 * Handler for copy-to-clipboard events.
780 * 882 *
781 * If some or all of the selected rows are off screen we may need to fill in 883 * If some or all of the selected rows are off screen we may need to fill in
782 * the rows between selection start and selection end. This handler determines 884 * the rows between selection start and selection end. This handler determines
783 * if we're missing some of the selected text, and if so populates one or both 885 * if we're missing some of the selected text, and if so populates one or both
784 * of the "select bags" with the missing text. 886 * of the "select bags" with the missing text.
785 */ 887 */
786 ScrollPort.prototype.onCopy_ = function(e) { 888 hterm.ScrollPort.prototype.onCopy_ = function(e) {
787 this.resetSelectBags_(); 889 this.resetSelectBags_();
788 this.selection_.sync(); 890 this.selection_.sync();
789 891
790 if (!this.selection_.startRow || 892 if (!this.selection_.startRow ||
791 this.selection_.endRow.rowIndex - this.selection_.startRow.rowIndex < 2) { 893 this.selection_.endRow.rowIndex - this.selection_.startRow.rowIndex < 2) {
792 return; 894 return;
793 } 895 }
794 896
795 var topRowIndex = this.getTopRowIndex(); 897 var topRowIndex = this.getTopRowIndex();
796 var bottomRowIndex = this.getBottomRowIndex(topRowIndex); 898 var bottomRowIndex = this.getBottomRowIndex(topRowIndex);
(...skipping 27 matching lines...) Expand all
824 } else { 926 } else {
825 // Selection starts above the bottom fold. 927 // Selection starts above the bottom fold.
826 startBackfillIndex = this.bottomFold_.previousSibling.rowIndex + 1; 928 startBackfillIndex = this.bottomFold_.previousSibling.rowIndex + 1;
827 } 929 }
828 930
829 this.bottomSelectBag_.textContent = this.rowProvider_.getRowsText( 931 this.bottomSelectBag_.textContent = this.rowProvider_.getRowsText(
830 startBackfillIndex, this.selection_.endRow.rowIndex); 932 startBackfillIndex, this.selection_.endRow.rowIndex);
831 this.rowNodes_.insertBefore(this.bottomSelectBag_, this.selection_.endRow); 933 this.rowNodes_.insertBefore(this.bottomSelectBag_, this.selection_.endRow);
832 } 934 }
833 }; 935 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698