mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-06-07 04:49:40 +00:00
Fix init
This commit is contained in:
@@ -151,10 +151,6 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
self.log.debug('Adding handler')
|
||||
fcntl.fcntl(self.fd, fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
|
||||
# Set the size of the terminal window:
|
||||
s = struct.pack("HHHH", 80, 80, 0, 0)
|
||||
fcntl.ioctl(self.fd, termios.TIOCSWINSZ, s)
|
||||
|
||||
def utf8_error(e):
|
||||
self.log.error(e)
|
||||
|
||||
@@ -202,16 +198,14 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
self.pty()
|
||||
|
||||
def on_message(self, message):
|
||||
if message.startswith('RS|'):
|
||||
message = message[3:]
|
||||
cols, rows = map(int, message.split(','))
|
||||
if message[0] == 'R':
|
||||
cols, rows = map(int, message[1:].split(','))
|
||||
s = struct.pack("HHHH", rows, cols, 0, 0)
|
||||
fcntl.ioctl(self.fd, termios.TIOCSWINSZ, s)
|
||||
self.log.info('SIZE (%d, %d)' % (cols, rows))
|
||||
elif message.startswith('SH|'):
|
||||
message = message[3:]
|
||||
elif message[0] == 'S':
|
||||
self.log.info('WRIT<%r' % message)
|
||||
self.writer.write(message)
|
||||
self.writer.write(message[1:])
|
||||
self.writer.flush()
|
||||
|
||||
def shell_handler(self, fd, events):
|
||||
|
||||
@@ -15,25 +15,26 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
term = ws = null
|
||||
cols = rows = null
|
||||
quit = false
|
||||
|
||||
|
||||
$ = document.querySelectorAll.bind(document)
|
||||
|
||||
send = (data) ->
|
||||
ws.send 'S' + data
|
||||
|
||||
ctl = (type, args...) ->
|
||||
params = args.join(',')
|
||||
if type == 'Resize'
|
||||
ws.send 'R' + params
|
||||
|
||||
ws_url = 'ws://' + document.location.host + '/ws' + location.pathname
|
||||
ws = new WebSocket ws_url
|
||||
term = new Terminal $('#wrapper')[0], send, ctl
|
||||
|
||||
ws.onopen = ->
|
||||
console.log "WebSocket open", arguments
|
||||
|
||||
term = new Terminal (data) -> ws.send 'SH|' + data
|
||||
|
||||
term.open $('main')[0]
|
||||
$('.terminal')[0].style = ''
|
||||
resize()
|
||||
|
||||
ws.send 'R' + term.cols + ',' + term.rows
|
||||
|
||||
ws.onerror = -> console.log "WebSocket error", arguments
|
||||
ws.onmessage = (e) ->
|
||||
@@ -51,32 +52,6 @@ addEventListener 'beforeunload', ->
|
||||
if not quit
|
||||
'This will exit the terminal session'
|
||||
|
||||
addEventListener 'resize', resize = ->
|
||||
main = $('main')[0]
|
||||
fake_term = document.createElement('div')
|
||||
fake_term.className = 'terminal test'
|
||||
fake_term_div = document.createElement('div')
|
||||
fake_term_line = document.createElement('span')
|
||||
fake_term_line.textContent = '0123456789'
|
||||
fake_term_div.appendChild(fake_term_line)
|
||||
fake_term.appendChild(fake_term_div)
|
||||
main.appendChild(fake_term)
|
||||
|
||||
ew = fake_term_line.getBoundingClientRect().width
|
||||
eh = fake_term_div.getBoundingClientRect().height
|
||||
main.removeChild(fake_term)
|
||||
|
||||
main_bb = main.getBoundingClientRect()
|
||||
cols = Math.floor(10 * main_bb.width / ew) - 1
|
||||
rows = Math.floor(main_bb.height / eh)
|
||||
|
||||
console.log "Computed #{cols} cols and #{rows} rows from ", main_bb, ew, eh
|
||||
term.resize cols, rows
|
||||
for div in $('.terminal div')
|
||||
div.style.height = eh + 'px'
|
||||
|
||||
ws.send "RS|#{cols},#{rows}"
|
||||
|
||||
bench = (n=100000000) ->
|
||||
rnd = ''
|
||||
while rnd.length < n
|
||||
|
||||
@@ -47,9 +47,40 @@ State =
|
||||
|
||||
|
||||
class Terminal
|
||||
constructor: (@out) ->
|
||||
@cols = 80
|
||||
@rows = 24
|
||||
constructor: (@parent, @out, @ctl=->) ->
|
||||
# Global elements
|
||||
@context = @parent.ownerDocument.defaultView
|
||||
@document = @parent.ownerDocument
|
||||
@body = @document.getElementsByTagName('body')[0]
|
||||
|
||||
# Main terminal element
|
||||
@element = @document.createElement('div')
|
||||
@element.className = 'terminal focus'
|
||||
@element.style.outline = 'none'
|
||||
@element.setAttribute 'tabindex', 0
|
||||
|
||||
@parent.appendChild(@element)
|
||||
|
||||
# Adding one line to compute char size
|
||||
div = @document.createElement('div')
|
||||
div.className = 'line'
|
||||
@element.appendChild(div)
|
||||
@children = [div]
|
||||
|
||||
@compute_char_size()
|
||||
div.style.height = @char_size.height + 'px'
|
||||
term_size = @parent.getBoundingClientRect()
|
||||
@cols = Math.floor(term_size.width / @char_size.width) - 1 # ?
|
||||
@rows = Math.floor(term_size.height / @char_size.height)
|
||||
|
||||
i = @rows - 1
|
||||
while i--
|
||||
div = @document.createElement('div')
|
||||
div.style.height = @char_size.height + 'px'
|
||||
div.className = 'line'
|
||||
@element.appendChild(div)
|
||||
@children.push(div)
|
||||
|
||||
@scrollback = 100000
|
||||
@visualBell = 100
|
||||
|
||||
@@ -58,9 +89,24 @@ class Terminal
|
||||
@cursorBlink = true
|
||||
@screenKeys = false
|
||||
@cursorState = 0
|
||||
@init()
|
||||
|
||||
init: ->
|
||||
@reset_vars()
|
||||
|
||||
# Draw screen
|
||||
@refresh 0, @rows - 1
|
||||
|
||||
@focus()
|
||||
|
||||
@startBlink()
|
||||
addEventListener 'keydown', @keyDown.bind(@)
|
||||
addEventListener 'keypress', @keyPress.bind(@)
|
||||
addEventListener 'focus', @focus.bind(@)
|
||||
addEventListener 'blur', @blur.bind(@)
|
||||
addEventListener 'paste', @paste.bind(@)
|
||||
addEventListener 'resize', @resize.bind(@)
|
||||
@initmouse()
|
||||
|
||||
reset_vars: ->
|
||||
@ybase = 0
|
||||
@ydisp = 0
|
||||
@x = 0
|
||||
@@ -96,6 +142,15 @@ class Terminal
|
||||
@setupStops()
|
||||
@skipNextKey = false
|
||||
|
||||
compute_char_size: ->
|
||||
test_span = document.createElement('span')
|
||||
test_span.textContent = '0123456789'
|
||||
@children[0].appendChild(test_span)
|
||||
@char_size =
|
||||
width: test_span.getBoundingClientRect().width / 10
|
||||
height: @children[0].getBoundingClientRect().height
|
||||
@children[0].removeChild(test_span)
|
||||
|
||||
eraseAttr: ->
|
||||
(@defAttr & ~0x1ff) | (@curAttr & 0x1ff)
|
||||
|
||||
@@ -119,44 +174,6 @@ class Terminal
|
||||
@send @context.clipboardData.getData('Text')
|
||||
cancel(ev)
|
||||
|
||||
open: (parent) ->
|
||||
@parent = parent or @parent
|
||||
throw new Error('Terminal requires a parent element') unless @parent
|
||||
|
||||
# Global elements
|
||||
@context = @parent.ownerDocument.defaultView
|
||||
@document = @parent.ownerDocument
|
||||
@body = @document.getElementsByTagName('body')[0]
|
||||
|
||||
# Main terminal element
|
||||
@element = @document.createElement('div')
|
||||
@element.className = 'terminal focus'
|
||||
@element.style.outline = 'none'
|
||||
@element.setAttribute('tabindex', 0)
|
||||
|
||||
# Terminal lines
|
||||
@children = [];
|
||||
i = @rows
|
||||
while i--
|
||||
div = @document.createElement('div')
|
||||
@element.appendChild(div)
|
||||
@children.push(div)
|
||||
|
||||
@parent.appendChild(@element);
|
||||
|
||||
# Draw screen
|
||||
@refresh 0, @rows - 1
|
||||
|
||||
@focus()
|
||||
|
||||
@startBlink()
|
||||
addEventListener('keydown', @keyDown.bind(@))
|
||||
addEventListener('keypress', @keyPress.bind(@))
|
||||
addEventListener('focus', @focus.bind(@))
|
||||
addEventListener('blur', @blur.bind(@))
|
||||
addEventListener('paste', @paste.bind(@))
|
||||
@initmouse()
|
||||
|
||||
# XTerm mouse events
|
||||
# http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
|
||||
# To better understand these
|
||||
@@ -350,7 +367,7 @@ class Terminal
|
||||
|
||||
|
||||
refresh: (start, end) ->
|
||||
if end - start >= @rows / 2
|
||||
if end - start >= @rows / 3
|
||||
parent = @element.parentNode
|
||||
parent?.removeChild @element
|
||||
|
||||
@@ -487,7 +504,6 @@ class Terminal
|
||||
@ydisp = @ybase
|
||||
@lines.splice @ybase + @scrollTop, 1
|
||||
|
||||
# @maxRange();
|
||||
@updateRange @scrollTop
|
||||
@updateRange @scrollBottom
|
||||
|
||||
@@ -706,7 +722,7 @@ class Terminal
|
||||
@state = State.normal
|
||||
else
|
||||
@state = State.normal
|
||||
@error "Unknown ESC control: %s.", ch
|
||||
console.log "Unknown ESC control:", ch
|
||||
|
||||
when State.charset
|
||||
switch ch
|
||||
@@ -1362,50 +1378,58 @@ class Terminal
|
||||
@element.classList.remove "bell"
|
||||
), @visualBell
|
||||
|
||||
resize: (x, y) ->
|
||||
x = 1 if x < 1
|
||||
y = 1 if y < 1
|
||||
resize: ->
|
||||
old_cols = @cols
|
||||
old_rows = @rows
|
||||
term_size = @parent.getBoundingClientRect()
|
||||
@cols = Math.floor(term_size.width / @char_size.width) - 1 # ?
|
||||
@rows = Math.floor(term_size.height / @char_size.height)
|
||||
if old_cols == @cols and old_rows == @rows
|
||||
return
|
||||
|
||||
@ctl 'Resize', @cols, @rows
|
||||
|
||||
# resize cols
|
||||
j = @cols
|
||||
if j < x
|
||||
if old_cols < @cols
|
||||
# does xterm use the default attr?
|
||||
ch = [@defAttr, " "]
|
||||
i = @lines.length
|
||||
while i--
|
||||
@lines[i].push ch while @lines[i].length < x
|
||||
else if j > x
|
||||
@lines[i].push ch while @lines[i].length < @cols
|
||||
else if old_cols > @cols
|
||||
i = @lines.length
|
||||
while i--
|
||||
@lines[i].pop() while @lines[i].length > x
|
||||
@lines[i].pop() while @lines[i].length > @cols
|
||||
|
||||
@setupStops j
|
||||
@cols = x
|
||||
@setupStops old_cols
|
||||
|
||||
# resize rows
|
||||
j = @rows
|
||||
if j < y
|
||||
j = old_rows
|
||||
if j < @rows
|
||||
el = @element
|
||||
while j++ < y
|
||||
while j++ < @rows
|
||||
@lines.push @blankLine() if @lines.length < y + @ybase
|
||||
if @children.length < y
|
||||
if @children.length < @rows
|
||||
line = @document.createElement("div")
|
||||
@line.className = 'line'
|
||||
line.style.height = @char_size.height + 'px'
|
||||
el.appendChild line
|
||||
@children.push line
|
||||
else if j > y
|
||||
while j-- > y
|
||||
@lines.pop() if @lines.length > y + @ybase
|
||||
if @children.length > y
|
||||
else if j > @rows
|
||||
while j-- > @rows
|
||||
@lines.pop() if @lines.length > @rows + @ybase
|
||||
if @children.length > @rows
|
||||
el = @children.pop()
|
||||
continue unless el
|
||||
el.parentNode.removeChild el
|
||||
@rows = y
|
||||
|
||||
# make sure the cursor stays on screen
|
||||
@y = y - 1 if @y >= y
|
||||
@x = x - 1 if @x >= x
|
||||
@y = @rows - 1 if @y >= @rows
|
||||
@x = @cols - 1 if @x >= @cols
|
||||
|
||||
@scrollTop = 0
|
||||
@scrollBottom = y - 1
|
||||
@scrollBottom = @rows - 1
|
||||
|
||||
@refresh 0, @rows - 1
|
||||
|
||||
# it's a real nightmare trying
|
||||
@@ -1520,7 +1544,7 @@ class Terminal
|
||||
|
||||
# ESC c Full Reset (RIS).
|
||||
reset: ->
|
||||
@init()
|
||||
@reset_vars()
|
||||
@refresh 0, @rows - 1
|
||||
|
||||
# ESC H Tab Set (HTS is 0x88).
|
||||
@@ -2907,12 +2931,12 @@ class Terminal
|
||||
|
||||
|
||||
get_html_height_in_lines: (html) ->
|
||||
line_height = +@children[0].style.height.replace("px", "")
|
||||
temp_node = document.createElement("div")
|
||||
temp_node.innerHTML = html
|
||||
@element.appendChild temp_node
|
||||
html_height = temp_node.getBoundingClientRect().height
|
||||
@element.removeChild temp_node
|
||||
Math.ceil(html_height / @char_size.height)
|
||||
|
||||
# DEC Special Character and Line Drawing Set.
|
||||
# http://vt100.net/docs/vt102-ug/table5-13.html
|
||||
|
||||
@@ -1,180 +1,6 @@
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
var $, State, Terminal, alt, bench, cancel, cbench, cols, ctrl, e, first, quit, resize, rows, s, state, term, virtual_input, ws, ws_url;
|
||||
|
||||
term = ws = null;
|
||||
|
||||
cols = rows = null;
|
||||
|
||||
quit = false;
|
||||
|
||||
$ = document.querySelectorAll.bind(document);
|
||||
|
||||
ws_url = 'ws://' + document.location.host + '/ws' + location.pathname;
|
||||
|
||||
ws = new WebSocket(ws_url);
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket open", arguments);
|
||||
term = new Terminal(function(data) {
|
||||
return ws.send('SH|' + data);
|
||||
});
|
||||
term.open($('main')[0]);
|
||||
$('.terminal')[0].style = '';
|
||||
return resize();
|
||||
};
|
||||
|
||||
ws.onerror = function() {
|
||||
return console.log("WebSocket error", arguments);
|
||||
};
|
||||
|
||||
ws.onmessage = function(e) {
|
||||
return setTimeout(function() {
|
||||
return term.write(e.data);
|
||||
}, 1);
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket closed", arguments);
|
||||
quit = true;
|
||||
return open('', '_self').close();
|
||||
};
|
||||
|
||||
addEventListener('beforeunload', function() {
|
||||
if (!quit) {
|
||||
return 'This will exit the terminal session';
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener('resize', resize = function() {
|
||||
var div, eh, ew, fake_term, fake_term_div, fake_term_line, main, main_bb, _i, _len, _ref;
|
||||
main = $('main')[0];
|
||||
fake_term = document.createElement('div');
|
||||
fake_term.className = 'terminal test';
|
||||
fake_term_div = document.createElement('div');
|
||||
fake_term_line = document.createElement('span');
|
||||
fake_term_line.textContent = '0123456789';
|
||||
fake_term_div.appendChild(fake_term_line);
|
||||
fake_term.appendChild(fake_term_div);
|
||||
main.appendChild(fake_term);
|
||||
ew = fake_term_line.getBoundingClientRect().width;
|
||||
eh = fake_term_div.getBoundingClientRect().height;
|
||||
main.removeChild(fake_term);
|
||||
main_bb = main.getBoundingClientRect();
|
||||
cols = Math.floor(10 * main_bb.width / ew) - 1;
|
||||
rows = Math.floor(main_bb.height / eh);
|
||||
console.log("Computed " + cols + " cols and " + rows + " rows from ", main_bb, ew, eh);
|
||||
term.resize(cols, rows);
|
||||
_ref = $('.terminal div');
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
div = _ref[_i];
|
||||
div.style.height = eh + 'px';
|
||||
}
|
||||
return ws.send("RS|" + cols + "," + rows);
|
||||
});
|
||||
|
||||
bench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
cbench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
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();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars + colors in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
try {
|
||||
document.createEvent("TouchEvent");
|
||||
virtual_input = true;
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
virtual_input = false;
|
||||
}
|
||||
|
||||
if (virtual_input) {
|
||||
ctrl = false;
|
||||
alt = false;
|
||||
first = true;
|
||||
virtual_input = document.createElement('input');
|
||||
virtual_input.type = 'password';
|
||||
virtual_input.style.position = 'fixed';
|
||||
virtual_input.style.top = 0;
|
||||
virtual_input.style.left = 0;
|
||||
virtual_input.style.border = 'none';
|
||||
virtual_input.style.outline = 'none';
|
||||
virtual_input.style.opacity = 0;
|
||||
virtual_input.value = '0';
|
||||
document.body.appendChild(virtual_input);
|
||||
virtual_input.addEventListener('blur', function() {
|
||||
var _this = this;
|
||||
return setTimeout((function() {
|
||||
return _this.focus();
|
||||
}), 10);
|
||||
});
|
||||
addEventListener('click', function() {
|
||||
return virtual_input.focus();
|
||||
});
|
||||
addEventListener('touchstart', function(e) {
|
||||
if (e.touches.length === 1) {
|
||||
return ctrl = true;
|
||||
} else if (e.touches.length === 2) {
|
||||
ctrl = false;
|
||||
return alt = true;
|
||||
} else if (e.touches.length === 3) {
|
||||
ctrl = true;
|
||||
return alt = true;
|
||||
}
|
||||
});
|
||||
virtual_input.addEventListener('keydown', function(e) {
|
||||
term.keyDown(e);
|
||||
return true;
|
||||
});
|
||||
virtual_input.addEventListener('input', function(e) {
|
||||
var len;
|
||||
len = this.value.length;
|
||||
if (len === 0) {
|
||||
e.keyCode = 8;
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
return true;
|
||||
}
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
if ((ctrl || alt) && !first) {
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
e.ctrlKey = ctrl;
|
||||
e.altKey = alt;
|
||||
if (e.keyCode >= 97 && e.keyCode <= 122) {
|
||||
e.keyCode -= 32;
|
||||
}
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
ctrl = alt = false;
|
||||
return true;
|
||||
}
|
||||
term.keyPress(e);
|
||||
first = false;
|
||||
this.value = '0';
|
||||
return true;
|
||||
});
|
||||
}
|
||||
var $, State, Terminal, alt, bench, cancel, cbench, cols, ctl, ctrl, e, first, quit, rows, s, send, state, term, virtual_input, ws, ws_url,
|
||||
__slice = [].slice;
|
||||
|
||||
cancel = function(ev) {
|
||||
if (ev.preventDefault) {
|
||||
@@ -200,10 +26,36 @@ State = {
|
||||
};
|
||||
|
||||
Terminal = (function() {
|
||||
function Terminal(out) {
|
||||
function Terminal(parent, out, ctl) {
|
||||
var div, i, term_size;
|
||||
this.parent = parent;
|
||||
this.out = out;
|
||||
this.cols = 80;
|
||||
this.rows = 24;
|
||||
this.ctl = ctl != null ? ctl : function() {};
|
||||
this.context = this.parent.ownerDocument.defaultView;
|
||||
this.document = this.parent.ownerDocument;
|
||||
this.body = this.document.getElementsByTagName('body')[0];
|
||||
this.element = this.document.createElement('div');
|
||||
this.element.className = 'terminal focus';
|
||||
this.element.style.outline = 'none';
|
||||
this.element.setAttribute('tabindex', 0);
|
||||
this.parent.appendChild(this.element);
|
||||
div = this.document.createElement('div');
|
||||
div.className = 'line';
|
||||
this.element.appendChild(div);
|
||||
this.children = [div];
|
||||
this.compute_char_size();
|
||||
div.style.height = this.char_size.height + 'px';
|
||||
term_size = this.parent.getBoundingClientRect();
|
||||
this.cols = Math.floor(term_size.width / this.char_size.width) - 1;
|
||||
this.rows = Math.floor(term_size.height / this.char_size.height);
|
||||
i = this.rows - 1;
|
||||
while (i--) {
|
||||
div = this.document.createElement('div');
|
||||
div.style.height = this.char_size.height + 'px';
|
||||
div.className = 'line';
|
||||
this.element.appendChild(div);
|
||||
this.children.push(div);
|
||||
}
|
||||
this.scrollback = 100000;
|
||||
this.visualBell = 100;
|
||||
this.convertEol = false;
|
||||
@@ -211,10 +63,20 @@ Terminal = (function() {
|
||||
this.cursorBlink = true;
|
||||
this.screenKeys = false;
|
||||
this.cursorState = 0;
|
||||
this.init();
|
||||
this.reset_vars();
|
||||
this.refresh(0, this.rows - 1);
|
||||
this.focus();
|
||||
this.startBlink();
|
||||
addEventListener('keydown', this.keyDown.bind(this));
|
||||
addEventListener('keypress', this.keyPress.bind(this));
|
||||
addEventListener('focus', this.focus.bind(this));
|
||||
addEventListener('blur', this.blur.bind(this));
|
||||
addEventListener('paste', this.paste.bind(this));
|
||||
addEventListener('resize', this.resize.bind(this));
|
||||
this.initmouse();
|
||||
}
|
||||
|
||||
Terminal.prototype.init = function() {
|
||||
Terminal.prototype.reset_vars = function() {
|
||||
var i;
|
||||
this.ybase = 0;
|
||||
this.ydisp = 0;
|
||||
@@ -248,6 +110,18 @@ Terminal = (function() {
|
||||
return this.skipNextKey = false;
|
||||
};
|
||||
|
||||
Terminal.prototype.compute_char_size = function() {
|
||||
var test_span;
|
||||
test_span = document.createElement('span');
|
||||
test_span.textContent = '0123456789';
|
||||
this.children[0].appendChild(test_span);
|
||||
this.char_size = {
|
||||
width: test_span.getBoundingClientRect().width / 10,
|
||||
height: this.children[0].getBoundingClientRect().height
|
||||
};
|
||||
return this.children[0].removeChild(test_span);
|
||||
};
|
||||
|
||||
Terminal.prototype.eraseAttr = function() {
|
||||
return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
|
||||
};
|
||||
@@ -280,38 +154,6 @@ Terminal = (function() {
|
||||
return cancel(ev);
|
||||
};
|
||||
|
||||
Terminal.prototype.open = function(parent) {
|
||||
var div, i;
|
||||
this.parent = parent || this.parent;
|
||||
if (!this.parent) {
|
||||
throw new Error('Terminal requires a parent element');
|
||||
}
|
||||
this.context = this.parent.ownerDocument.defaultView;
|
||||
this.document = this.parent.ownerDocument;
|
||||
this.body = this.document.getElementsByTagName('body')[0];
|
||||
this.element = this.document.createElement('div');
|
||||
this.element.className = 'terminal focus';
|
||||
this.element.style.outline = 'none';
|
||||
this.element.setAttribute('tabindex', 0);
|
||||
this.children = [];
|
||||
i = this.rows;
|
||||
while (i--) {
|
||||
div = this.document.createElement('div');
|
||||
this.element.appendChild(div);
|
||||
this.children.push(div);
|
||||
}
|
||||
this.parent.appendChild(this.element);
|
||||
this.refresh(0, this.rows - 1);
|
||||
this.focus();
|
||||
this.startBlink();
|
||||
addEventListener('keydown', this.keyDown.bind(this));
|
||||
addEventListener('keypress', this.keyPress.bind(this));
|
||||
addEventListener('focus', this.focus.bind(this));
|
||||
addEventListener('blur', this.blur.bind(this));
|
||||
addEventListener('paste', this.paste.bind(this));
|
||||
return this.initmouse();
|
||||
};
|
||||
|
||||
Terminal.prototype.initmouse = function() {
|
||||
var encode, getButton, getCoords, pressed, sendButton, sendEvent, sendMove,
|
||||
_this = this;
|
||||
@@ -388,7 +230,7 @@ Terminal = (function() {
|
||||
return _this.send("\x1b[M" + String.fromCharCode.apply(String, data));
|
||||
};
|
||||
getButton = function(ev) {
|
||||
var button, meta, mod, shift;
|
||||
var button, ctrl, meta, mod, shift;
|
||||
switch (ev.type) {
|
||||
case "mousedown":
|
||||
button = ev.button != null ? +ev.button : (ev.which != null ? ev.which - 1 : null);
|
||||
@@ -492,7 +334,7 @@ Terminal = (function() {
|
||||
|
||||
Terminal.prototype.refresh = function(start, end) {
|
||||
var attr, bg, ch, classes, data, fg, flags, i, line, out, parent, row, width, x, y;
|
||||
if (end - start >= this.rows / 2) {
|
||||
if (end - start >= this.rows / 3) {
|
||||
parent = this.element.parentNode;
|
||||
if (parent != null) {
|
||||
parent.removeChild(this.element);
|
||||
@@ -501,7 +343,6 @@ Terminal = (function() {
|
||||
width = this.cols;
|
||||
y = start;
|
||||
if (end >= this.lines.length) {
|
||||
this.log("`end` is too large. Most likely a bad CSR.");
|
||||
end = this.lines.length - 1;
|
||||
}
|
||||
while (y <= end) {
|
||||
@@ -573,7 +414,7 @@ Terminal = (function() {
|
||||
if (ch <= " ") {
|
||||
out += " ";
|
||||
} else {
|
||||
if (ch > "" && (ch >= "!" && ch <= "ᄒ") || (ch >= "ᅡ" && ch <= "ᅦ") || (ch >= "ᅧ" && ch <= "ᅬ") || (ch >= "ᅭ" && ch <= "ᅲ") || (ch >= "ᅳ" && ch <= "ᅵ") || (ch >= "¢" && ch <= "₩") || (ch >= "│" && ch <= "○")) {
|
||||
if (("\uff00" < ch && ch < "\uffef")) {
|
||||
i++;
|
||||
}
|
||||
out += ch;
|
||||
@@ -738,7 +579,7 @@ Terminal = (function() {
|
||||
this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch];
|
||||
this.x++;
|
||||
this.updateRange(this.y);
|
||||
if (ch > "" && (ch >= "!" && ch <= "ᄒ") || (ch >= "ᅡ" && ch <= "ᅦ") || (ch >= "ᅧ" && ch <= "ᅬ") || (ch >= "ᅭ" && ch <= "ᅲ") || (ch >= "ᅳ" && ch <= "ᅵ") || (ch >= "¢" && ch <= "₩") || (ch >= "│" && ch <= "○")) {
|
||||
if (("\uff00" < ch && ch < "\uffef")) {
|
||||
j = this.y + this.ybase;
|
||||
if (this.cols < 2 || this.x >= this.cols) {
|
||||
this.lines[j][this.x - 1] = [this.curAttr, " "];
|
||||
@@ -860,7 +701,7 @@ Terminal = (function() {
|
||||
break;
|
||||
default:
|
||||
this.state = State.normal;
|
||||
this.error("Unknown ESC control: %s.", ch);
|
||||
console.log("Unknown ESC control:", ch);
|
||||
}
|
||||
break;
|
||||
case State.charset:
|
||||
@@ -1130,8 +971,6 @@ Terminal = (function() {
|
||||
}
|
||||
this.send("\x1bP" + +valid + "$r" + pt + "\x1b\\");
|
||||
break;
|
||||
case "+p":
|
||||
break;
|
||||
case "+q":
|
||||
pt = this.currentParam;
|
||||
valid = false;
|
||||
@@ -1463,76 +1302,55 @@ Terminal = (function() {
|
||||
}), this.visualBell);
|
||||
};
|
||||
|
||||
Terminal.prototype.log = function() {
|
||||
var args;
|
||||
if (!this.debug) {
|
||||
Terminal.prototype.resize = function() {
|
||||
var ch, el, i, j, line, old_cols, old_rows, term_size;
|
||||
old_cols = this.cols;
|
||||
old_rows = this.rows;
|
||||
term_size = this.parent.getBoundingClientRect();
|
||||
this.cols = Math.floor(term_size.width / this.char_size.width) - 1;
|
||||
this.rows = Math.floor(term_size.height / this.char_size.height);
|
||||
if (old_cols === this.cols && old_rows === this.rows) {
|
||||
return;
|
||||
}
|
||||
if (!this.context.console || !this.context.console.log) {
|
||||
return;
|
||||
}
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
return this.context.console.log.apply(this.context.console, args);
|
||||
};
|
||||
|
||||
Terminal.prototype.error = function() {
|
||||
var args;
|
||||
if (!this.debug) {
|
||||
return;
|
||||
}
|
||||
if (!this.context.console || !this.context.console.error) {
|
||||
return;
|
||||
}
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
return this.context.console.error.apply(this.console.console, args);
|
||||
};
|
||||
|
||||
Terminal.prototype.resize = function(x, y) {
|
||||
var ch, el, i, j, line;
|
||||
if (x < 1) {
|
||||
x = 1;
|
||||
}
|
||||
if (y < 1) {
|
||||
y = 1;
|
||||
}
|
||||
j = this.cols;
|
||||
if (j < x) {
|
||||
this.ctl('Resize', this.cols, this.rows);
|
||||
if (old_cols < this.cols) {
|
||||
ch = [this.defAttr, " "];
|
||||
i = this.lines.length;
|
||||
while (i--) {
|
||||
while (this.lines[i].length < x) {
|
||||
while (this.lines[i].length < this.cols) {
|
||||
this.lines[i].push(ch);
|
||||
}
|
||||
}
|
||||
} else if (j > x) {
|
||||
} else if (old_cols > this.cols) {
|
||||
i = this.lines.length;
|
||||
while (i--) {
|
||||
while (this.lines[i].length > x) {
|
||||
while (this.lines[i].length > this.cols) {
|
||||
this.lines[i].pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setupStops(j);
|
||||
this.cols = x;
|
||||
j = this.rows;
|
||||
if (j < y) {
|
||||
this.setupStops(old_cols);
|
||||
j = old_rows;
|
||||
if (j < this.rows) {
|
||||
el = this.element;
|
||||
while (j++ < y) {
|
||||
while (j++ < this.rows) {
|
||||
if (this.lines.length < y + this.ybase) {
|
||||
this.lines.push(this.blankLine());
|
||||
}
|
||||
if (this.children.length < y) {
|
||||
if (this.children.length < this.rows) {
|
||||
line = this.document.createElement("div");
|
||||
this.line.className = 'line';
|
||||
line.style.height = this.char_size.height + 'px';
|
||||
el.appendChild(line);
|
||||
this.children.push(line);
|
||||
}
|
||||
}
|
||||
} else if (j > y) {
|
||||
while (j-- > y) {
|
||||
if (this.lines.length > y + this.ybase) {
|
||||
} else if (j > this.rows) {
|
||||
while (j-- > this.rows) {
|
||||
if (this.lines.length > this.rows + this.ybase) {
|
||||
this.lines.pop();
|
||||
}
|
||||
if (this.children.length > y) {
|
||||
if (this.children.length > this.rows) {
|
||||
el = this.children.pop();
|
||||
if (!el) {
|
||||
continue;
|
||||
@@ -1541,15 +1359,14 @@ Terminal = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.rows = y;
|
||||
if (this.y >= y) {
|
||||
this.y = y - 1;
|
||||
if (this.y >= this.rows) {
|
||||
this.y = this.rows - 1;
|
||||
}
|
||||
if (this.x >= x) {
|
||||
this.x = x - 1;
|
||||
if (this.x >= this.cols) {
|
||||
this.x = this.cols - 1;
|
||||
}
|
||||
this.scrollTop = 0;
|
||||
this.scrollBottom = y - 1;
|
||||
this.scrollBottom = this.rows - 1;
|
||||
this.refresh(0, this.rows - 1);
|
||||
return this.normal = null;
|
||||
};
|
||||
@@ -1705,7 +1522,7 @@ Terminal = (function() {
|
||||
};
|
||||
|
||||
Terminal.prototype.reset = function() {
|
||||
this.init();
|
||||
this.reset_vars();
|
||||
return this.refresh(0, this.rows - 1);
|
||||
};
|
||||
|
||||
@@ -2164,7 +1981,6 @@ Terminal = (function() {
|
||||
case 7:
|
||||
return this.wraparoundMode = true;
|
||||
case 66:
|
||||
this.log("Serial port requested application keypad.");
|
||||
return this.applicationKeypad = true;
|
||||
case 9:
|
||||
case 1000:
|
||||
@@ -2174,8 +1990,7 @@ Terminal = (function() {
|
||||
this.vt200Mouse = params === 1000;
|
||||
this.normalMouse = params > 1000;
|
||||
this.mouseEvents = true;
|
||||
this.element.style.cursor = "default";
|
||||
return this.log("Binding to mouse events.");
|
||||
return this.element.style.cursor = "default";
|
||||
case 1004:
|
||||
return this.sendFocus = true;
|
||||
case 1005:
|
||||
@@ -2233,7 +2048,6 @@ Terminal = (function() {
|
||||
case 7:
|
||||
return this.wraparoundMode = false;
|
||||
case 66:
|
||||
this.log("Switching back to normal keypad.");
|
||||
return this.applicationKeypad = false;
|
||||
case 9:
|
||||
case 1000:
|
||||
@@ -2526,13 +2340,13 @@ Terminal = (function() {
|
||||
};
|
||||
|
||||
Terminal.prototype.get_html_height_in_lines = function(html) {
|
||||
var html_height, line_height, temp_node;
|
||||
line_height = +this.children[0].style.height.replace("px", "");
|
||||
var html_height, temp_node;
|
||||
temp_node = document.createElement("div");
|
||||
temp_node.innerHTML = html;
|
||||
this.element.appendChild(temp_node);
|
||||
html_height = temp_node.getBoundingClientRect().height;
|
||||
return this.element.removeChild(temp_node);
|
||||
this.element.removeChild(temp_node);
|
||||
return Math.ceil(html_height / this.char_size.height);
|
||||
};
|
||||
|
||||
Terminal.prototype.charsets = {
|
||||
@@ -2620,3 +2434,159 @@ document.addEventListener('keydown', function(e) {
|
||||
return state.x = state.y = null;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
document.createEvent("TouchEvent");
|
||||
virtual_input = true;
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
virtual_input = false;
|
||||
}
|
||||
|
||||
if (virtual_input) {
|
||||
ctrl = false;
|
||||
alt = false;
|
||||
first = true;
|
||||
virtual_input = document.createElement('input');
|
||||
virtual_input.type = 'password';
|
||||
virtual_input.style.position = 'fixed';
|
||||
virtual_input.style.top = 0;
|
||||
virtual_input.style.left = 0;
|
||||
virtual_input.style.border = 'none';
|
||||
virtual_input.style.outline = 'none';
|
||||
virtual_input.style.opacity = 0;
|
||||
virtual_input.value = '0';
|
||||
document.body.appendChild(virtual_input);
|
||||
virtual_input.addEventListener('blur', function() {
|
||||
var _this = this;
|
||||
return setTimeout((function() {
|
||||
return _this.focus();
|
||||
}), 10);
|
||||
});
|
||||
addEventListener('click', function() {
|
||||
return virtual_input.focus();
|
||||
});
|
||||
addEventListener('touchstart', function(e) {
|
||||
if (e.touches.length === 1) {
|
||||
return ctrl = true;
|
||||
} else if (e.touches.length === 2) {
|
||||
ctrl = false;
|
||||
return alt = true;
|
||||
} else if (e.touches.length === 3) {
|
||||
ctrl = true;
|
||||
return alt = true;
|
||||
}
|
||||
});
|
||||
virtual_input.addEventListener('keydown', function(e) {
|
||||
term.keyDown(e);
|
||||
return true;
|
||||
});
|
||||
virtual_input.addEventListener('input', function(e) {
|
||||
var len;
|
||||
len = this.value.length;
|
||||
if (len === 0) {
|
||||
e.keyCode = 8;
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
return true;
|
||||
}
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
if ((ctrl || alt) && !first) {
|
||||
e.keyCode = this.value.charAt(1).charCodeAt(0);
|
||||
e.ctrlKey = ctrl;
|
||||
e.altKey = alt;
|
||||
if (e.keyCode >= 97 && e.keyCode <= 122) {
|
||||
e.keyCode -= 32;
|
||||
}
|
||||
term.keyDown(e);
|
||||
this.value = '0';
|
||||
ctrl = alt = false;
|
||||
return true;
|
||||
}
|
||||
term.keyPress(e);
|
||||
first = false;
|
||||
this.value = '0';
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
cols = rows = null;
|
||||
|
||||
quit = false;
|
||||
|
||||
$ = document.querySelectorAll.bind(document);
|
||||
|
||||
send = function(data) {
|
||||
return ws.send('S' + data);
|
||||
};
|
||||
|
||||
ctl = function() {
|
||||
var args, params, type;
|
||||
type = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
params = args.join(',');
|
||||
if (type === 'Resize') {
|
||||
return ws.send('R' + params);
|
||||
}
|
||||
};
|
||||
|
||||
ws_url = 'ws://' + document.location.host + '/ws' + location.pathname;
|
||||
|
||||
ws = new WebSocket(ws_url);
|
||||
|
||||
term = new Terminal($('#wrapper')[0], send, ctl);
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log("WebSocket open", arguments);
|
||||
return ws.send('R' + term.cols + ',' + term.rows);
|
||||
};
|
||||
|
||||
ws.onerror = function() {
|
||||
return console.log("WebSocket error", arguments);
|
||||
};
|
||||
|
||||
ws.onmessage = function(e) {
|
||||
return setTimeout(function() {
|
||||
return term.write(e.data);
|
||||
}, 1);
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log("WebSocket closed", arguments);
|
||||
quit = true;
|
||||
return open('', '_self').close();
|
||||
};
|
||||
|
||||
addEventListener('beforeunload', function() {
|
||||
if (!quit) {
|
||||
return 'This will exit the terminal session';
|
||||
}
|
||||
});
|
||||
|
||||
bench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
n = 100000000;
|
||||
}
|
||||
rnd = '';
|
||||
while (rnd.length < n) {
|
||||
rnd += Math.random().toString(36).substring(2);
|
||||
}
|
||||
t0 = (new Date()).getTime();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
cbench = function(n) {
|
||||
var rnd, t0;
|
||||
if (n == null) {
|
||||
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();
|
||||
term.write(rnd);
|
||||
return console.log("" + n + " chars + colors in " + ((new Date()).getTime() - t0) + " ms");
|
||||
};
|
||||
|
||||
@@ -22,34 +22,31 @@ html, body
|
||||
padding: 0
|
||||
line-height: 1.2
|
||||
|
||||
body
|
||||
main
|
||||
display: flex
|
||||
height: 100%
|
||||
flex-direction: column
|
||||
background-color: black
|
||||
overflow: hidden
|
||||
white-space: nowrap
|
||||
|
||||
.terminal
|
||||
flex: 1
|
||||
outline: none
|
||||
background-color: $bg
|
||||
color: $fg
|
||||
text-shadow: 0 0 $shadow rgba($fg, $shadow-alpha)
|
||||
transition: 200ms
|
||||
#wrapper
|
||||
height: 100%
|
||||
background-color: black
|
||||
overflow: hidden
|
||||
white-space: nowrap
|
||||
|
||||
&.bell
|
||||
-webkit-filter: blur(2px)
|
||||
.terminal
|
||||
outline: none
|
||||
background-color: $bg
|
||||
color: $fg
|
||||
text-shadow: 0 0 $shadow rgba($fg, $shadow-alpha)
|
||||
transition: 200ms
|
||||
|
||||
&.skip
|
||||
-webkit-filter: sepia(1)
|
||||
&.bell
|
||||
-webkit-filter: blur(2px)
|
||||
|
||||
div
|
||||
overflow: visible
|
||||
&.skip
|
||||
-webkit-filter: sepia(1)
|
||||
|
||||
.inline-html
|
||||
white-space: normal
|
||||
.line
|
||||
overflow: visible
|
||||
|
||||
.inline-html
|
||||
white-space: normal
|
||||
|
||||
|
||||
.focus .cursor
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,8 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
|
||||
<main id="wrapper">
|
||||
</main>
|
||||
|
||||
<script src="{{ static_url('javascripts/main.js') }}"></script>
|
||||
|
||||
5
dev.py
5
dev.py
@@ -8,7 +8,10 @@ import shlex
|
||||
|
||||
commands = [
|
||||
'coffee -wcb -j butterfly/static/javascripts/main.js ' +
|
||||
' '.join(glob('butterfly/static/coffees/*.coffee')),
|
||||
'butterfly/static/coffees/term.coffee ' +
|
||||
'butterfly/static/coffees/backsel.coffee ' +
|
||||
'butterfly/static/coffees/virtual_input.coffee ' +
|
||||
'butterfly/static/coffees/main.coffee ',
|
||||
'compass watch butterfly/static',
|
||||
'python butterfly.server.py ' + ' '.join(sys.argv[1:])
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user