|
1 | 1 | 'use strict'; |
2 | 2 |
|
3 | | -function generateContent(lines, num) { |
4 | | - return lines.slice(-num).join('\n'); |
| 3 | +var _ = require('lodash'); |
| 4 | + |
| 5 | +function generateContent(lines, start, minLength) { |
| 6 | + return _(lines) |
| 7 | + .slice(start) |
| 8 | + .thru(function(array){ |
| 9 | + if(array.length < minLength){ |
| 10 | + // pad whitespace at top of array |
| 11 | + return _(new Array(minLength - array.length)) |
| 12 | + .fill('\u2009') |
| 13 | + .concat(array) |
| 14 | + .value(); |
| 15 | + }else{ |
| 16 | + return array; |
| 17 | + } |
| 18 | + }) |
| 19 | + .map(function(line){ |
| 20 | + if(line.length === 0){ |
| 21 | + // insert a blank space to prevent pre omitting a trailing newline, |
| 22 | + // even though pre/pre-nowrap/pre-line are specified. |
| 23 | + return '\u2009'; |
| 24 | + } |
| 25 | + return line; |
| 26 | + }) |
| 27 | + .join('\n'); |
5 | 28 | } |
6 | 29 |
|
7 | 30 | var Scroller = function() { |
8 | 31 | this.lines = []; |
9 | 32 | this.minVisible = 30; |
10 | | - this.visibleCount = this.minVisible; |
| 33 | + this.startPosition = 0; |
| 34 | + this.animateRequest = null; |
11 | 35 | this.sticky = true; |
| 36 | + this.jumpToBottom = true; |
12 | 37 | this.dirty = false; |
13 | 38 | this.console = null; |
14 | 39 | this.refresh = this.renderVisible.bind(this); |
15 | 40 | this.scroll = this.onScroll.bind(this); |
| 41 | + this.expand = _.throttle(this._expand.bind(this), 100, { |
| 42 | + leading: true, |
| 43 | + trailing: false |
| 44 | + }); |
16 | 45 | }; |
17 | 46 |
|
18 | | -Scroller.prototype.setLines = function(lines) { |
| 47 | +Scroller.prototype.setLines = function(newLines) { |
| 48 | + var len = newLines.length; |
19 | 49 | if(this.sticky){ |
20 | | - this.visibleCount = this.minVisible; |
21 | | - }else{ |
22 | | - //keep sticky position within view |
23 | | - this.visibleCount += Math.max(0, lines.length - this.lines.length); |
| 50 | + this.startPosition = Math.max(0, len - this.minVisible); |
24 | 51 | } |
25 | | - this.lines = lines; |
| 52 | + this.lines = newLines; |
| 53 | + this.dirty = true; |
| 54 | +}; |
| 55 | + |
| 56 | +Scroller.prototype.reset = function(clearLines){ |
| 57 | + this.visibleCount = this.minVisible; |
| 58 | + this.sticky = true; |
26 | 59 | this.dirty = true; |
| 60 | + if(clearLines){ |
| 61 | + this.lines = []; |
| 62 | + this.startPosition = 0; |
| 63 | + } |
| 64 | + if(this.console){ |
| 65 | + this.animateRequest = requestAnimationFrame(this.refresh); |
| 66 | + } |
| 67 | +}; |
| 68 | + |
| 69 | +Scroller.prototype.requestRefresh = function(){ |
| 70 | + if(this.console){ |
| 71 | + this.animateRequest = requestAnimationFrame(this.refresh); |
| 72 | + } |
27 | 73 | }; |
28 | 74 |
|
29 | 75 | Scroller.prototype.renderVisible = function(){ |
| 76 | + this.animateRequest = null; |
30 | 77 | if(this.dirty && this.console){ |
31 | | - this.console.innerHTML = generateContent(this.lines, this.visibleCount); |
32 | 78 | if(this.sticky){ |
| 79 | + this.startPosition = Math.max(0, this.lines.length - this.minVisible); |
| 80 | + } |
| 81 | + this.console.innerHTML = generateContent(this.lines, this.startPosition, this.minVisible); |
| 82 | + if(this.jumpToBottom){ |
33 | 83 | this.console.scrollTop = 350000; |
| 84 | + this.jumpToBottom = false; |
34 | 85 | } |
35 | 86 | this.dirty = false; |
36 | 87 | } |
37 | 88 | }; |
38 | 89 |
|
| 90 | +Scroller.prototype._expand = function(){ |
| 91 | + this.startPosition = Math.max(0, this.startPosition - this.minVisible); |
| 92 | + this.sticky = false; |
| 93 | + if(this.console){ |
| 94 | + var scrollHeight = this.console.scrollHeight; |
| 95 | + var scrollTop = this.console.scrollTop; |
| 96 | + |
| 97 | + // do an inline scroll to avoid potential scroll interleaving |
| 98 | + this.console.innerHTML = generateContent(this.lines, this.startPosition, this.minVisible); |
| 99 | + var newScrollHeight = this.console.scrollHeight; |
| 100 | + this.console.scrollTop = scrollTop + newScrollHeight - scrollHeight; |
| 101 | + |
| 102 | + this.dirty = false; |
| 103 | + } |
| 104 | + if(!this.animateRequest){ |
| 105 | + this.animateRequest = requestAnimationFrame(this.refresh); |
| 106 | + } |
| 107 | +}; |
| 108 | + |
39 | 109 | Scroller.prototype.onScroll = function(){ |
40 | 110 | var height = this.console.offsetHeight; |
41 | 111 | var scrollHeight = this.console.scrollHeight; |
42 | 112 | var scrollTop = this.console.scrollTop; |
43 | | - if(scrollTop < 30 && this.visibleCount < this.lines.length){ |
44 | | - this.visibleCount += this.minVisible; |
45 | | - this.sticky = false; |
46 | | - this.dirty = true; |
47 | | - }else if(scrollTop + height > scrollHeight - 30){ |
| 113 | + if(scrollTop < 15 && this.startPosition > 0){ |
| 114 | + this.expand(); |
| 115 | + }else if(scrollTop + height > scrollHeight - 15){ |
48 | 116 | if(!this.sticky){ |
| 117 | + this.jumpToBottom = true; |
49 | 118 | this.sticky = true; |
50 | 119 | this.dirty = true; |
51 | 120 | } |
52 | | - }else{ |
53 | | - this.sicky = false; |
54 | 121 | } |
55 | 122 |
|
56 | 123 | if(this.dirty){ |
57 | | - requestAnimationFrame(this.refresh); |
| 124 | + this.animateRequest = requestAnimationFrame(this.refresh); |
58 | 125 | } |
59 | 126 | }; |
60 | 127 |
|
|
0 commit comments