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

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: Created 9 years, 1 month 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));
186 this.screen_.addEventListener('mousewheel', this.onScrollWheel_.bind(this)); 201 this.screen_.addEventListener('mousewheel', this.onScrollWheel_.bind(this));
187 this.screen_.addEventListener('copy', this.onCopy_.bind(this)); 202 this.screen_.addEventListener('copy', this.onCopy_.bind(this));
188 203
189 // This is the main container for the fixed rows. 204 // This is the main container for the fixed rows.
190 this.rowNodes_ = doc.createElement('div'); 205 this.rowNodes_ = doc.createElement('div');
191 this.rowNodes_.style.cssText = ( 206 this.rowNodes_.style.cssText = (
192 'display: block;' + 207 'display: -webkit-box;' +
193 'position: fixed;' + 208 'position: fixed;' +
194 '-webkit-user-select: text;'); 209 '-webkit-user-select: text;' +
210 '-webkit-box-orient: vertical;');
195 this.screen_.appendChild(this.rowNodes_); 211 this.screen_.appendChild(this.rowNodes_);
196 212
197 // Two nodes to hold offscreen text during the copy event. 213 // Two nodes to hold offscreen text during the copy event.
198 this.topSelectBag_ = doc.createElement('x-select-bag'); 214 this.topSelectBag_ = doc.createElement('x-select-bag');
199 this.topSelectBag_.style.cssText = ( 215 this.topSelectBag_.style.cssText = (
200 'display: block;' + 216 'display: block;' +
201 'overflow: hidden;' + 217 'overflow: hidden;' +
202 'white-space: pre;'); 218 'white-space: pre;');
203 219
204 this.bottomSelectBag_ = this.topSelectBag_.cloneNode(); 220 this.bottomSelectBag_ = this.topSelectBag_.cloneNode();
(...skipping 17 matching lines...) Expand all
222 // it in the selection when a user 'drag selects' upwards (drag the mouse to 238 // 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 239 // select and scroll at the same time). Without this, the selection gets
224 // out of whack. 240 // out of whack.
225 this.scrollArea_ = doc.createElement('div'); 241 this.scrollArea_ = doc.createElement('div');
226 this.scrollArea_.style.cssText = 'visibility: hidden'; 242 this.scrollArea_.style.cssText = 'visibility: hidden';
227 this.screen_.appendChild(this.scrollArea_); 243 this.screen_.appendChild(this.scrollArea_);
228 244
229 this.setRowMetrics(this.fontSize_, this.rowHeight_); 245 this.setRowMetrics(this.fontSize_, this.rowHeight_);
230 }; 246 };
231 247
232 ScrollPort.prototype.getRowHeight = function() { 248 hterm.ScrollPort.prototype.getForegroundColor = function() {
249 return this.document_.body.style.color;
250 };
251
252 hterm.ScrollPort.prototype.setForegroundColor = function(color) {
253 this.document_.body.style.color = color;
254 };
255
256 hterm.ScrollPort.prototype.getBackgroundColor = function() {
257 return this.document_.body.style.backgroundColor;
258 };
259
260 hterm.ScrollPort.prototype.setBackgroundColor = function(color) {
261 this.document_.body.style.backgroundColor = color;
262 };
263
264 hterm.ScrollPort.prototype.getRowHeight = function() {
233 return this.rowHeight_; 265 return this.rowHeight_;
234 }; 266 };
235 267
236 ScrollPort.prototype.getScreenWidth = function() { 268 hterm.ScrollPort.prototype.getScreenWidth = function() {
237 return this.screen_.clientWidth; 269 return this.screen_.clientWidth;
238 }; 270 };
239 271
240 ScrollPort.prototype.getScreenWidth = function() { 272 hterm.ScrollPort.prototype.getScreenHeight = function() {
Vladislav Kaznacheev 2011/11/28 14:00:07 Did I miss that in the original land CL? Shame on
241 return this.screen_.clientHeight; 273 return this.screen_.clientHeight;
242 }; 274 };
243 275
244 ScrollPort.prototype.getCharacterWidth = function() { 276 hterm.ScrollPort.prototype.getCharacterWidth = function() {
245 var span = this.document_.createElement('span'); 277 var span = this.document_.createElement('span');
246 span.textContent = '\xa0'; //   278 span.textContent = '\xa0'; //  
247 this.rowNodes_.appendChild(span); 279 this.rowNodes_.appendChild(span);
248 var width = span.offsetWidth; 280 var width = span.offsetWidth;
249 this.rowNodes_.removeChild(span); 281 this.rowNodes_.removeChild(span);
250 return width; 282 return width;
251 }; 283 };
252 284
253 /** 285 /**
254 * Return the document that holds the visible rows of this ScrollPort. 286 * Return the document that holds the visible rows of this hterm.ScrollPort.
255 */ 287 */
256 ScrollPort.prototype.getDocument = function() { 288 hterm.ScrollPort.prototype.getDocument = function() {
257 return this.document_; 289 return this.document_;
258 }; 290 };
259 291
260 /** 292 /**
261 * Clear out any cached rowNodes. 293 * Clear out any cached rowNodes.
262 */ 294 */
263 ScrollPort.prototype.resetCache = function() { 295 hterm.ScrollPort.prototype.resetCache = function() {
264 this.currentRowNodeCache_ = null; 296 this.currentRowNodeCache_ = null;
265 this.previousRowNodeCache_ = {}; 297 this.previousRowNodeCache_ = {};
266 }; 298 };
267 299
268 /** 300 /**
269 * Change the current rowProvider. 301 * Change the current rowProvider.
270 * 302 *
271 * This will clear the row cache and cause a redraw. 303 * This will clear the row cache and cause a redraw.
272 * 304 *
273 * @param {Object} rowProvider An object capable of providing the rows 305 * @param {Object} rowProvider An object capable of providing the rows
274 * in this ScrollPort. 306 * in this hterm.ScrollPort.
275 */ 307 */
276 ScrollPort.prototype.setRowProvider = function(rowProvider) { 308 hterm.ScrollPort.prototype.setRowProvider = function(rowProvider) {
277 this.resetCache_(); 309 this.resetCache();
278 this.rowProvider_ = rowProvider; 310 this.rowProvider_ = rowProvider;
279 this.redraw_(); 311 this.redraw_();
280 }; 312 };
281 313
314 hterm.ScrollPort.prototype.invalidateRowRange = function(start, end) {
315 this.resetCache();
316
317 var node = this.rowNodes_.firstChild;
318 while (node) {
319 if ('rowIndex' in node &&
320 node.rowIndex >= start && node.rowIndex <= end) {
321 var nextSibling = node.nextSibling;
322 this.rowNodes_.removeChild(node);
323 this.rowNodes_.insertBefore(this.rowProvider_.getRowNode(node.rowIndex),
324 nextSibling);
325
326 node = nextSibling;
327 } else {
328 node = node.nextSibling;
329 }
330 }
331 };
332
282 /** 333 /**
283 * Set the fontSize and lineHeight of this ScrollPort. 334 * Set the fontSize and lineHeight of this hterm.ScrollPort.
284 * 335 *
285 * @param {integer} fontSize The css font-size, in pixels. 336 * @param {integer} fontSize The css font-size, in pixels.
286 * @param {integer} opt_lineHeight Optional css line-height in pixels. 337 * @param {integer} opt_lineHeight Optional css line-height in pixels.
287 * If omitted it will be computed based on the fontSize. 338 * If omitted it will be computed based on the fontSize.
288 */ 339 */
289 ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) { 340 hterm.ScrollPort.prototype.setRowMetrics = function(fontSize, opt_lineHeight) {
290 this.fontSize_ = fontSize; 341 this.fontSize_ = fontSize;
291 this.rowHeight_ = opt_lineHeight || fontSize + 2; 342 this.rowHeight_ = opt_lineHeight || fontSize + 2;
292 343
293 this.screen_.style.fontSize = this.fontSize_ + 'px'; 344 this.screen_.style.fontSize = this.fontSize_ + 'px';
294 this.screen_.style.lineHeight = this.rowHeight_ + 'px'; 345 this.screen_.style.lineHeight = this.rowHeight_ + 'px';
346 this.xrowCssRule_.style.height = this.rowHeight_ + 'px';
295 347
296 this.topSelectBag_.style.height = this.rowHeight_ + 'px'; 348 this.topSelectBag_.style.height = this.rowHeight_ + 'px';
297 this.bottomSelectBag_.style.height = this.rowHeight_ + 'px'; 349 this.bottomSelectBag_.style.height = this.rowHeight_ + 'px';
298 350
299 if (this.DEBUG_) { 351 if (this.DEBUG_) {
300 // When we're debugging we add padding to the body so that the offscreen 352 // When we're debugging we add padding to the body so that the offscreen
301 // elements are visible. 353 // elements are visible.
302 this.document_.body.style.paddingTop = 3 * this.rowHeight_ + 'px'; 354 this.document_.body.style.paddingTop = 3 * this.rowHeight_ + 'px';
303 this.document_.body.style.paddingBottom = 3 * this.rowHeight_ + 'px'; 355 this.document_.body.style.paddingBottom = 3 * this.rowHeight_ + 'px';
304 } 356 }
305 357
306 this.resize(); 358 this.resize();
307 }; 359 };
308 360
309 /** 361 /**
310 * Reset dimensions and visible row count to account for a change in the 362 * Reset dimensions and visible row count to account for a change in the
311 * dimensions of the 'x-screen'. 363 * dimensions of the 'x-screen'.
312 */ 364 */
313 ScrollPort.prototype.resize = function() { 365 hterm.ScrollPort.prototype.resize = function() {
314 var screenWidth = this.screen_.clientWidth; 366 var screenWidth = this.screen_.clientWidth;
315 var screenHeight = this.screen_.clientHeight; 367 var screenHeight = this.screen_.clientHeight;
316 368
317 // We don't want to show a partial row because it would be distracting 369 // 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. 370 // in a terminal, so we floor any fractional row count.
319 this.visibleRowCount = Math.floor(screenHeight / this.rowHeight_); 371 this.visibleRowCount = Math.floor(screenHeight / this.rowHeight_);
320 372
321 // Then compute the height of our integral number of rows. 373 // Then compute the height of our integral number of rows.
322 var visibleRowsHeight = this.visibleRowCount * this.rowHeight_; 374 var visibleRowsHeight = this.visibleRowCount * this.rowHeight_;
323 375
324 // Then the difference between the screen height and total row height needs to 376 // 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 377 // be made up for as top margin. We need to record this value so it
326 // can be used later to determine the topRowIndex. 378 // can be used later to determine the topRowIndex.
327 this.visibleRowTopMargin = screenHeight - visibleRowsHeight; 379 this.visibleRowTopMargin = screenHeight - visibleRowsHeight;
328 this.topFold_.style.marginBottom = this.visibleRowTopMargin + 'px'; 380 this.topFold_.style.marginBottom = this.visibleRowTopMargin + 'px';
329 381
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. 382 // Set the dimensions of the visible rows container.
336 this.rowNodes_.style.width = screenWidth + 'px'; 383 this.rowNodes_.style.width = screenWidth + 'px';
337 this.rowNodes_.style.height = visibleRowsHeight + 'px'; 384 this.rowNodes_.style.height = visibleRowsHeight + 'px';
338 this.rowNodes_.style.left = this.screen_.offsetLeft + 'px'; 385 this.rowNodes_.style.left = this.screen_.offsetLeft + 'px';
339 386
340 var self = this; 387 var self = this;
341 this.publish('resize', 388 this.publish
342 { scrollPort: this }, 389 ('resize', { scrollPort: this },
343 function() { self.redraw_() }); 390 function() {
391 var index = self.bottomFold_.previousSibling.rowIndex;
392 self.scrollRowToBottom(index);
393 });
394 };
395
396 hterm.ScrollPort.prototype.syncScrollHeight = function() {
397 // Resize the scroll area to appear as though it contains every row.
398 this.scrollArea_.style.height = (this.rowHeight_ *
399 this.rowProvider_.getRowCount() +
400 this.visibleRowTopMargin + 'px');
344 }; 401 };
345 402
346 /** 403 /**
347 * Redraw the current ScrollPort based on the current scrollbar position. 404 * Redraw the current hterm.ScrollPort based on the current scrollbar position.
348 * 405 *
349 * When redrawing, we are careful to make sure that the rows that start or end 406 * 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 407 * 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 408 * 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. 409 * 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 410 * We even stash the selection start/end outside of the visible area if
354 * they are not supposed to be visible in the ScrollPort. 411 * they are not supposed to be visible in the hterm.ScrollPort.
355 */ 412 */
356 ScrollPort.prototype.redraw_ = function() { 413 hterm.ScrollPort.prototype.redraw_ = function() {
357 this.resetSelectBags_(); 414 this.resetSelectBags_();
358 this.selection_.sync(); 415 this.selection_.sync();
359 416
417 this.syncScrollHeight();
418
360 this.currentRowNodeCache_ = {}; 419 this.currentRowNodeCache_ = {};
361 420
362 var topRowIndex = this.getTopRowIndex(); 421 var topRowIndex = this.getTopRowIndex();
363 var bottomRowIndex = this.getBottomRowIndex(topRowIndex); 422 var bottomRowIndex = this.getBottomRowIndex(topRowIndex);
364 423
365 this.drawTopFold_(topRowIndex); 424 this.drawTopFold_(topRowIndex);
366 this.drawBottomFold_(bottomRowIndex); 425 this.drawBottomFold_(bottomRowIndex);
367 this.drawVisibleRows_(topRowIndex, bottomRowIndex); 426 this.drawVisibleRows_(topRowIndex, bottomRowIndex);
368 427
369 this.syncRowNodesTop_(); 428 this.syncRowNodesTop_();
370 429
371 this.previousRowNodeCache_ = this.currentRowNodeCache_; 430 this.previousRowNodeCache_ = this.currentRowNodeCache_;
372 this.currentRowNodeCache_ = null; 431 this.currentRowNodeCache_ = null;
373 }; 432 };
374 433
375 /** 434 /**
376 * Ensure that the nodes above the top fold are as they should be. 435 * Ensure that the nodes above the top fold are as they should be.
377 * 436 *
378 * If the selection start and/or end nodes are above the visible range 437 * 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 438 * 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). 439 * before the top fold (the first x-fold element, aka this.topFold).
381 * 440 *
382 * If not, the top fold will be the first element. 441 * If not, the top fold will be the first element.
383 * 442 *
384 * It is critical that this method does not move the selection nodes. Doing 443 * 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 444 * so would clear the current selection. Instead, the rest of the DOM is
386 * adjusted around them. 445 * adjusted around them.
387 */ 446 */
388 ScrollPort.prototype.drawTopFold_ = function(topRowIndex) { 447 hterm.ScrollPort.prototype.drawTopFold_ = function(topRowIndex) {
389 if (!this.selection_.startRow || 448 if (!this.selection_.startRow ||
390 this.selection_.startRow.rowIndex >= topRowIndex) { 449 this.selection_.startRow.rowIndex >= topRowIndex) {
391 // Selection is entirely below the top fold, just make sure the fold is 450 // Selection is entirely below the top fold, just make sure the fold is
392 // the first child. 451 // the first child.
393 if (this.rowNodes_.firstChild != this.topFold_) 452 if (this.rowNodes_.firstChild != this.topFold_)
394 this.rowNodes_.insertBefore(this.topFold_, this.rowNodes_.firstChild); 453 this.rowNodes_.insertBefore(this.topFold_, this.rowNodes_.firstChild);
395 454
396 return; 455 return;
397 } 456 }
398 457
399 if (!this.selection_.isMultiline || 458 if (!this.selection_.isMultiline ||
400 this.selection_.endRow.rowIndex >= topRowIndex) { 459 this.selection_.endRow.rowIndex >= topRowIndex) {
401 // Only the startRow is above the fold. 460 // Only the startRow is above the fold.
402 if (this.selection_.startRow.nextSibling != this.topFold_) 461 if (this.selection_.startRow.nextSibling != this.topFold_)
403 this.rowNodes_.insertBefore(this.topFold_, 462 this.rowNodes_.insertBefore(this.topFold_,
404 this.selection_.startRow.nextSibling); 463 this.selection_.startRow.nextSibling);
405 } else { 464 } else {
406 // Both rows are above the fold. 465 // Both rows are above the fold.
407 if (this.selection_.endRow.nextSibling != this.topFold_) { 466 if (this.selection_.endRow.nextSibling != this.topFold_) {
408 this.rowNodes_.insertBefore(this.topFold_, 467 this.rowNodes_.insertBefore(this.topFold_,
409 this.selection_.endRow.nextSibling); 468 this.selection_.endRow.nextSibling);
410 } 469 }
411 470
412 // Trim any intermediate lines. 471 // Trim any intermediate lines.
413 while (this.selection_.startRow.nextSibling != 472 while (this.selection_.startRow.nextSibling !=
414 this.selection_.endRow) { 473 this.selection_.endRow) {
415 this.rowNodes_.removeChild(this.selection_.startRow.nextSibling); 474 this.rowNodes_.removeChild(this.selection_.startRow.nextSibling);
416 } 475 }
417 } 476 }
418 477
419 while(this.rowNodes_.firstChild != this.selection_.startRow) { 478 while(this.rowNodes_.firstChild != this.selection_.startRow) {
420 this.rowNodes_.removeChild(this.rowNodes_.firstChild); 479 this.rowNodes_.removeChild(this.rowNodes_.firstChild);
421 } 480 }
422 }; 481 };
423 482
424 /** 483 /**
425 * Ensure that the nodes below the bottom fold are as they should be. 484 * Ensure that the nodes below the bottom fold are as they should be.
426 * 485 *
427 * If the selection start and/or end nodes are below the visible range 486 * 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 487 * 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). 488 * after the bottom fold (the second x-fold element, aka this.bottomFold).
430 * 489 *
431 * If not, the bottom fold will be the last element. 490 * If not, the bottom fold will be the last element.
432 * 491 *
433 * It is critical that this method does not move the selection nodes. Doing 492 * 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 493 * so would clear the current selection. Instead, the rest of the DOM is
435 * adjusted around them. 494 * adjusted around them.
436 */ 495 */
437 ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) { 496 hterm.ScrollPort.prototype.drawBottomFold_ = function(bottomRowIndex) {
438 if (!this.selection_.endRow || 497 if (!this.selection_.endRow ||
439 this.selection_.endRow.rowIndex <= bottomRowIndex) { 498 this.selection_.endRow.rowIndex <= bottomRowIndex) {
440 // Selection is entirely above the bottom fold, just make sure the fold is 499 // Selection is entirely above the bottom fold, just make sure the fold is
441 // the last child. 500 // the last child.
442 if (this.rowNodes_.lastChild != this.bottomFold_) 501 if (this.rowNodes_.lastChild != this.bottomFold_)
443 this.rowNodes_.appendChild(this.bottomFold_); 502 this.rowNodes_.appendChild(this.bottomFold_);
444 503
445 return; 504 return;
446 } 505 }
447 506
(...skipping 29 matching lines...) Expand all
477 * run, and that they have left any visible selection row (selection start 536 * run, and that they have left any visible selection row (selection start
478 * or selection end) between the folds. 537 * or selection end) between the folds.
479 * 538 *
480 * It recycles DOM nodes from the previous redraw where possible, but will ask 539 * It recycles DOM nodes from the previous redraw where possible, but will ask
481 * the rowSource to make new nodes if necessary. 540 * the rowSource to make new nodes if necessary.
482 * 541 *
483 * It is critical that this method does not move the selection nodes. Doing 542 * 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 543 * so would clear the current selection. Instead, the rest of the DOM is
485 * adjusted around them. 544 * adjusted around them.
486 */ 545 */
487 ScrollPort.prototype.drawVisibleRows_ = function(topRowIndex, bottomRowIndex) { 546 hterm.ScrollPort.prototype.drawVisibleRows_ = function(
547 topRowIndex, bottomRowIndex) {
488 var self = this; 548 var self = this;
489 549
490 // Keep removing nodes, starting with currentNode, until we encounter 550 // Keep removing nodes, starting with currentNode, until we encounter
491 // targetNode. Throws on failure. 551 // targetNode. Throws on failure.
492 function removeUntilNode(currentNode, targetNode) { 552 function removeUntilNode(currentNode, targetNode) {
493 while (currentNode != targetNode) { 553 while (currentNode != targetNode) {
494 if (!currentNode) 554 if (!currentNode)
495 throw 'Did not encounter target node'; 555 throw 'Did not encounter target node';
496 556
497 if (currentNode == self.bottomFold_) 557 if (currentNode == self.bottomFold_)
498 throw 'Encountered bottom fold before target node'; 558 throw 'Encountered bottom fold before target node';
499 559
500 var deadNode = currentNode; 560 var deadNode = currentNode;
501 currentNode = currentNode.nextSibling; 561 currentNode = currentNode.nextSibling;
502 deadNode.parentNode.removeChild(deadNode); 562 deadNode.parentNode.removeChild(deadNode);
503 } 563 }
504 } 564 }
505 565
506 // Shorthand for things we're going to use a lot. 566 // Shorthand for things we're going to use a lot.
507 var selectionStartRow = this.selection_.startRow; 567 var selectionStartRow = this.selection_.startRow;
508 var selectionEndRow = this.selection_.endRow; 568 var selectionEndRow = this.selection_.endRow;
509 var bottomFold = this.bottomFold_; 569 var bottomFold = this.bottomFold_;
510 570
511 // The node we're examining during the current iteration. 571 // The node we're examining during the current iteration.
512 var node = this.topFold_.nextSibling; 572 var node = this.topFold_.nextSibling;
513 573
514 for (var drawCount = 0; drawCount < this.visibleRowCount; drawCount++) { 574 var targetDrawCount = Math.min(this.visibleRowCount,
575 this.rowProvider_.getRowCount());
576
577 for (var drawCount = 0; drawCount < targetDrawCount; drawCount++) {
515 var rowIndex = topRowIndex + drawCount; 578 var rowIndex = topRowIndex + drawCount;
516 579
517 if (node == bottomFold) { 580 if (node == bottomFold) {
581 //console.log('bottom fold');
Vladislav Kaznacheev 2011/11/28 14:00:07 Debugging code left
rginda 2011/11/28 20:39:47 Done.
518 // We've hit the bottom fold, we need to insert a new row. 582 // We've hit the bottom fold, we need to insert a new row.
519 var newNode = this.fetchRowNode_(rowIndex); 583 var newNode = this.fetchRowNode_(rowIndex);
584 if (!newNode) {
585 console.log("Couldn't fetch row index: " + rowIndex);
586 break;
587 }
588
520 this.rowNodes_.insertBefore(newNode, node); 589 this.rowNodes_.insertBefore(newNode, node);
521 continue; 590 continue;
522 } 591 }
523 592
524 if (node.rowIndex == rowIndex) { 593 if (node.rowIndex == rowIndex) {
594 //console.log('move along');
525 // This node is in the right place, move along. 595 // This node is in the right place, move along.
526 node = node.nextSibling; 596 node = node.nextSibling;
527 continue; 597 continue;
528 } 598 }
529 599
530 if (selectionStartRow && selectionStartRow.rowIndex == rowIndex) { 600 if (selectionStartRow && selectionStartRow.rowIndex == rowIndex) {
601 //console.log('selstart');
531 // The selection start row is supposed to be here, remove nodes until 602 // The selection start row is supposed to be here, remove nodes until
532 // we find it. 603 // we find it.
533 removeUntilNode(node, selectionStartRow); 604 removeUntilNode(node, selectionStartRow);
534 node = selectionStartRow.nextSibling; 605 node = selectionStartRow.nextSibling;
535 continue; 606 continue;
536 } 607 }
537 608
538 if (selectionEndRow && selectionEndRow.rowIndex == rowIndex) { 609 if (selectionEndRow && selectionEndRow.rowIndex == rowIndex) {
610 //console.log('selend');
539 // The selection end row is supposed to be here, remove nodes until 611 // The selection end row is supposed to be here, remove nodes until
540 // we find it. 612 // we find it.
541 removeUntilNode(node, selectionEndRow); 613 removeUntilNode(node, selectionEndRow);
542 node = selectionEndRow.nextSibling; 614 node = selectionEndRow.nextSibling;
543 continue; 615 continue;
544 } 616 }
545 617
546 if (node == selectionStartRow || node == selectionEndRow) { 618 if (node == selectionStartRow || node == selectionEndRow) {
619 //console.log('notsel');
547 // We encountered the start/end of the selection, but we don't want it 620 // We encountered the start/end of the selection, but we don't want it
548 // yet. Insert a new row instead. 621 // yet. Insert a new row instead.
549 var newNode = this.fetchRowNode_(rowIndex); 622 var newNode = this.fetchRowNode_(rowIndex);
623 if (!newNode) {
624 console.log("Couldn't fetch row index: " + rowIndex);
625 break;
626 }
627
550 this.rowNodes_.insertBefore(newNode, node); 628 this.rowNodes_.insertBefore(newNode, node);
551 continue; 629 continue;
552 } 630 }
553 631
554 // There is nothing special about this node, but it's in our way. Replace 632 // There is nothing special about this node, but it's in our way. Replace
555 // it with the node that should be here. 633 // it with the node that should be here.
556 var newNode = this.fetchRowNode_(rowIndex); 634 var newNode = this.fetchRowNode_(rowIndex);
635 if (!newNode) {
636 console.log("Couldn't fetch row index: " + rowIndex);
637 break;
638 }
639
640 if (node == newNode) {
641 node = node.nextSibling;
642 continue;
643 }
644
557 this.rowNodes_.insertBefore(newNode, node); 645 this.rowNodes_.insertBefore(newNode, node);
646 if (!newNode.nextSibling)
647 debugger;
558 this.rowNodes_.removeChild(node); 648 this.rowNodes_.removeChild(node);
559 node = newNode.nextSibling; 649 node = newNode.nextSibling;
560 } 650 }
561 651
562 if (node != this.bottomFold_) 652 if (node != this.bottomFold_)
563 removeUntilNode(node, bottomFold); 653 removeUntilNode(node, bottomFold);
564 }; 654 };
565 655
566 /** 656 /**
567 * Empty out both select bags and remove them from the document. 657 * Empty out both select bags and remove them from the document.
568 * 658 *
569 * These nodes hold the text between the start and end of the selection 659 * 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 660 * when that text is otherwise off screen. They are filled out in the
571 * onCopy_ event. 661 * onCopy_ event.
572 */ 662 */
573 ScrollPort.prototype.resetSelectBags_ = function() { 663 hterm.ScrollPort.prototype.resetSelectBags_ = function() {
574 if (this.topSelectBag_.parentNode) { 664 if (this.topSelectBag_.parentNode) {
575 this.topSelectBag_.textContent = ''; 665 this.topSelectBag_.textContent = '';
576 this.topSelectBag_.parentNode.removeChild(this.topSelectBag_); 666 this.topSelectBag_.parentNode.removeChild(this.topSelectBag_);
577 } 667 }
578 668
579 if (this.bottomSelectBag_.parentNode) { 669 if (this.bottomSelectBag_.parentNode) {
580 this.bottomSelectBag_.textContent = ''; 670 this.bottomSelectBag_.textContent = '';
581 this.bottomSelectBag_.parentNode.removeChild(this.bottomSelectBag_); 671 this.bottomSelectBag_.parentNode.removeChild(this.bottomSelectBag_);
582 } 672 }
583 }; 673 };
584 674
585 /** 675 /**
586 * Set the top coordinate of the row nodes. 676 * Set the top coordinate of the row nodes.
587 * 677 *
588 * The rowNodes_ are a fixed position DOM element. When nodes are stashed 678 * 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_ 679 * 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 680 * so that the first node *after* the top fold is always the first visible
591 * DOM node. 681 * DOM node.
592 */ 682 */
593 ScrollPort.prototype.syncRowNodesTop_ = function() { 683 hterm.ScrollPort.prototype.syncRowNodesTop_ = function() {
594 var topMargin = 0; 684 var topMargin = 0;
595 var node = this.topFold_.previousSibling; 685 var node = this.topFold_.previousSibling;
596 while (node) { 686 while (node) {
597 topMargin += this.rowHeight_; 687 topMargin += this.rowHeight_;
598 node = node.previousSibling; 688 node = node.previousSibling;
599 } 689 }
600 690
601 this.rowNodes_.style.top = this.screen_.offsetTop - topMargin + 'px'; 691 this.rowNodes_.style.top = this.screen_.offsetTop - topMargin + 'px';
602 }; 692 };
603 693
604 /** 694 /**
605 * Place a row node in the cache of visible nodes. 695 * Place a row node in the cache of visible nodes.
606 * 696 *
607 * This method may only be used during a redraw_. 697 * This method may only be used during a redraw_.
608 */ 698 */
609 ScrollPort.prototype.cacheRowNode_ = function(rowNode) { 699 hterm.ScrollPort.prototype.cacheRowNode_ = function(rowNode) {
610 this.currentRowNodeCache_[rowNode.rowIndex] = rowNode; 700 this.currentRowNodeCache_[rowNode.rowIndex] = rowNode;
611 }; 701 };
612 702
613 /** 703 /**
614 * Fetch the row node for the given index. 704 * Fetch the row node for the given index.
615 * 705 *
616 * This will return a node from the cache if possible, or will request one 706 * This will return a node from the cache if possible, or will request one
617 * from the RowProvider if not. 707 * from the RowProvider if not.
618 * 708 *
619 * If a redraw_ is in progress the row will be added to the current cache. 709 * If a redraw_ is in progress the row will be added to the current cache.
620 */ 710 */
621 ScrollPort.prototype.fetchRowNode_ = function(rowIndex) { 711 hterm.ScrollPort.prototype.fetchRowNode_ = function(rowIndex) {
712 return this.rowProvider_.getRowNode(rowIndex);
Vladislav Kaznacheev 2011/11/28 14:00:07 Dead code below, debugging leftovers?
rginda 2011/11/28 20:39:47 Oops. This early return was me defeating the row
713
622 var node; 714 var node;
623 715
624 if (this.previousRowNodeCache_ && rowIndex in this.previousRowNodeCache_) { 716 if (this.previousRowNodeCache_ && rowIndex in this.previousRowNodeCache_) {
625 node = this.previousRowNodeCache_[rowIndex]; 717 node = this.previousRowNodeCache_[rowIndex];
626 } else { 718 } else {
627 node = this.rowProvider_.getRowNode(rowIndex); 719 node = this.rowProvider_.getRowNode(rowIndex);
628 } 720 }
629 721
630 if (this.currentRowNodeCache_) 722 if (this.currentRowNodeCache_)
631 this.cacheRowNode_(node); 723 this.cacheRowNode_(node);
632 724
633 return node; 725 return node;
634 }; 726 };
635 727
636 /** 728 /**
637 * Select all rows in the viewport. 729 * Select all rows in the viewport.
638 */ 730 */
639 ScrollPort.prototype.selectAll = function() { 731 hterm.ScrollPort.prototype.selectAll = function() {
640 var firstRow; 732 var firstRow;
641 733
642 if (this.topFold_.nextSibling.rowIndex != 0) { 734 if (this.topFold_.nextSibling.rowIndex != 0) {
643 while (this.topFold_.previousSibling) { 735 while (this.topFold_.previousSibling) {
644 this.rowNodes_.removeChild(this.topFold_.previousSibling); 736 this.rowNodes_.removeChild(this.topFold_.previousSibling);
645 } 737 }
646 738
647 firstRow = this.fetchRowNode_(0); 739 firstRow = this.fetchRowNode_(0);
648 this.rowNodes_.insertBefore(firstRow, this.topFold_); 740 this.rowNodes_.insertBefore(firstRow, this.topFold_);
649 this.syncRowNodesTop_(); 741 this.syncRowNodesTop_();
(...skipping 18 matching lines...) Expand all
668 var selection = this.document_.getSelection(); 760 var selection = this.document_.getSelection();
669 selection.collapse(firstRow, 0); 761 selection.collapse(firstRow, 0);
670 selection.extend(lastRow, lastRow.childNodes.length); 762 selection.extend(lastRow, lastRow.childNodes.length);
671 763
672 this.selection_.sync(); 764 this.selection_.sync();
673 }; 765 };
674 766
675 /** 767 /**
676 * Return the maximum scroll position in pixels. 768 * Return the maximum scroll position in pixels.
677 */ 769 */
678 ScrollPort.prototype.getScrollMax_ = function(e) { 770 hterm.ScrollPort.prototype.getScrollMax_ = function(e) {
679 return (this.scrollArea_.clientHeight + this.visibleRowTopMargin - 771 return (this.scrollArea_.clientHeight + this.visibleRowTopMargin -
680 this.screen_.clientHeight); 772 this.screen_.clientHeight);
681 }; 773 };
682 774
683 /** 775 /**
684 * Scroll the given rowIndex to the top of the ScrollPort. 776 * Scroll the given rowIndex to the top of the hterm.ScrollPort.
685 * 777 *
686 * @param {integer} rowIndex Index of the target row. 778 * @param {integer} rowIndex Index of the target row.
687 */ 779 */
688 ScrollPort.prototype.scrollRowToTop = function(rowIndex) { 780 hterm.ScrollPort.prototype.scrollRowToTop = function(rowIndex) {
781 this.syncScrollHeight();
782
689 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin; 783 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
690 784
691 var scrollMax = this.getScrollMax_(); 785 var scrollMax = this.getScrollMax_();
692 if (scrollTop > scrollMax) 786 if (scrollTop > scrollMax)
693 scrollTop = scrollMax; 787 scrollTop = scrollMax;
694 788
695 this.screen_.scrollTop = scrollTop; 789 this.screen_.scrollTop = scrollTop;
696 this.redraw_(); 790 this.redraw_();
697 }; 791 };
698 792
699 /** 793 /**
700 * Scroll the given rowIndex to the bottom of the ScrollPort. 794 * Scroll the given rowIndex to the bottom of the hterm.ScrollPort.
701 * 795 *
702 * @param {integer} rowIndex Index of the target row. 796 * @param {integer} rowIndex Index of the target row.
703 */ 797 */
704 ScrollPort.prototype.scrollRowToBottom = function(rowIndex) { 798 hterm.ScrollPort.prototype.scrollRowToBottom = function(rowIndex) {
799 this.syncScrollHeight();
800
705 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin; 801 var scrollTop = rowIndex * this.rowHeight_ + this.visibleRowTopMargin;
706 scrollTop -= (this.visibleRowCount - 1) * this.rowHeight_; 802 scrollTop -= (this.visibleRowCount - 1) * this.rowHeight_;
707 803
708 if (scrollTop < 0) 804 if (scrollTop < 0)
709 scrollTop = 0; 805 scrollTop = 0;
710 806
711 this.screen_.scrollTop = scrollTop; 807 this.screen_.scrollTop = scrollTop;
712 this.redraw_(); 808 this.redraw_();
713 }; 809 };
714 810
715 /** 811 /**
716 * Return the row index of the first visible row. 812 * Return the row index of the first visible row.
717 * 813 *
718 * This is based on the scroll position. If a redraw_ is in progress this 814 * This is based on the scroll position. If a redraw_ is in progress this
719 * returns the row that *should* be at the top. 815 * returns the row that *should* be at the top.
720 */ 816 */
721 ScrollPort.prototype.getTopRowIndex = function() { 817 hterm.ScrollPort.prototype.getTopRowIndex = function() {
722 return Math.floor(this.screen_.scrollTop / this.rowHeight_); 818 return Math.floor(this.screen_.scrollTop / this.rowHeight_);
723 }; 819 };
724 820
725 /** 821 /**
726 * Return the row index of the last visible row. 822 * Return the row index of the last visible row.
727 * 823 *
728 * This is based on the scroll position. If a redraw_ is in progress this 824 * This is based on the scroll position. If a redraw_ is in progress this
729 * returns the row that *should* be at the bottom. 825 * returns the row that *should* be at the bottom.
730 */ 826 */
731 ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) { 827 hterm.ScrollPort.prototype.getBottomRowIndex = function(topRowIndex) {
732 return topRowIndex + this.visibleRowCount - 1; 828 return topRowIndex + this.visibleRowCount - 1;
733 }; 829 };
734 830
735 /** 831 /**
736 * Handler for scroll events. 832 * Handler for scroll events.
737 * 833 *
738 * The onScroll event fires when the user moves the scrollbar associated with 834 * The onScroll event fires when scrollArea's scrollTop property changes. This
739 * this ScrollPort. 835 * may be due to the user manually move the scrollbar, or a programmatic change.
740 */ 836 */
741 ScrollPort.prototype.onScroll_ = function(e) { 837 hterm.ScrollPort.prototype.onScroll_ = function(e) {
742 this.redraw_(); 838 this.redraw_();
839 this.publish('scroll', { scrollPort: this });
743 }; 840 };
744 841
745 /** 842 /**
746 * Handler for scroll-wheel events. 843 * Handler for scroll-wheel events.
747 * 844 *
748 * The onScrollWheel event fires when the user moves their scrollwheel over this 845 * The onScrollWheel event fires when the user moves their scrollwheel over this
749 * ScrollPort. Because the frontmost element in the ScrollPort is a fixed 846 * hterm.ScrollPort. Because the frontmost element in the hterm.ScrollPort is
750 * position DIV, the scroll wheel does nothing by default. Instead, we have 847 * a fixed position DIV, the scroll wheel does nothing by default. Instead, we
751 * to handle it manually. 848 * have to handle it manually.
752 */ 849 */
753 ScrollPort.prototype.onScrollWheel_ = function(e) { 850 hterm.ScrollPort.prototype.onScrollWheel_ = function(e) {
754 var top = this.screen_.scrollTop - e.wheelDeltaY; 851 var top = this.screen_.scrollTop - e.wheelDeltaY;
755 if (top < 0) 852 if (top < 0)
756 top = 0; 853 top = 0;
757 854
758 var scrollMax = this.getScrollMax_(); 855 var scrollMax = this.getScrollMax_();
759 if (top > scrollMax) 856 if (top > scrollMax)
760 top = scrollMax; 857 top = scrollMax;
761 858
762 // Moving scrollTop causes a scroll event, which triggers the redraw. 859 // Moving scrollTop causes a scroll event, which triggers the redraw.
763 this.screen_.scrollTop = top; 860 this.screen_.scrollTop = top;
764 }; 861 };
765 862
766 /** 863 /**
767 * Handler for resize events. 864 * Handler for resize events.
768 * 865 *
769 * The browser will resize us such that the top row stays at the top, but we 866 * 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. 867 * prefer to the bottom row to stay at the bottom.
771 */ 868 */
772 ScrollPort.prototype.onResize = function(e) { 869 hterm.ScrollPort.prototype.onResize = function(e) {
773 var index = this.bottomFold_.previousSibling.rowIndex;
774 this.resize(); 870 this.resize();
775 this.scrollRowToBottom(index);
776 }; 871 };
777 872
778 /** 873 /**
779 * Handler for copy-to-clipboard events. 874 * Handler for copy-to-clipboard events.
780 * 875 *
781 * If some or all of the selected rows are off screen we may need to fill in 876 * 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 877 * 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 878 * 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. 879 * of the "select bags" with the missing text.
785 */ 880 */
786 ScrollPort.prototype.onCopy_ = function(e) { 881 hterm.ScrollPort.prototype.onCopy_ = function(e) {
787 this.resetSelectBags_(); 882 this.resetSelectBags_();
788 this.selection_.sync(); 883 this.selection_.sync();
789 884
790 if (!this.selection_.startRow || 885 if (!this.selection_.startRow ||
791 this.selection_.endRow.rowIndex - this.selection_.startRow.rowIndex < 2) { 886 this.selection_.endRow.rowIndex - this.selection_.startRow.rowIndex < 2) {
792 return; 887 return;
793 } 888 }
794 889
795 var topRowIndex = this.getTopRowIndex(); 890 var topRowIndex = this.getTopRowIndex();
796 var bottomRowIndex = this.getBottomRowIndex(topRowIndex); 891 var bottomRowIndex = this.getBottomRowIndex(topRowIndex);
(...skipping 27 matching lines...) Expand all
824 } else { 919 } else {
825 // Selection starts above the bottom fold. 920 // Selection starts above the bottom fold.
826 startBackfillIndex = this.bottomFold_.previousSibling.rowIndex + 1; 921 startBackfillIndex = this.bottomFold_.previousSibling.rowIndex + 1;
827 } 922 }
828 923
829 this.bottomSelectBag_.textContent = this.rowProvider_.getRowsText( 924 this.bottomSelectBag_.textContent = this.rowProvider_.getRowsText(
830 startBackfillIndex, this.selection_.endRow.rowIndex); 925 startBackfillIndex, this.selection_.endRow.rowIndex);
831 this.rowNodes_.insertBefore(this.bottomSelectBag_, this.selection_.endRow); 926 this.rowNodes_.insertBefore(this.bottomSelectBag_, this.selection_.endRow);
832 } 927 }
833 }; 928 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698