Lot of native scroll fix + ff copy/paste

This commit is contained in:
Florian Mounier
2015-04-10 16:34:20 +02:00
parent 000754d5c8
commit 2eae240842
9 changed files with 196 additions and 155 deletions

View File

@@ -28,7 +28,7 @@ html, body
body[data-native-scroll="yes"]
#wrapper
overflow-y: auto
overflow-y: scroll
::-webkit-scrollbar
background: $bg

View File

@@ -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;

View File

@@ -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;

View File

@@ -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++;

View File

@@ -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'

View File

@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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')

View File

@@ -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/

View File

@@ -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')

View File

@@ -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 += "</span>" 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++