From 2eae240842921f6277e2be03378b58d597b94edb Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Fri, 10 Apr 2015 16:34:20 +0200 Subject: [PATCH] Lot of native scroll fix + ff copy/paste --- butterfly/sass/_layout.sass | 2 +- butterfly/static/ext.js | 12 +-- butterfly/static/main.css | 2 +- butterfly/static/main.js | 173 +++++++++++++++++++---------------- coffees/ext/alarm.coffee | 2 +- coffees/ext/clipboard.coffee | 4 +- coffees/ext/selection.coffee | 6 +- coffees/main.coffee | 27 +++--- coffees/term.coffee | 123 +++++++++++++++---------- 9 files changed, 196 insertions(+), 155 deletions(-) diff --git a/butterfly/sass/_layout.sass b/butterfly/sass/_layout.sass index 9f2f447..23b55cc 100644 --- a/butterfly/sass/_layout.sass +++ b/butterfly/sass/_layout.sass @@ -28,7 +28,7 @@ html, body body[data-native-scroll="yes"] #wrapper - overflow-y: auto + overflow-y: scroll ::-webkit-scrollbar background: $bg diff --git a/butterfly/static/ext.js b/butterfly/static/ext.js index 88dc5c3..2fc81a3 100644 --- a/butterfly/static/ext.js +++ b/butterfly/static/ext.js @@ -33,7 +33,7 @@ return false; }; - document.addEventListener('keydown', function(e) { + addEventListener('keydown', function(e) { if (!(e.altKey && e.keyCode === 65)) { return true; } @@ -47,7 +47,7 @@ return cancel(e); }); - document.addEventListener('copy', copy = function(e) { + addEventListener('copy', copy = function(e) { var data, end, j, len1, line, ref, sel; butterfly.bell("copied"); e.clipboardData.clearData(); @@ -68,7 +68,7 @@ return e.preventDefault(); }); - document.addEventListener('paste', function(e) { + addEventListener('paste', function(e) { var data; butterfly.bell("pasted"); data = e.clipboardData.getData('text/plain'); @@ -301,7 +301,7 @@ })(); - document.addEventListener('keydown', function(e) { + addEventListener('keydown', function(e) { var ref, ref1; if (ref = e.keyCode, indexOf.call([16, 17, 18, 19], ref) >= 0) { return true; @@ -347,7 +347,7 @@ return true; }); - document.addEventListener('keyup', function(e) { + addEventListener('keyup', function(e) { var ref, ref1; if (ref = e.keyCode, indexOf.call([16, 17, 18, 19], ref) >= 0) { return true; @@ -368,7 +368,7 @@ return true; }); - document.addEventListener('dblclick', function(e) { + addEventListener('dblclick', function(e) { var anchorNode, anchorOffset, new_range, range, sel; if (e.ctrlKey || e.altkey) { return; diff --git a/butterfly/static/main.css b/butterfly/static/main.css index cee176d..3b119a9 100644 --- a/butterfly/static/main.css +++ b/butterfly/static/main.css @@ -3029,7 +3029,7 @@ html, body { white-space: nowrap; } body[data-native-scroll="yes"] #wrapper { - overflow-y: auto; } + overflow-y: scroll; } body[data-native-scroll="yes"] ::-webkit-scrollbar { background: #110f13; width: .75em; diff --git a/butterfly/static/main.js b/butterfly/static/main.js index 05d2840..ade3b8c 100644 --- a/butterfly/static/main.js +++ b/butterfly/static/main.js @@ -12,7 +12,7 @@ $ = document.querySelectorAll.bind(document); document.addEventListener('DOMContentLoaded', function() { - var bench, cbench, ctl, last_data, send, t_stop, term, ws, ws_url; + var ctl, last_data, send, t_stop, term, ws, ws_url; send = function(data) { return ws.send('S' + data); }; @@ -42,7 +42,6 @@ t_stop = null; last_data = ''; ws.addEventListener('message', function(e) { - var t; if (term.stop) { last_data += e.data; last_data = last_data.slice(-10 * 1024); @@ -60,9 +59,7 @@ }, 100); return; } - return t = setTimeout(function() { - return term.write(e.data); - }, 1); + return term.write(e.data); }); ws.addEventListener('close', function() { console.log("WebSocket closed", arguments); @@ -82,8 +79,10 @@ return 'This will exit the terminal session'; } }); - bench = function(n) { - var rnd, t0; + term.ws = ws; + window.butterfly = term; + window.bench = function(n) { + var rnd; if (n == null) { n = 100000000; } @@ -91,12 +90,14 @@ while (rnd.length < n) { rnd += Math.random().toString(36).substring(2); } - t0 = (new Date()).getTime(); + console.time('bench'); + console.profile('bench'); term.write(rnd); - return console.log(n + " chars in " + ((new Date()).getTime() - t0) + " ms"); + console.profileEnd(); + return console.timeEnd('bench'); }; - cbench = function(n) { - var rnd, t0; + return window.cbench = function(n) { + var rnd; if (n == null) { n = 100000000; } @@ -105,12 +106,12 @@ rnd += "\x1b[" + (30 + parseInt(Math.random() * 20)) + "m"; rnd += Math.random().toString(36).substring(2); } - t0 = (new Date()).getTime(); + console.time('cbench'); + console.profile('cbench'); term.write(rnd); - return console.log(n + " chars + colors in " + ((new Date()).getTime() - t0) + " ms"); + console.profileEnd(); + return console.timeEnd('cbench'); }; - term.ws = ws; - return window.butterfly = term; }); cancel = function(ev) { @@ -138,7 +139,7 @@ Terminal = (function() { function Terminal(parent1, out1, ctl1) { - var div, i, px, term_size; + var div, i, px, term_height, term_width; this.parent = parent1; this.out = out1; this.ctl = ctl1 != null ? ctl1 : function() {}; @@ -161,10 +162,11 @@ if (!this.native_scroll) { div.style.height = this.char_size.height + 'px'; } - term_size = this.parent.getBoundingClientRect(); - this.cols = Math.floor(term_size.width / this.char_size.width); - this.rows = Math.floor(term_size.height / this.char_size.height); - px = term_size.height % this.char_size.height; + term_width = this.element.getBoundingClientRect().width; + term_height = this.parent.getBoundingClientRect().height; + this.cols = Math.floor(term_width / this.char_size.width); + this.rows = Math.floor(term_height / this.char_size.height); + px = term_height % this.char_size.height; this.element.style['padding-bottom'] = px + "px"; this.html = {}; i = this.rows - 1; @@ -178,7 +180,6 @@ this.children.push(div); } this.scrollback = 5000; - this.missing_lines = 0; this.visualBell = 100; this.convertEol = false; this.termName = 'xterm'; @@ -198,18 +199,9 @@ addEventListener('blur', this.blur.bind(this)); addEventListener('resize', this.resize.bind(this)); if (typeof InstallTrigger !== "undefined") { - this.element.contentEditable = 'true'; - this.element.addEventListener("mouseup", function() { - var sel; - sel = getSelection().getRangeAt(0); - if (sel.startOffset === sel.endOffset) { - return getSelection().removeAllRanges(); - } - }); - } - if (!this.native_scroll) { - this.initmouse(); + this.body.contentEditable = 'true'; } + this.initmouse(); setTimeout(this.resize.bind(this), 100); } @@ -222,10 +214,8 @@ this.queue = ''; this.ybase = 0; this.ydisp = 0; - if (!this.native_scroll) { - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - } + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; this.applicationKeypad = false; this.applicationCursor = false; this.originMode = false; @@ -401,7 +391,7 @@ el = "offsetParent" in el ? el.offsetParent : el.parentNode; } w = _this.element.clientWidth; - h = _this.element.clientHeight; + h = _this.parent.clientHeight; x = Math.ceil((x / w) * _this.cols); y = Math.ceil((y / h) * _this.rows); if (x < 0) { @@ -432,13 +422,6 @@ return; } sendButton(ev); - if (_this.vt200Mouse) { - sendButton({ - __proto__: ev, - type: "mouseup" - }); - return cancel(ev); - } sm = sendMove.bind(_this); if (_this.normalMouse) { addEventListener("mousemove", sm); @@ -475,23 +458,16 @@ }; Terminal.prototype.refresh = function(start, end) { - var attr, bg, ch, classes, data, fg, flags, html, i, j, k, l, line, m, o, out, parent, ref, ref1, ref2, ref3, ref4, row, x; + var attr, bg, ch, classes, data, fg, flags, html, i, j, k, l, line, m, out, parent, ref, ref1, ref2, ref3, row, x; + console.log("Refresh " + start + " -> " + end); if (!this.native_scroll && end - start >= this.rows / 3) { parent = this.element.parentNode; if (parent != null) { parent.removeChild(this.element); } } - if (this.native_scroll) { - if (this.missing_lines) { - for (i = k = 1, ref = this.missing_lines; 1 <= ref ? k <= ref : k >= ref; i = 1 <= ref ? ++k : --k) { - this.new_line(); - } - this.missing_lines = 0; - } - } end = Math.min(end, this.screen.length - 1); - for (j = m = ref1 = start, ref2 = end; ref1 <= ref2 ? m <= ref2 : m >= ref2; j = ref1 <= ref2 ? ++m : --m) { + for (j = k = ref = start, ref1 = end; ref <= ref1 ? k <= ref1 : k >= ref1; j = ref <= ref1 ? ++k : --k) { row = j + this.ydisp; line = this.screen[row]; out = ""; @@ -501,7 +477,7 @@ x = -Infinity; } attr = this.defAttr; - for (i = o = 0, ref3 = this.cols - 1; 0 <= ref3 ? o <= ref3 : o >= ref3; i = 0 <= ref3 ? ++o : --o) { + for (i = m = 0, ref2 = this.cols - 1; 0 <= ref2 ? m <= ref2 : m >= ref2; i = 0 <= ref2 ? ++m : --m) { data = line[i][0]; ch = line[i][1]; if (data !== attr) { @@ -582,9 +558,9 @@ parent.appendChild(this.element); } if (this.native_scroll) { - ref4 = this.html; - for (l in ref4) { - html = ref4[l]; + ref3 = this.html; + for (l in ref3) { + html = ref3[l]; this.element.insertBefore(html, this.children[l]); } this.html = {}; @@ -636,12 +612,18 @@ Terminal.prototype.scroll = function() { var row; if (this.native_scroll) { - this.screen.shift(); - this.screen.push(this.blank_line()); - this.refreshStart = Math.max(this.refreshStart - 1, 0); - this.missing_lines++; - if (this.missing_lines >= this.rows) { - return this.refresh(0, this.rows - 1); + if (this.scrollTop !== 0 || this.scrollBottom !== this.rows - 1) { + console.log('Non native scroll'); + this.screen.splice(this.scrollTop, 1); + this.screen.splice(this.scrollBottom, 0, this.blank_line()); + console.log(this.y); + this.y--; + return this.maxRange(); + } else { + this.screen.shift(); + this.screen.push(this.blank_line()); + this.refreshStart = Math.max(this.refreshStart - 1, 0); + return this.new_line(); } } else { if (++this.ybase === this.scrollback) { @@ -698,7 +680,7 @@ Terminal.prototype.next_line = function() { this.y++; - if (this.y >= (this.native_scroll ? this.rows : this.scrollBottom)) { + if (this.y >= this.scrollBottom) { this.y--; return this.scroll(); } @@ -717,7 +699,7 @@ i = 0; l = data.length; while (i < l) { - ch = data[i]; + ch = data.charAt(i); switch (this.state) { case State.normal: switch (ch) { @@ -762,9 +744,9 @@ this.x = 0; this.next_line(); } + this.updateRange(this.y); this.screen[this.y + this.ybase][this.x] = [this.curAttr, ch]; this.x++; - this.updateRange(this.y); if (("\uff00" < ch && ch < "\uffef")) { if (this.cols < 2 || this.x >= this.cols) { this.screen[this.y + this.ybase][this.x - 1] = [this.curAttr, " "]; @@ -1388,7 +1370,7 @@ if (ev.keyCode >= 65 && ev.keyCode <= 90) { if (ev.keyCode === 67) { t = (new Date()).getTime(); - if ((t - this.last_cc) < 200 && !this.stop) { + if ((t - this.last_cc) < 500 && !this.stop) { id = setTimeout(function() {}); while (id--) { if (id !== this.t_bell && id !== this.t_queue && id !== this.t_blink) { @@ -1466,11 +1448,20 @@ }; Terminal.prototype.keyPress = function(ev) { - var key; + var key, ref; if (this.skipNextKey === false) { this.skipNextKey = null; return true; } + if (ev.keyCode > 15 && ev.keyCode < 19) { + return true; + } + if ((ev.shiftKey || ev.ctrlKey) && ev.keyCode === 45) { + return true; + } + if ((ev.shiftKey && ev.ctrlKey) && ((ref = ev.keyCode) === 67 || ref === 86)) { + return true; + } cancel(ev); if (ev.charCode) { key = ev.charCode; @@ -1518,14 +1509,16 @@ }; Terminal.prototype.resize = function() { - var ch, el, i, j, line, old_cols, old_rows, term_size; + var ch, el, i, j, line, old_cols, old_rows, px, term_height, term_width; old_cols = this.cols; old_rows = this.rows; this.compute_char_size(); - term_size = this.parent.getBoundingClientRect(); - this.cols = Math.floor(term_size.width / this.char_size.width); - this.rows = Math.floor(term_size.height / this.char_size.height); - this.element.style['padding-bottom'] = (term_size.height % this.char_size.height) + "px"; + term_width = this.element.getBoundingClientRect().width; + term_height = this.parent.getBoundingClientRect().height; + this.cols = Math.floor(term_width / this.char_size.width); + this.rows = Math.floor(term_height / this.char_size.height); + px = term_height % this.char_size.height; + this.element.style['padding-bottom'] = px + "px"; if (old_cols === this.cols && old_rows === this.rows) { return; } @@ -1583,10 +1576,8 @@ if (this.x >= this.cols) { this.x = this.cols - 1; } - if (!this.native_scroll) { - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - } + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; this.refresh(0, this.rows - 1); return this.normal = null; }; @@ -1727,9 +1718,29 @@ }; Terminal.prototype.reverseIndex = function() { - var j; - console.log('TODO: Reverse index'); - if (!this.native_scroll) { + var j, prevNode; + if (this.native_scroll) { + if (this.scrollTop !== 0 || this.scrollBottom !== this.rows - 1) { + console.log('Non native scroll'); + this.screen.splice(this.scrollBottom, 1); + this.screen.splice(this.scrollTop, 0, this.blank_line(true)); + console.log(this.y); + this.y--; + this.maxRange(); + } else { + prevNode = this.children[0].previousElementSibling; + if (prevNode) { + this.children.slice(-1)[0].remove(); + this.children.pop(); + this.children.unshift(this.children[0].previousElementSibling); + } else { + this.new_line(); + } + this.screen.pop(); + this.screen.unshift(this.blank_line()); + } + this.maxRange(); + } else { this.y--; if (this.y < this.scrollTop) { this.y++; diff --git a/coffees/ext/alarm.coffee b/coffees/ext/alarm.coffee index 71f4801..c2757c1 100644 --- a/coffees/ext/alarm.coffee +++ b/coffees/ext/alarm.coffee @@ -24,7 +24,7 @@ cancel = (ev) -> false -document.addEventListener 'keydown', (e) -> +addEventListener 'keydown', (e) -> return true unless e.altKey and e.keyCode is 65 if Notification and Notification.permission is 'default' diff --git a/coffees/ext/clipboard.coffee b/coffees/ext/clipboard.coffee index f56d33c..b982b6b 100644 --- a/coffees/ext/clipboard.coffee +++ b/coffees/ext/clipboard.coffee @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -document.addEventListener 'copy', copy = (e) -> +addEventListener 'copy', copy = (e) -> butterfly.bell "copied" e.clipboardData.clearData() sel = getSelection().toString().replace( @@ -33,7 +33,7 @@ document.addEventListener 'copy', copy = (e) -> e.clipboardData.setData 'text/plain', data.slice(0, -1) e.preventDefault() -document.addEventListener 'paste', (e) -> +addEventListener 'paste', (e) -> butterfly.bell "pasted" data = e.clipboardData.getData 'text/plain' data = data.replace(/\r\n/g, '\n').replace(/\n/g, '\r') diff --git a/coffees/ext/selection.coffee b/coffees/ext/selection.coffee index ed48159..4fb6bd6 100644 --- a/coffees/ext/selection.coffee +++ b/coffees/ext/selection.coffee @@ -169,7 +169,7 @@ class Selection return needle -document.addEventListener 'keydown', (e) -> +addEventListener 'keydown', (e) -> return true if e.keyCode in [16..19] # Paste natural selection too if shiftkey @@ -210,7 +210,7 @@ document.addEventListener 'keydown', (e) -> return cancel e true -document.addEventListener 'keyup', (e) -> +addEventListener 'keyup', (e) -> return true if e.keyCode in [16..19] if selection @@ -225,7 +225,7 @@ document.addEventListener 'keyup', (e) -> return true true -document.addEventListener 'dblclick', (e) -> +addEventListener 'dblclick', (e) -> return if e.ctrlKey or e.altkey sel = getSelection() return if sel.isCollapsed or sel.toString().match /\s/ diff --git a/coffees/main.coffee b/coffees/main.coffee index 04491f5..a191a74 100644 --- a/coffees/main.coffee +++ b/coffees/main.coffee @@ -64,9 +64,7 @@ document.addEventListener 'DOMContentLoaded', -> , 100 return - t = setTimeout -> - term.write e.data - , 1 + term.write e.data ws.addEventListener 'close', -> console.log "WebSocket closed", arguments @@ -86,25 +84,30 @@ document.addEventListener 'DOMContentLoaded', -> if not quit 'This will exit the terminal session' - bench = (n=100000000) -> + term.ws = ws + window.butterfly = term + + + window.bench = (n=100000000) -> rnd = '' while rnd.length < n rnd += Math.random().toString(36).substring(2) - t0 = (new Date()).getTime() + console.time('bench') + console.profile('bench') term.write rnd - console.log "#{n} chars in #{(new Date()).getTime() - t0} ms" + console.profileEnd() + console.timeEnd('bench') - cbench = (n=100000000) -> + window.cbench = (n=100000000) -> rnd = '' while rnd.length < n rnd += "\x1b[#{30 + parseInt(Math.random() * 20)}m" rnd += Math.random().toString(36).substring(2) - t0 = (new Date()).getTime() + console.time('cbench') + console.profile('cbench') term.write rnd - console.log "#{n} chars + colors in #{(new Date()).getTime() - t0} ms" - - term.ws = ws - window.butterfly = term + console.profileEnd() + console.timeEnd('cbench') diff --git a/coffees/term.coffee b/coffees/term.coffee index 31bc321..72bdb54 100644 --- a/coffees/term.coffee +++ b/coffees/term.coffee @@ -72,10 +72,11 @@ class Terminal @compute_char_size() div.style.height = @char_size.height + 'px' unless @native_scroll - term_size = @parent.getBoundingClientRect() - @cols = Math.floor(term_size.width / @char_size.width) - @rows = Math.floor(term_size.height / @char_size.height) - px = term_size.height % @char_size.height + term_width = @element.getBoundingClientRect().width + term_height = @parent.getBoundingClientRect().height + @cols = Math.floor(term_width / @char_size.width) + @rows = Math.floor(term_height / @char_size.height) + px = term_height % @char_size.height @element.style['padding-bottom'] = "#{px}px" @html = {} @@ -88,7 +89,6 @@ class Terminal @children.push(div) @scrollback = 5000 - @missing_lines = 0 @visualBell = 100 @convertEol = false @@ -110,15 +110,11 @@ class Terminal addEventListener 'blur', @blur.bind(@) addEventListener 'resize', @resize.bind(@) - # Horrible Firefox paste workaround + # # Horrible Firefox paste workaround if typeof InstallTrigger isnt "undefined" - @element.contentEditable = 'true' - @element.addEventListener "mouseup", -> - sel = getSelection().getRangeAt(0) - if sel.startOffset is sel.endOffset - getSelection().removeAllRanges() + @body.contentEditable = 'true' - @initmouse() unless @native_scroll + @initmouse() setTimeout(@resize.bind(@), 100) @@ -131,9 +127,8 @@ class Terminal @ybase = 0 @ydisp = 0 - unless @native_scroll - @scrollTop = 0 - @scrollBottom = @rows - 1 + @scrollTop = 0 + @scrollBottom = @rows - 1 # modes @applicationKeypad = false @@ -327,7 +322,7 @@ class Terminal # convert to cols/rows w = @element.clientWidth - h = @element.clientHeight + h = @parent.clientHeight x = Math.ceil((x / w) * @cols) y = Math.ceil((y / h) * @rows) @@ -355,12 +350,12 @@ class Terminal # fix for odd bug #if (@vt200Mouse && !@normalMouse) { - if @vt200Mouse - sendButton - __proto__: ev - type: "mouseup" + # if @vt200Mouse + # sendButton + # __proto__: ev + # type: "mouseup" - return cancel(ev) + # return cancel(ev) sm = sendMove.bind(this) addEventListener "mousemove", sm if @normalMouse @@ -385,16 +380,11 @@ class Terminal refresh: (start, end) -> + console.log "Refresh #{start} -> #{end}" if not @native_scroll and end - start >= @rows / 3 parent = @element.parentNode parent?.removeChild @element - if @native_scroll - if @missing_lines - for i in [1..@missing_lines] - @new_line() - @missing_lines = 0 - end = Math.min(end, @screen.length - 1) for j in [start..end] @@ -466,7 +456,6 @@ class Terminal attr = data out += "" if attr isnt @defAttr @children[j].innerHTML = out - parent?.appendChild @element if @native_scroll @@ -506,12 +495,20 @@ class Terminal scroll: -> if @native_scroll - @screen.shift() - @screen.push @blank_line() - @refreshStart = Math.max(@refreshStart - 1, 0) - @missing_lines++ - if @missing_lines >= @rows - @refresh 0, @rows - 1 + + if @scrollTop isnt 0 or @scrollBottom isnt @rows - 1 + # inner scroll + console.log('Non native scroll') + @screen.splice @scrollTop, 1 + @screen.splice @scrollBottom, 0, @blank_line() + console.log(@y) + @y-- + @maxRange() + else + @screen.shift() + @screen.push @blank_line() + @refreshStart = Math.max(@refreshStart - 1, 0) + @new_line() else if ++@ybase is @scrollback @ybase = @ybase / 2 | 0 @@ -567,7 +564,7 @@ class Terminal next_line: -> @y++ - if @y >= (if @native_scroll then @rows else @scrollBottom) + if @y >= @scrollBottom @y-- @scroll() @@ -583,7 +580,7 @@ class Terminal i = 0 l = data.length while i < l - ch = data[i] + ch = data.charAt(i) switch @state when State.normal switch ch @@ -630,9 +627,10 @@ class Terminal @x = 0 @next_line() + @updateRange @y + @screen[@y + @ybase][@x] = [@curAttr, ch] @x++ - @updateRange @y if "\uff00" < ch < "\uffef" if @cols < 2 or @x >= @cols @screen[@y + @ybase][@x - 1] = [@curAttr, " "] @@ -1343,7 +1341,7 @@ class Terminal if ev.keyCode >= 65 and ev.keyCode <= 90 if ev.keyCode is 67 t = (new Date()).getTime() - if (t - @last_cc) < 200 and not @stop + if (t - @last_cc) < 500 and not @stop id = (setTimeout ->) (clearTimeout id if id not in [ @t_bell, @t_queue, @t_blink]) while id-- @@ -1421,6 +1419,16 @@ class Terminal @skipNextKey = null return true + # Don't handle modifiers alone + return true if ev.keyCode > 15 and ev.keyCode < 19 + + # Handle shift insert and ctrl insert + # copy/paste usefull for typematrix keyboard + return true if (ev.shiftKey or ev.ctrlKey) and ev.keyCode is 45 + + # Let the ctrl+shift+c, ctrl+shift+v go through to handle native copy paste + return true if (ev.shiftKey and ev.ctrlKey) and ev.keyCode in [67, 86] + cancel ev if ev.charCode @@ -1459,11 +1467,12 @@ class Terminal old_cols = @cols old_rows = @rows @compute_char_size() - term_size = @parent.getBoundingClientRect() - @cols = Math.floor(term_size.width / @char_size.width) - @rows = Math.floor(term_size.height / @char_size.height) - @element.style['padding-bottom'] = "#{term_size.height % - @char_size.height}px" + term_width = @element.getBoundingClientRect().width + term_height = @parent.getBoundingClientRect().height + @cols = Math.floor(term_width / @char_size.width) + @rows = Math.floor(term_height / @char_size.height) + px = term_height % @char_size.height + @element.style['padding-bottom'] = "#{px}px" if old_cols == @cols and old_rows == @rows return @@ -1508,9 +1517,8 @@ class Terminal @y = @rows - 1 if @y >= @rows @x = @cols - 1 if @x >= @cols - unless @native_scroll - @scrollTop = 0 - @scrollBottom = @rows - 1 + @scrollTop = 0 + @scrollBottom = @rows - 1 @refresh 0, @rows - 1 @@ -1606,8 +1614,27 @@ class Terminal # ESC M Reverse Index (RI is 0x8d). reverseIndex: -> - console.log 'TODO: Reverse index' - unless @native_scroll + if @native_scroll + if @scrollTop isnt 0 or @scrollBottom isnt @rows - 1 + # inner scroll + console.log('Non native scroll') + @screen.splice @scrollBottom, 1 + @screen.splice @scrollTop, 0, @blank_line(true) + console.log(@y) + @y-- + @maxRange() + else + prevNode = @children[0].previousElementSibling + if prevNode + @children.slice(-1)[0].remove() + @children.pop() + @children.unshift @children[0].previousElementSibling + else + @new_line() + @screen.pop() + @screen.unshift @blank_line() + @maxRange() + else @y-- if @y < @scrollTop @y++