diff --git a/butterfly/static/coffees/main.coffee b/butterfly/static/coffees/main.coffee index c3c75ff..db33981 100644 --- a/butterfly/static/coffees/main.coffee +++ b/butterfly/static/coffees/main.coffee @@ -15,65 +15,786 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +worker = new Worker('/static/javascripts/worker.js') + +worker.addEventListener 'message', (e) -> + switch e.data.cmd + when 'refresh' + frontterm.refresh e.data.lines, e.data.start, e.data.end + cols = rows = null quit = false $ = document.querySelectorAll.bind(document) send = (data) -> - ws.send 'S' + data + worker.postMessage + cmd: 'data' + data: data ctl = (type, args...) -> params = args.join(',') if type == 'Resize' - ws.send 'R' + params + worker.postMessage 'R' + params + # ws.send 'R' + params -ws_url = 'ws://' + document.location.host + '/ws' + location.pathname -ws = new WebSocket ws_url - -ws.addEventListener 'open', -> - console.log "WebSocket open", arguments - ws.send 'R' + term.cols + ',' + term.rows - if location.hash - setTimeout -> - ws.send 'S' + location.hash.slice(1) + '\n' - , 100 - -ws.addEventListener 'error', -> - console.log "WebSocket error", arguments - -ws.addEventListener 'message', (e) -> - setTimeout -> - term.write e.data - , 1 - -ws.addEventListener 'close', -> - console.log "WebSocket closed", arguments - quit = true - open('','_self').close() - - -term = new Terminal $('#wrapper')[0], send, ctl addEventListener 'beforeunload', -> if not quit 'This will exit the terminal session' -bench = (n=100000000) -> - rnd = '' - while rnd.length < n - rnd += Math.random().toString(36).substring(2) - - t0 = (new Date()).getTime() - term.write rnd - console.log "#{n} chars in #{(new Date()).getTime() - t0} ms" +cancel = (ev) -> + ev.preventDefault() if ev.preventDefault + ev.stopPropagation() if ev.stopPropagation + ev.cancelBubble = true + false -cbench = (n=100000000) -> - rnd = '' - while rnd.length < n - rnd += "\x1b[#{30 + parseInt(Math.random() * 20)}m" - rnd += Math.random().toString(36).substring(2) +class FrontTerminal + constructor: (@parent) -> + # Global elements + @context = @parent.ownerDocument.defaultView + @document = @parent.ownerDocument + @body = @document.getElementsByTagName('body')[0] - t0 = (new Date()).getTime() - term.write rnd - console.log "#{n} chars + colors in #{(new Date()).getTime() - t0} ms" + # 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) + + @visualBell = 100 + @cursorHidden = false + @queue = '' + + # stream + @defAttr = (0 << 18) | (257 << 9) | (256 << 0) + @skipNextKey = null + @cursorState = 0 + + # 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(@) + + # Horrible Firefox paste workaround + if typeof InstallTrigger isnt "undefined" + @element.contentEditable = 'true' + @element.addEventListener "mouseup", (ev) => + sel = getSelection().getRangeAt(0) + if sel.startOffset is sel.endOffset + getSelection().removeAllRanges() + + @initmouse() + + 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) + + focus: -> + @send('\x1b[I') if @sendFocus + @showCursor() + @element.classList.add('focus') + @element.classList.remove('blur') + + blur: -> + @cursorState = 1 + # @refresh(@y, @y) + @send('\x1b[O') if @sendFocus + @element.classList.add('blur') + @element.classList.remove('focus') + + paste: (ev) -> + if ev.clipboardData + @send ev.clipboardData.getData('text/plain') + else if @context.clipboardData + @send @context.clipboardData.getData('Text') + cancel(ev) + + # XTerm mouse events + # http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking + # To better understand these + # the xterm code is very helpful: + # Relevant files: + # button.c, charproc.c, misc.c + # Relevant functions in xterm/button.c: + # BtnCode, EmitButtonCode, EditorButton, SendMousePosition + initmouse: -> + pressed = 32 + + # mouseup, mousedown, mousewheel + # left click: ^[[M 3<^[[M#3< + # mousewheel up: ^[[M`3> + sendButton = (ev) => + # get the xterm-style button + button = getButton(ev) + + # get mouse coordinates + pos = getCoords(ev) + return unless pos + sendEvent button, pos + switch ev.type + when "mousedown" + pressed = button + + when "mouseup" + # keep it at the left + # button, just in case. + pressed = 32 + + # motion example of a left click: + # ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< + sendMove = (ev) => + button = pressed + pos = getCoords(ev) + return unless pos + + # buttons marked as motions + # are incremented by 32 + button += 32 + sendEvent button, pos + + # encode button and + # position to characters + encode = (data, ch) => + unless @utfMouse + return data.push(0) if ch is 255 + ch = 127 if ch > 127 + data.push ch + else + return data.push(0) if ch is 2047 + if ch < 127 + data.push ch + else + ch = 2047 if ch > 2047 + data.push 0xC0 | (ch >> 6) + data.push 0x80 | (ch & 0x3F) + + + # send a mouse event: + # regular/utf8: ^[[M Cb Cx Cy + # urxvt: ^[[ Cb ; Cx ; Cy M + # sgr: ^[[ Cb ; Cx ; Cy M/m + # vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r + # locator: CSI P e ; P b ; P r ; P c ; P p & w + sendEvent = (button, pos) => + + if @urxvtMouse + pos.x -= 32 + pos.y -= 32 + pos.x++ + pos.y++ + @send "\x1b[" + button + ";" + pos.x + ";" + pos.y + "M" + return + + if @sgrMouse + pos.x -= 32 + pos.y -= 32 + @send "\x1b[<" + (if (button & 3) is 3 then button & ~3 else button) + ";" + pos.x + ";" + pos.y + (if (button & 3) is 3 then "m" else "M") + return + + data = [] + encode data, button + encode data, pos.x + encode data, pos.y + @send "\x1b[M" + String.fromCharCode.apply(String, data) + + getButton = (ev) => + # two low bits: + # 0 = left + # 1 = middle + # 2 = right + # 3 = release + # wheel up/down: + # 1, and 2 - with 64 added + switch ev.type + when "mousedown" + button = if ev.button? then +ev.button else (if ev.which? then ev.which - 1 else null) + when "mouseup" + button = 3 + when "wheel" + button = if ev.deltaY < 0 then 64 else 65 + + # next three bits are the modifiers: + # 4 = shift, 8 = meta, 16 = control + shift = if ev.shiftKey then 4 else 0 + meta = if ev.metaKey then 8 else 0 + ctrl = if ev.ctrlKey then 16 else 0 + mod = shift | meta | ctrl + + # no mods + if @vt200Mouse + # ctrl only + mod &= ctrl + else + mod = 0 unless @normalMouse + + # increment to SP + (32 + (mod << 2)) + button + + # mouse coordinates measured in cols/rows + getCoords = (ev) => + x = ev.pageX + y = ev.pageY + + # should probably check offsetParent + # but this is more portable + el = @element + while el and el isnt @document.documentElement + x -= el.offsetLeft + y -= el.offsetTop + el = if "offsetParent" of el then el.offsetParent else el.parentNode + + # convert to cols/rows + w = @element.clientWidth + h = @element.clientHeight + x = Math.ceil((x / w) * @cols) + y = Math.ceil((y / h) * @rows) + + # be sure to avoid sending + # bad positions to the program + x = 0 if x < 0 + x = @cols if x > @cols + y = 0 if y < 0 + y = @rows if y > @rows + + # xterm sends raw bytes and + # starts at 32 (SP) for each. + x += 32 + y += 32 + + x: x + y: y + type: ev.type + + addEventListener "mousedown", (ev) => + return unless @mouseEvents + + # send the button + sendButton ev + + # fix for odd bug + #if (@vt200Mouse && !@normalMouse) { + if @vt200Mouse + sendButton + __proto__: ev + type: "mouseup" + + return cancel(ev) + + addEventListener "mousemove", sendMove.bind(this) if @normalMouse + + # x10 compatibility mode can't send button releases + unless @x10Mouse + addEventListener "mouseup", up = (ev) => + sendButton ev + removeEventListener "mousemove", sendMove if @normalMouse + removeEventListener "mouseup", up + cancel ev + cancel ev + + addEventListener "wheel", (ev) => + if @mouseEvents + return if @x10Mouse + sendButton ev + else + return if @applicationKeypad + @scrollDisp if ev.deltaY > 0 then 5 else -5 + cancel ev + + + refresh: (lines, start, end) -> + if end - start >= @rows / 3 + parent = @element.parentNode + parent?.removeChild @element + + width = @cols + y = start + + if end >= lines.length + end = lines.length - 1 + + while y <= end + row = y + line = lines[row] + out = "" + + if y is @y and (@ydisp is @ybase or @selectMode) and not @cursorHidden + x = @x + else + x = -Infinity + + attr = @defAttr + i = 0 + while i < width + data = line[i][0] + ch = line[i][1] + if data isnt attr + out += "" if attr isnt @defAttr + if data isnt @defAttr + classes = [] + out += "> 9) & 0x1ff + flags = data >> 18 + + # bold + classes.push "bold" if flags & 1 + # underline + classes.push "underline" if flags & 2 + # blink + classes.push "blink" if flags & 4 + # inverse + classes.push "reverse-video" if flags & 8 + # invisible + classes.push "invisible" if flags & 16 + + fg += 8 if flags & 1 and fg < 8 + classes.push "bg-color-" + bg + classes.push "fg-color-" + fg + + out += "class=\"" + out += classes.join(" ") + out += "\">" + out += "" if i is x + + # This is a temporary dirty hack for raw html insertion + if ch.length > 1 + out += ch + else + switch ch + when "&" + out += "&" + when "<" + out += "<" + when ">" + out += ">" + else + if ch <= " " + out += " " + else + i++ if "\uff00" < ch < "\uffef" + out += ch + out += "" if i is x + attr = data + i++ + out += "" if attr isnt @defAttr + @children[y].innerHTML = out + y++ + + parent?.appendChild @element + + + _cursorBlink: -> + @cursorState ^= 1 + cursor = @element.querySelector(".cursor") + return unless cursor + if cursor.classList.contains("reverse-video") + cursor.classList.remove "reverse-video" + else + cursor.classList.add "reverse-video" + + + showCursor: -> + return + # unless @cursorState + # @cursorState = 1 + # @refresh @y, @y + + + startBlink: -> + return unless @cursorBlink + @_blinker = => @_cursorBlink() + @_blink = setInterval(@_blinker, 500) + + + refreshBlink: -> + return unless @cursorBlink + clearInterval @_blink + @_blink = setInterval(@_blinker, 500) + + + scrollDisp: (disp) -> + @ydisp += disp + if @ydisp > @ybase + @ydisp = @ybase + + else + @ydisp = 0 if @ydisp < 0 + + # @refresh 0, @rows - 1 + + keyDown: (ev) -> + # Key Resources: + # https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent + # 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 + + # Alt-z works as an escape to relay the following keys to the browser. + # usefull to trigger browser shortcuts, i.e.: Alt+Z F5 to reload + # May be redundant with keyPrefix + if ev.altKey and ev.keyCode is 90 and not @skipNextKey + @skipNextKey = true + @element.classList.add('skip') + return cancel(ev) + + if @skipNextKey + @skipNextKey = false + @element.classList.remove('skip') + return true + + switch ev.keyCode + # backspace + when 8 + key = if ev.altKey then "\x1b" else "" + if ev.shiftKey + key += "\x08" # ^H + break + key += "\x7f" # ^? + + # tab + when 9 + if ev.shiftKey + key = "\x1b[Z" + break + key = "\t" + + # return/enter + when 13 + key = "\r" + + # escape + when 27 + key = "\x1b" + + # left-arrow + when 37 + if @applicationCursor + key = "\x1bOD" # SS3 as ^[O for 7-bit + #key = '\x8fD'; // SS3 as 0x8f for 8-bit + break + return true if ev.shiftKey + key = "\x1b[D" + + # right-arrow + when 39 + if @applicationCursor + key = "\x1bOC" + break + return true if ev.shiftKey + key = "\x1b[C" + + # up-arrow + when 38 + if @applicationCursor + key = "\x1bOA" + break + if ev.ctrlKey + @scrollDisp -1 + return cancel(ev) + else if ev.shiftKey + return true + else + key = "\x1b[A" + + # down-arrow + when 40 + if @applicationCursor + key = "\x1bOB" + break + if ev.ctrlKey + @scrollDisp 1 + return cancel(ev) + else if ev.shiftKey + return true + else + key = "\x1b[B" + + # delete + when 46 + key = "\x1b[3~" + + # insert + when 45 + key = "\x1b[2~" + + # home + when 36 + if @applicationKeypad + key = "\x1bOH" + break + key = "\x1bOH" + + # end + when 35 + if @applicationKeypad + key = "\x1bOF" + break + key = "\x1bOF" + + # page up + when 33 + if ev.shiftKey + @scrollDisp -(@rows - 1) + return cancel(ev) + else + key = "\x1b[5~" + + # page down + when 34 + if ev.shiftKey + @scrollDisp @rows - 1 + return cancel(ev) + else + key = "\x1b[6~" + + # F1 + when 112 + key = "\x1bOP" + + # F2 + when 113 + key = "\x1bOQ" + + # F3 + when 114 + key = "\x1bOR" + + # F4 + when 115 + key = "\x1bOS" + + # F5 + when 116 + key = "\x1b[15~" + + # F6 + when 117 + key = "\x1b[17~" + + # F7 + when 118 + key = "\x1b[18~" + + # F8 + when 119 + key = "\x1b[19~" + + # F9 + when 120 + key = "\x1b[20~" + + # F10 + when 121 + key = "\x1b[21~" + + # F11 + when 122 + key = "\x1b[23~" + + # F12 + when 123 + key = "\x1b[24~" + + else + # a-z and space + if ev.ctrlKey + if ev.keyCode >= 65 and ev.keyCode <= 90 + + # Ctrl-A + if @screenKeys + if not @prefixMode and not @selectMode and ev.keyCode is 65 + @enterPrefix() + return cancel(ev) + + # Ctrl-V + if @prefixMode and ev.keyCode is 86 + @leavePrefix() + return + + # Ctrl-C + if (@prefixMode or @selectMode) and ev.keyCode is 67 + if @visualMode + setTimeout (=> + @leaveVisual() + return + ), 1 + return + key = String.fromCharCode(ev.keyCode - 64) + else if ev.keyCode is 32 + + # NUL + key = String.fromCharCode(0) + else if ev.keyCode >= 51 and ev.keyCode <= 55 + + # escape, file sep, group sep, record sep, unit sep + key = String.fromCharCode(ev.keyCode - 51 + 27) + else if ev.keyCode is 56 + + # delete + key = String.fromCharCode(127) + else if ev.keyCode is 219 + + # ^[ - escape + key = String.fromCharCode(27) + + # ^] - group sep + else + key = String.fromCharCode(29) if ev.keyCode is 221 + + else if ev.altKey + if ev.keyCode >= 65 and ev.keyCode <= 90 + key = "\x1b" + String.fromCharCode(ev.keyCode + 32) + else if ev.keyCode is 192 + key = "\x1b`" + else + key = "\x1b" + (ev.keyCode - 48) if ev.keyCode >= 48 and ev.keyCode <= 57 + + if ev.keyCode >= 37 and ev.keyCode <= 40 + if ev.ctrlKey + key = key.slice(0, -1) + "1;5" + key.slice(-1) + else if ev.altKey + key = key.slice(0, -1) + "1;3" + key.slice(-1) + else key = key.slice(0, -1) + "1;4" + key.slice(-1) if ev.shiftKey + + return true unless key + + if @prefixMode + @leavePrefix() + return cancel(ev) + + if @selectMode + @keySelect ev, key + return cancel(ev) + + @showCursor() + @handler(key) + cancel ev + + keyPress: (ev) -> + if @skipNextKey is false + @skipNextKey = null + return true + + cancel ev + + if ev.charCode + key = ev.charCode + else unless ev.which? + key = ev.keyCode + else if ev.which isnt 0 and ev.charCode isnt 0 + key = ev.which + else + return false + + return false if not key or ev.ctrlKey or ev.altKey or ev.metaKey + key = String.fromCharCode(key) + + @showCursor() + @handler key + false + + handler: (data) -> + worker.postMessage + cmd: 'data', + data: data + + send: (data) -> + unless @queue + setTimeout (=> + @handler @queue + @queue = "" + return + ), 1 + + @queue += data + + bell: -> + return unless @visualBell + @element.classList.add "bell" + setTimeout (=> + @element.classList.remove "bell" + ), @visualBell + + 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 rows + j = old_rows + if j < @rows + el = @element + while j++ < @rows + 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 > @rows + while j-- > @rows + if @children.length > @rows + el = @children.pop() + continue unless el + el.parentNode.removeChild el + + + get_html_height_in_lines: (html) -> + 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) + + +frontterm = new FrontTerminal $('#wrapper')[0], send, ctl +worker.postMessage + cmd: 'init' + cols: frontterm.cols + rows: frontterm.rows + wsurl: 'ws://' + document.location.host + '/ws' + location.pathname diff --git a/butterfly/static/coffees/worker.coffee b/butterfly/static/coffees/worker.coffee new file mode 100644 index 0000000..10e9096 --- /dev/null +++ b/butterfly/static/coffees/worker.coffee @@ -0,0 +1,2303 @@ + +ws = null +backterm = null + +self.addEventListener 'message', (e) -> + switch e.data.cmd + when 'init' + backterm = new BackTerminal(e.data.cols, e.data.rows) + ws = new WebSocket e.data.wsurl + ws.addEventListener 'open', -> + console.log "WebSocket open", arguments + ws.send 'R' + e.data.cols + ',' + e.data.rows + # ws.send 'R' + term.cols + ',' + term.rows + # if location.hash + # setTimeout -> + # ws.send 'S' + location.hash.slice(1) + '\n' + # , 100 + + ws.addEventListener 'error', -> + console.log "WebSocket error", arguments + + ws.addEventListener 'message', (e) -> + backterm.write e.data + # setTimeout -> + # term.write e.data + # , 1 + + ws.addEventListener 'close', -> + console.log "WebSocket closed", arguments + # quit = true + # open('','_self').close() + when 'data' + ws.send 'S' + e.data.data + +s = 0 +State = + normal: s++ + escaped: s++ + csi: s++ + osc: s++ + charset: s++ + dcs: s++ + ignore: s++ + + +class BackTerminal + constructor: (@cols, @rows) -> + @scrollback = 100000 + @visualBell = 100 + @convertEol = false + @termName = 'xterm' + @cursorBlink = true + @screenKeys = false + @cursorState = 0 + @reset_vars() + + + reset_vars: -> + @ybase = 0 + @ydisp = 0 + @x = 0 + @y = 0 + @state = State.normal + @scrollTop = 0 + @scrollBottom = @rows - 1 + + # modes + @applicationKeypad = false + @applicationCursor = false + @originMode = false + @wraparoundMode = false + @normal = null + + # charset + @charset = null + @gcharset = null + @glevel = 0 + @charsets = [null] + + # stream + @defAttr = (0 << 18) | (257 << 9) | (256 << 0) + @curAttr = @defAttr + @params = [] + @currentParam = 0 + @prefix = "" + @lines = [] + i = @rows + @lines.push @blankLine() while i-- + @setupStops() + + eraseAttr: -> + (@defAttr & ~0x1ff) | (@curAttr & 0x1ff) + + scroll: -> + if ++@ybase is @scrollback + @ybase = @ybase / 2 | 0 + @lines = @lines.slice(-(@ybase + @rows) + 1) + + @ydisp = @ybase + + # last line + row = @ybase + @rows - 1 + + # subtract the bottom scroll region + row -= @rows - 1 - @scrollBottom + if row is @lines.length + # potential optimization: + # pushing is faster than splicing + # when they amount to the same + # behavior. + @lines.push @blankLine() + else + # add our new line + @lines.splice row, 0, @blankLine() + + if @scrollTop isnt 0 + if @ybase isnt 0 + @ybase-- + @ydisp = @ybase + @lines.splice @ybase + @scrollTop, 1 + + @updateRange @scrollTop + @updateRange @scrollBottom + + scrollDisp: (disp) -> + @ydisp += disp + if @ydisp > @ybase + @ydisp = @ybase + + else + @ydisp = 0 if @ydisp < 0 + + @refresh 0, @rows - 1 + + refresh: (start, end) -> + self.postMessage + cmd: 'refresh' + lines: @lines.slice(@ydisp, @ydisp + @rows) + start: start + end: end + + write: (data) -> + @refreshStart = @y + @refreshEnd = @y + + if @ybase isnt @ydisp + @ydisp = @ybase + @maxRange() + + i = 0 + l = data.length + while i < l + ch = data[i] + switch @state + when State.normal + switch ch + + # '\a' + when "\x07" + @bell() + + # '\n', '\v', '\f' + when "\n", "\x0b", "\x0c" + @x = 0 if @convertEol + + @y++ + if @y > @scrollBottom + @y-- + @scroll() + + # '\r' + when "\r" + @x = 0 + + # '\b' + when "\b" + @x-- if @x > 0 + + # '\t' + when "\t" + @x = @nextStop() + + # shift out + when "\x0e" + @setgLevel 1 + + # shift in + when "\x0f" + @setgLevel 0 + + # '\e' + when "\x1b" + @state = State.escaped + + else + # ' ' + if ch >= " " + ch = @charset[ch] if @charset?[ch] + if @x >= @cols + @x = 0 + @y++ + if @y > @scrollBottom + @y-- + @scroll() + @lines[@y + @ybase][@x] = [@curAttr, ch] + @x++ + @updateRange @y + if "\uff00" < ch < "\uffef" + j = @y + @ybase + if @cols < 2 or @x >= @cols + @lines[j][@x - 1] = [@curAttr, " "] + break + + @lines[j][@x] = [@curAttr, " "] + @x++ + + when State.escaped + switch ch + # ESC [ Control Sequence Introducer ( CSI is 0x9b). + when "[" + @params = [] + @currentParam = 0 + @state = State.csi + + # ESC ] Operating System Command ( OSC is 0x9d). + when "]" + @params = [] + @currentParam = 0 + @state = State.osc + + # ESC P Device Control String ( DCS is 0x90). + when "P" + @params = [] + @currentParam = 0 + @state = State.dcs + + # ESC _ Application Program Command ( APC is 0x9f). + when "_" + @state = State.ignore + + # ESC ^ Privacy Message ( PM is 0x9e). + when "^" + @state = State.ignore + + # ESC c Full Reset (RIS). + when "c" + @reset() + + # ESC E Next Line ( NEL is 0x85). + when "E" + @x = 0 + @index() + + # ESC D Index ( IND is 0x84). + when "D" + @index() + + # ESC M Reverse Index ( RI is 0x8d). + when "M" + @reverseIndex() + + # ESC % Select default/utf-8 character set. + # @ = default, G = utf-8 + when "%" + @setgLevel 0 + @setgCharset 0, BackTerminal::charsets.US + @state = State.normal + i++ + + # ESC (,),*,+,-,. Designate G0-G2 Character Set. + # <-- this seems to get all the attention + when "(", ")" , "*" , "+" , "-" , "." + switch ch + when "(" + @gcharset = 0 + when ")", "-" + @gcharset = 1 + when "*", "." + @gcharset = 2 + when "+" + @gcharset = 3 + @state = State.charset + + # Designate G3 Character Set (VT300). + # A = ISO Latin-1 Supplemental. + # Not implemented. + when "/" + @gcharset = 3 + @state = State.charset + i-- + + # ESC n + # Invoke the G2 Character Set as GL (LS2). + when "n" + @setgLevel 2 + + # ESC o + # Invoke the G3 Character Set as GL (LS3). + when "o" + @setgLevel 3 + + # ESC | + # Invoke the G3 Character Set as GR (LS3R). + when "|" + @setgLevel 3 + + # ESC } + # Invoke the G2 Character Set as GR (LS2R). + when "}" + @setgLevel 2 + + # ESC ~ + # Invoke the G1 Character Set as GR (LS1R). + when "~" + @setgLevel 1 + + # ESC 7 Save Cursor (DECSC). + when "7" + @saveCursor() + @state = State.normal + + # ESC 8 Restore Cursor (DECRC). + when "8" + @restoreCursor() + @state = State.normal + + # ESC # 3 DEC line height/width + when "#" + @state = State.normal + i++ + + # ESC H Tab Set (HTS is 0x88). + when "H" + @tabSet() + + # ESC = Application Keypad (DECPAM). + when "=" + @applicationKeypad = true + @state = State.normal + + # ESC > Normal Keypad (DECPNM). + when ">" + @applicationKeypad = false + @state = State.normal + else + @state = State.normal + console.log "Unknown ESC control:", ch + + when State.charset + switch ch + when "0" # DEC Special Character and Line Drawing Set. + cs = BackTerminal::charsets.SCLD + when "A" # UK + cs = BackTerminal::charsets.UK + when "B" # United States (USASCII). + cs = BackTerminal::charsets.US + when "4" # Dutch + cs = BackTerminal::charsets.Dutch + # Finnish + when "C", "5" + cs = BackTerminal::charsets.Finnish + when "R" # French + cs = BackTerminal::charsets.French + when "Q" # FrenchCanadian + cs = BackTerminal::charsets.FrenchCanadian + when "K" # German + cs = BackTerminal::charsets.German + when "Y" # Italian + cs = BackTerminal::charsets.Italian + # NorwegianDanish + when "E", "6" + cs = BackTerminal::charsets.NorwegianDanish + when "Z" # Spanish + cs = BackTerminal::charsets.Spanish + # Swedish + when "H", "7" + cs = BackTerminal::charsets.Swedish + when "=" # Swiss + cs = BackTerminal::charsets.Swiss + when "/" # ISOLatin (actually /A) + cs = BackTerminal::charsets.ISOLatin + i++ + else # Default + cs = BackTerminal::charsets.US + @setgCharset @gcharset, cs + @gcharset = null + @state = State.normal + + when State.osc + # OSC Ps ; Pt ST + # OSC Ps ; Pt BEL + # Set Text Parameters. + if ch is "\x1b" or ch is "\x07" + i++ if ch is "\x1b" + @params.push @currentParam + switch @params[0] + when 0, 1 , 2 + if @params[1] + @title = @params[1] + " - ƸӜƷ butterfly" + @handleTitle @title + + when 99 + # Custom escape to produce raw html + html = "
" + @params[1] + "
" + @lines[@y + @ybase][@x] = [ + @curAttr + html + ] + line = 0 + + while line < @get_html_height_in_lines(html) - 1 + @y++ + if @y > @scrollBottom + @y-- + @scroll() + line++ + @updateRange @y + + # reset colors + @params = [] + @currentParam = 0 + @state = State.normal + else + unless @params.length + if ch >= "0" and ch <= "9" + @currentParam = @currentParam * 10 + ch.charCodeAt(0) - 48 + else if ch is ";" + @params.push @currentParam + @currentParam = "" + else + @currentParam += ch + + when State.csi + # '?', '>', '!' + if ch is "?" or ch is ">" or ch is "!" + @prefix = ch + break + + # 0 - 9 + if ch >= "0" and ch <= "9" + @currentParam = @currentParam * 10 + ch.charCodeAt(0) - 48 + break + + # '$', '"', ' ', '\'' + if ch is "$" or ch is "\"" or ch is " " or ch is "'" + break + @params.push @currentParam + @currentParam = 0 + + # ';' + break if ch is ";" + @state = State.normal + switch ch + # CSI Ps A + # Cursor Up Ps Times (default = 1) (CUU). + when "A" + @cursorUp @params + + # CSI Ps B + # Cursor Down Ps Times (default = 1) (CUD). + when "B" + @cursorDown @params + + # CSI Ps C + # Cursor Forward Ps Times (default = 1) (CUF). + when "C" + @cursorForward @params + + # CSI Ps D + # Cursor Backward Ps Times (default = 1) (CUB). + when "D" + @cursorBackward @params + + # CSI Ps ; Ps H + # Cursor Position [row;column] (default = [1,1]) (CUP). + when "H" + @cursorPos @params + + # CSI Ps J Erase in Display (ED). + when "J" + @eraseInDisplay @params + + # CSI Ps K Erase in Line (EL). + when "K" + @eraseInLine @params + + # CSI Pm m Character Attributes (SGR). + when "m" + @charAttributes @params unless @prefix + + # CSI Ps n Device Status Report (DSR). + when "n" + @deviceStatus @params unless @prefix + + # CSI Ps @ + # Insert Ps (Blank) Character(s) (default = 1) (ICH). + when "@" + @insertChars @params + + # CSI Ps E + # Cursor Next Line Ps Times (default = 1) (CNL). + when "E" + @cursorNextLine @params + + # CSI Ps F + # Cursor Preceding Line Ps Times (default = 1) (CNL). + when "F" + @cursorPrecedingLine @params + + # CSI Ps G + # Cursor Character Absolute [column] (default = [row,1]) (CHA). + when "G" + @cursorCharAbsolute @params + + # CSI Ps L + # Insert Ps Line(s) (default = 1) (IL). + when "L" + @insertLines @params + + # CSI Ps M + # Delete Ps Line(s) (default = 1) (DL). + when "M" + @deleteLines @params + + # CSI Ps P + # Delete Ps Character(s) (default = 1) (DCH). + when "P" + @deleteChars @params + + # CSI Ps X + # Erase Ps Character(s) (default = 1) (ECH). + when "X" + @eraseChars @params + + # CSI Pm ` Character Position Absolute + # [column] (default = [row,1]) (HPA). + when "`" + @charPosAbsolute @params + + # 141 61 a * HPR - + # Horizontal Position Relative + when "a" + @HPositionRelative @params + + # CSI P s c + # Send Device Attributes (Primary DA). + # CSI > P s c + # Send Device Attributes (Secondary DA) + when "c" + @sendDeviceAttributes @params + + # CSI Pm d + # Line Position Absolute [row] (default = [1,column]) (VPA). + when "d" + @linePosAbsolute @params + + # 145 65 e * VPR - Vertical Position Relative + when "e" + @VPositionRelative @params + + # CSI Ps ; Ps f + # Horizontal and Vertical Position [row;column] (default = + # [1,1]) (HVP). + when "f" + @HVPosition @params + + # CSI Pm h Set Mode (SM). + # CSI ? Pm h - mouse escape codes, cursor escape codes + when "h" + @setMode @params + + # CSI Pm l Reset Mode (RM). + # CSI ? Pm l + when "l" + @resetMode @params + + # CSI Ps ; Ps r + # Set Scrolling Region [top;bottom] (default = full size of win- + # dow) (DECSTBM). + # CSI ? Pm r + when "r" + @setScrollRegion @params + + # CSI s + # Save cursor (ANSI.SYS). + when "s" + @saveCursor @params + + # CSI u + # Restore cursor (ANSI.SYS). + when "u" + @restoreCursor @params + + # CSI Ps I + # Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). + when "I" + @cursorForwardTab @params + + # CSI Ps S Scroll up Ps lines (default = 1) (SU). + when "S" + @scrollUp @params + + # CSI Ps T Scroll down Ps lines (default = 1) (SD). + # CSI Ps ; Ps ; Ps ; Ps ; Ps T + # CSI > Ps; Ps T + when "T" + @scrollDown @params if @params.length < 2 and not @prefix + + # CSI Ps Z + # Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + when "Z" + @cursorBackwardTab @params + + # CSI Ps b Repeat the preceding graphic character Ps times (REP). + when "b" + @repeatPrecedingCharacter @params + + # CSI Ps g Tab Clear (TBC). + when "g" + @tabClear @params + + # CSI > Ps p Set pointer mode. + # CSI ! p Soft terminal reset (DECSTR). + # CSI Ps$ p + # Request ANSI mode (DECRQM). + # CSI ? Ps$ p + # Request DEC private mode (DECRQM). + # CSI Ps ; Ps " p + when "p" + if @prefix is '!' + @softReset @params + + else + @error "Unknown CSI code: %s.", ch + @prefix = "" + + when State.dcs + if ch is "\x1b" or ch is "\x07" + i++ if ch is "\x1b" + switch @prefix + # User-Defined Keys (DECUDK). + when "" + break + + # Request Status String (DECRQSS). + # test: echo -e '\eP$q"p\e\\' + when "$q" + pt = @currentParam + valid = false + switch pt + + # DECSCA + when "\"q" + pt = "0\"q" + + # DECSCL + when "\"p" + pt = "61\"p" + + # DECSTBM + when "r" + pt = "" + (@scrollTop + 1) + ";" + (@scrollBottom + 1) + "r" + + # SGR + when "m" + pt = "0m" + + else + @error "Unknown DCS Pt: %s.", pt + pt = "" + + @send "\x1bP" + +valid + "$r" + pt + "\x1b\\" + + when "+q" + pt = @currentParam + valid = false + @send "\x1bP" + +valid + "+r" + pt + "\x1b\\" + + else + @error "Unknown DCS prefix: %s.", @prefix + + @currentParam = 0 + @prefix = "" + @state = State.normal + + else unless @currentParam + if not @prefix and ch isnt "$" and ch isnt "+" + @currentParam = ch + else if @prefix.length is 2 + @currentParam = ch + else + @prefix += ch + else + @currentParam += ch + + when State.ignore + # For PM and APC. + if ch is "\x1b" or ch is "\x07" + i++ if ch is "\x1b" + @state = State.normal + i++ + @updateRange @y + @refresh @refreshStart, @refreshEnd + + writeln: (data) -> + @write "#{data}\r\n" + + setgLevel: (g) -> + @glevel = g + @charset = @charsets[g] + + setgCharset: (g, charset) -> + @charsets[g] = charset + @charset = charset if @glevel is g + + resize: (cols, rows) -> + @cols = cols + @rows = rows + # resize cols + 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 < @cols + else if old_cols > @cols + i = @lines.length + while i-- + @lines[i].pop() while @lines[i].length > @cols + + @setupStops old_cols + + # resize rows + j = old_rows + if j < @rows + while j++ < @rows + @lines.push @blankLine() if @lines.length < @rows + @ybase + else if j > @rows + while j-- > @rows + @lines.pop() if @lines.length > @rows + @ybase + + # make sure the cursor stays on screen + @y = @rows - 1 if @y >= @rows + @x = @cols - 1 if @x >= @cols + + @scrollTop = 0 + @scrollBottom = @rows - 1 + + # TODO + # @refresh 0, @rows - 1 + + # it's a real nightmare trying + # to resize the original + # screen buffer. just set it + # to null for now. + @normal = null + + updateRange: (y) -> + @refreshStart = y if y < @refreshStart + @refreshEnd = y if y > @refreshEnd + + maxRange: -> + @refreshStart = 0 + @refreshEnd = @rows - 1 + + setupStops: (i) -> + if i? + i = @prevStop(i) unless @tabs[i] + else + @tabs = {} + i = 0 + while i < @cols + @tabs[i] = true + i += 8 + + prevStop: (x) -> + x = @x unless x? + while not @tabs[--x] and x > 0 + 1 + + if x >= @cols then @cols - 1 else (if x < 0 then 0 else x) + + nextStop: (x) -> + x = @x unless x? + while not @tabs[++x] and x < @cols + 1 + + if x >= @cols then @cols - 1 else (if x < 0 then 0 else x) + + eraseRight: (x, y) -> + line = @lines[@ybase + y] + # xterm + ch = [@eraseAttr(), " "] + + while x < @cols + line[x] = ch + x++ + @updateRange y + + eraseLeft: (x, y) -> + line = @lines[@ybase + y] + # xterm + ch = [@eraseAttr(), " "] + x++ + line[x] = ch while x-- + @updateRange y + + eraseLine: (y) -> + @eraseRight 0, y + + blankLine: (cur) -> + attr = (if cur then @eraseAttr() else @defAttr) + ch = [attr, " "] + line = [] + i = 0 + while i < @cols + line[i] = ch + i++ + line + + ch: (cur) -> + if cur then [@eraseAttr(), " "] else [@defAttr, " "] + + isterm: (term) -> + "#{@termName}".indexOf(term) is 0 + + handler: (data) -> + @out data + + handleTitle: (title) -> + # document.title = title + + ## ESC ## + + # ESC D Index (IND is 0x84). + index: -> + @y++ + if @y > @scrollBottom + @y-- + @scroll() + @state = State.normal + + # ESC M Reverse Index (RI is 0x8d). + reverseIndex: -> + @y-- + if @y < @scrollTop + @y++ + + # possibly move the code below to term.reverseScroll(); + # test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + # blankLine(true) is xterm/linux behavior + @lines.splice @y + @ybase, 0, @blankLine(true) + j = @rows - 1 - @scrollBottom + @lines.splice @rows - 1 + @ybase - j + 1, 1 + + # @maxRange(); + @updateRange @scrollTop + @updateRange @scrollBottom + @state = State.normal + + + # ESC c Full Reset (RIS). + reset: -> + @reset_vars() + @refresh 0, @rows - 1 + + # ESC H Tab Set (HTS is 0x88). + tabSet: -> + @tabs[@x] = true + @state = State.normal + + + ## CSI ## + + # CSI Ps A + # Cursor Up Ps Times (default = 1) (CUU). + cursorUp: (params) -> + param = params[0] + param = 1 if param < 1 + @y -= param + @y = 0 if @y < 0 + + # CSI Ps B + # Cursor Down Ps Times (default = 1) (CUD). + cursorDown: (params) -> + param = params[0] + param = 1 if param < 1 + @y += param + @y = @rows - 1 if @y >= @rows + + # CSI Ps C + # Cursor Forward Ps Times (default = 1) (CUF). + cursorForward: (params) -> + param = params[0] + param = 1 if param < 1 + @x += param + @x = @cols - 1 if @x >= @cols + + # CSI Ps D + # Cursor Backward Ps Times (default = 1) (CUB). + cursorBackward: (params) -> + param = params[0] + param = 1 if param < 1 + @x -= param + @x = 0 if @x < 0 + + # CSI Ps ; Ps H + # Cursor Position [row;column] (default = [1,1]) (CUP). + cursorPos: (params) -> + row = params[0] - 1 + if params.length >= 2 + col = params[1] - 1 + else + col = 0 + + if row < 0 + row = 0 + else + row = @rows - 1 if row >= @rows + + if col < 0 + col = 0 + else + col = @cols - 1 if col >= @cols + + @x = col + @y = row + + + # CSI Ps J Erase in Display (ED). + # Ps = 0 -> Erase Below (default). + # Ps = 1 -> Erase Above. + # Ps = 2 -> Erase All. + # Ps = 3 -> Erase Saved Lines (xterm). + # CSI ? Ps J + # Erase in Display (DECSED). + # Ps = 0 -> Selective Erase Below (default). + # Ps = 1 -> Selective Erase Above. + # Ps = 2 -> Selective Erase All. + eraseInDisplay: (params) -> + switch params[0] + when 0 + @eraseRight @x, @y + j = @y + 1 + while j < @rows + @eraseLine j + j++ + when 1 + @eraseLeft @x, @y + j = @y + @eraseLine j while j-- + when 2 + j = @rows + @eraseLine j while j-- + + + # CSI Ps K Erase in Line (EL). + # Ps = 0 -> Erase to Right (default). + # Ps = 1 -> Erase to Left. + # Ps = 2 -> Erase All. + # CSI ? Ps K + # Erase in Line (DECSEL). + # Ps = 0 -> Selective Erase to Right (default). + # Ps = 1 -> Selective Erase to Left. + # Ps = 2 -> Selective Erase All. + eraseInLine: (params) -> + switch params[0] + when 0 + @eraseRight @x, @y + when 1 + @eraseLeft @x, @y + when 2 + @eraseLine @y + + + # CSI Pm m Character Attributes (SGR). + # Ps = 0 -> Normal (default). + # Ps = 1 -> Bold. + # Ps = 4 -> Underlined. + # Ps = 5 -> Blink (appears as Bold). + # Ps = 7 -> Inverse. + # Ps = 8 -> Invisible, i.e., hidden (VT300). + # Ps = 2 2 -> Normal (neither bold nor faint). + # Ps = 2 4 -> Not underlined. + # Ps = 2 5 -> Steady (not blinking). + # Ps = 2 7 -> Positive (not inverse). + # Ps = 2 8 -> Visible, i.e., not hidden (VT300). + # Ps = 3 0 -> Set foreground color to Black. + # Ps = 3 1 -> Set foreground color to Red. + # Ps = 3 2 -> Set foreground color to Green. + # Ps = 3 3 -> Set foreground color to Yellow. + # Ps = 3 4 -> Set foreground color to Blue. + # Ps = 3 5 -> Set foreground color to Magenta. + # Ps = 3 6 -> Set foreground color to Cyan. + # Ps = 3 7 -> Set foreground color to White. + # Ps = 3 9 -> Set foreground color to default (original). + # Ps = 4 0 -> Set background color to Black. + # Ps = 4 1 -> Set background color to Red. + # Ps = 4 2 -> Set background color to Green. + # Ps = 4 3 -> Set background color to Yellow. + # Ps = 4 4 -> Set background color to Blue. + # Ps = 4 5 -> Set background color to Magenta. + # Ps = 4 6 -> Set background color to Cyan. + # Ps = 4 7 -> Set background color to White. + # Ps = 4 9 -> Set background color to default (original). + + # If 16-color support is compiled, the following apply. Assume + # that xterm's resources are set so that the ISO color codes are + # the first 8 of a set of 16. Then the aixterm colors are the + # bright versions of the ISO colors: + # Ps = 9 0 -> Set foreground color to Black. + # Ps = 9 1 -> Set foreground color to Red. + # Ps = 9 2 -> Set foreground color to Green. + # Ps = 9 3 -> Set foreground color to Yellow. + # Ps = 9 4 -> Set foreground color to Blue. + # Ps = 9 5 -> Set foreground color to Magenta. + # Ps = 9 6 -> Set foreground color to Cyan. + # Ps = 9 7 -> Set foreground color to White. + # Ps = 1 0 0 -> Set background color to Black. + # Ps = 1 0 1 -> Set background color to Red. + # Ps = 1 0 2 -> Set background color to Green. + # Ps = 1 0 3 -> Set background color to Yellow. + # Ps = 1 0 4 -> Set background color to Blue. + # Ps = 1 0 5 -> Set background color to Magenta. + # Ps = 1 0 6 -> Set background color to Cyan. + # Ps = 1 0 7 -> Set background color to White. + + # If xterm is compiled with the 16-color support disabled, it + # supports the following, from rxvt: + # Ps = 1 0 0 -> Set foreground and background color to + # default. + + # If 88- or 256-color support is compiled, the following apply. + # Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second + # Ps. + # Ps = 4 8 ; 5 ; Ps -> Set background color to the second + # Ps. + charAttributes: (params) -> + # Optimize a single SGR0. + if params.length is 1 and params[0] is 0 + @curAttr = @defAttr + return + flags = @curAttr >> 18 + + fg = (@curAttr >> 9) & 0x1ff + bg = @curAttr & 0x1ff + + l = params.length + i = 0 + while i < l + p = params[i] + if p >= 30 and p <= 37 + # fg color 8 + fg = p - 30 + else if p >= 40 and p <= 47 + # bg color 8 + bg = p - 40 + else if p >= 90 and p <= 97 + # fg color 16 + p += 8 + fg = p - 90 + else if p >= 100 and p <= 107 + # bg color 16 + p += 8 + bg = p - 100 + else if p is 0 + # default + flags = @defAttr >> 18 + fg = (@defAttr >> 9) & 0x1ff + bg = @defAttr & 0x1ff + + # flags = 0; + # fg = 0x1ff; + # bg = 0x1ff; + else if p is 1 + # bold text + flags |= 1 + else if p is 4 + # underlined text + flags |= 2 + else if p is 5 + # blink + flags |= 4 + else if p is 7 + # inverse and positive + # test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' + flags |= 8 + else if p is 8 + # invisible + flags |= 16 + else if p is 22 + # not bold + flags &= ~1 + else if p is 24 + # not underlined + flags &= ~2 + else if p is 25 + # not blink + flags &= ~4 + else if p is 27 + # not inverse + flags &= ~8 + else if p is 28 + # not invisible + flags &= ~16 + else if p is 39 + # reset fg + fg = (@defAttr >> 9) & 0x1ff + else if p is 49 + # reset bg + bg = @defAttr & 0x1ff + else if p is 38 + if params[i + 1] is 2 + # fg color 2^24 + i += 2 + fg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff + i += 2 + else if params[i + 1] is 5 + # fg color 256 + i += 2 + fg = params[i] & 0xff + else if p is 48 + if params[i + 1] is 2 + # bg color 2^24 + i += 2 + bg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff + i += 2 + else if params[i + 1] is 5 + # bg color 256 + i += 2 + bg = params[i] & 0xff + else if p is 100 + # reset fg/bg + fg = (@defAttr >> 9) & 0x1ff + bg = @defAttr & 0x1ff + else + @error "Unknown SGR attribute: %d.", p + i++ + @curAttr = (flags << 18) | (fg << 9) | bg + + + # CSI Ps n Device Status Report (DSR). + # Ps = 5 -> Status Report. Result (``OK'') is + # CSI 0 n + # Ps = 6 -> Report Cursor Position (CPR) [row;column]. + # Result is + # CSI r ; c R + # CSI ? Ps n + # Device Status Report (DSR, DEC-specific). + # Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI + # ? r ; c R (assumes page is zero). + # Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). + # or CSI ? 1 1 n (not ready). + # Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) + # or CSI ? 2 1 n (locked). + # Ps = 2 6 -> Report Keyboard status as + # CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). + # The last two parameters apply to VT400 & up, and denote key- + # board ready and LK01 respectively. + # Ps = 5 3 -> Report Locator status as + # CSI ? 5 3 n Locator available, if compiled-in, or + # CSI ? 5 0 n No Locator, if not. + deviceStatus: (params) -> + unless @prefix + switch params[0] + when 5 + # status report + @send "\x1b[0n" + when 6 + # cursor position + @send "\x1b[" + (@y + 1) + ";" + (@x + 1) + "R" + else if @prefix is "?" + # modern xterm doesnt seem to + # respond to any of these except ?6, 6, and 5 + if params[0] is 6 + # cursor position + @send "\x1b[?" + (@y + 1) + ";" + (@x + 1) + "R" + + + ## Additions ## + + # CSI Ps @ + # Insert Ps (Blank) Character(s) (default = 1) (ICH). + insertChars: (params) -> + param = params[0] + param = 1 if param < 1 + row = @y + @ybase + j = @x + # xterm + ch = [@eraseAttr(), " "] + while param-- and j < @cols + @lines[row].splice j++, 0, ch + @lines[row].pop() + + + # CSI Ps E + # Cursor Next Line Ps Times (default = 1) (CNL). + # same as CSI Ps B ? + cursorNextLine: (params) -> + param = params[0] + param = 1 if param < 1 + @y += param + @y = @rows - 1 if @y >= @rows + @x = 0 + + + # CSI Ps F + # Cursor Preceding Line Ps Times (default = 1) (CNL). + # reuse CSI Ps A ? + cursorPrecedingLine: (params) -> + param = params[0] + param = 1 if param < 1 + @y -= param + @y = 0 if @y < 0 + @x = 0 + + + # CSI Ps G + # Cursor Character Absolute [column] (default = [row,1]) (CHA). + cursorCharAbsolute: (params) -> + param = params[0] + param = 1 if param < 1 + @x = param - 1 + + # CSI Ps L + # Insert Ps Line(s) (default = 1) (IL). + insertLines: (params) -> + param = params[0] + param = 1 if param < 1 + row = @y + @ybase + j = @rows - 1 - @scrollBottom + j = @rows - 1 + @ybase - j + 1 + while param-- + # test: echo -e '\e[44m\e[1L\e[0m' + # blankLine(true) - xterm/linux behavior + @lines.splice row, 0, @blankLine(true) + @lines.splice j, 1 + + @updateRange @y + @updateRange @scrollBottom + + + # CSI Ps M + # Delete Ps Line(s) (default = 1) (DL). + deleteLines: (params) -> + param = params[0] + param = 1 if param < 1 + row = @y + @ybase + j = @rows - 1 - @scrollBottom + j = @rows - 1 + @ybase - j + while param-- + # test: echo -e '\e[44m\e[1M\e[0m' + # blankLine(true) - xterm/linux behavior + @lines.splice j + 1, 0, @blankLine(true) + @lines.splice row, 1 + + @updateRange @y + @updateRange @scrollBottom + + + # CSI Ps P + # Delete Ps Character(s) (default = 1) (DCH). + deleteChars: (params) -> + param = params[0] + param = 1 if param < 1 + row = @y + @ybase + # xterm + ch = [@eraseAttr(), " "] + while param-- + @lines[row].splice @x, 1 + @lines[row].push ch + + + # CSI Ps X + # Erase Ps Character(s) (default = 1) (ECH). + eraseChars: (params) -> + param = params[0] + param = 1 if param < 1 + row = @y + @ybase + j = @x + # xterm + ch = [@eraseAttr(), " "] + @lines[row][j++] = ch while param-- and j < @cols + + + # CSI Pm ` Character Position Absolute + # [column] (default = [row,1]) (HPA). + charPosAbsolute: (params) -> + param = params[0] + param = 1 if param < 1 + @x = param - 1 + @x = @cols - 1 if @x >= @cols + + + # 141 61 a * HPR - + # Horizontal Position Relative + # reuse CSI Ps C ? + HPositionRelative: (params) -> + param = params[0] + param = 1 if param < 1 + @x += param + @x = @cols - 1 if @x >= @cols + + + # CSI Ps c Send Device Attributes (Primary DA). + # Ps = 0 or omitted -> request attributes from terminal. The + # response depends on the decTerminalID resource setting. + # -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') + # -> CSI ? 1 ; 0 c (``VT101 with No Options'') + # -> CSI ? 6 c (``VT102'') + # -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') + # The VT100-style response parameters do not mean anything by + # themselves. VT220 parameters do, telling the host what fea- + # tures the terminal supports: + # Ps = 1 -> 132-columns. + # Ps = 2 -> Printer. + # Ps = 6 -> Selective erase. + # Ps = 8 -> User-defined keys. + # Ps = 9 -> National replacement character sets. + # Ps = 1 5 -> Technical characters. + # Ps = 2 2 -> ANSI color, e.g., VT525. + # Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). + # CSI > Ps c + # Send Device Attributes (Secondary DA). + # Ps = 0 or omitted -> request the terminal's identification + # code. The response depends on the decTerminalID resource set- + # ting. It should apply only to VT220 and up, but xterm extends + # this to VT100. + # -> CSI > Pp ; Pv ; Pc c + # where Pp denotes the terminal type + # Pp = 0 -> ``VT100''. + # Pp = 1 -> ``VT220''. + # and Pv is the firmware version (for xterm, this was originally + # the XFree86 patch number, starting with 95). In a DEC termi- + # nal, Pc indicates the ROM cartridge registration number and is + # always zero. + # More information: + # xterm/charproc.c - line 2012, for more information. + # vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) + sendDeviceAttributes: (params) -> + return if params[0] > 0 + unless @prefix + if @isterm("xterm") or @isterm("rxvt-unicode") or @isterm("screen") + @send "\x1b[?1;2c" + else @send "\x1b[?6c" if @isterm("linux") + + else if @prefix is ">" + # xterm and urxvt + # seem to spit this + # out around ~370 times (?). + if @isterm("xterm") + @send "\x1b[>0;276;0c" + else if @isterm("rxvt-unicode") + @send "\x1b[>85;95;0c" + else if @isterm("linux") + # not supported by linux console. + # linux console echoes parameters. + @send params[0] + "c" + else @send "\x1b[>83;40003;0c" if @isterm("screen") + + + # CSI Pm d + # Line Position Absolute [row] (default = [1,column]) (VPA). + linePosAbsolute: (params) -> + param = params[0] + param = 1 if param < 1 + @y = param - 1 + @y = @rows - 1 if @y >= @rows + + + # 145 65 e * VPR - Vertical Position Relative + # reuse CSI Ps B ? + VPositionRelative: (params) -> + param = params[0] + param = 1 if param < 1 + @y += param + @y = @rows - 1 if @y >= @rows + + + # CSI Ps ; Ps f + # Horizontal and Vertical Position [row;column] (default = + # [1,1]) (HVP). + HVPosition: (params) -> + params[0] = 1 if params[0] < 1 + params[1] = 1 if params[1] < 1 + @y = params[0] - 1 + @y = @rows - 1 if @y >= @rows + @x = params[1] - 1 + @x = @cols - 1 if @x >= @cols + + + # CSI Pm h Set Mode (SM). + # Ps = 2 -> Keyboard Action Mode (AM). + # Ps = 4 -> Insert Mode (IRM). + # Ps = 1 2 -> Send/receive (SRM). + # Ps = 2 0 -> Automatic Newline (LNM). + # CSI ? Pm h + # DEC Private Mode Set (DECSET). + # Ps = 1 -> Application Cursor Keys (DECCKM). + # Ps = 2 -> Designate USASCII for character sets G0-G3 + # (DECANM), and set VT100 mode. + # Ps = 3 -> 132 Column Mode (DECCOLM). + # Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). + # Ps = 5 -> Reverse Video (DECSCNM). + # Ps = 6 -> Origin Mode (DECOM). + # Ps = 7 -> Wraparound Mode (DECAWM). + # Ps = 8 -> Auto-repeat Keys (DECARM). + # Ps = 9 -> Send Mouse X & Y on button press. See the sec- + # tion Mouse Tracking. + # Ps = 1 0 -> Show toolbar (rxvt). + # Ps = 1 2 -> Start Blinking Cursor (att610). + # Ps = 1 8 -> Print form feed (DECPFF). + # Ps = 1 9 -> Set print extent to full screen (DECPEX). + # Ps = 2 5 -> Show Cursor (DECTCEM). + # Ps = 3 0 -> Show scrollbar (rxvt). + # Ps = 3 5 -> Enable font-shifting functions (rxvt). + # Ps = 3 8 -> Enter Tektronix Mode (DECTEK). + # Ps = 4 0 -> Allow 80 -> 132 Mode. + # Ps = 4 1 -> more(1) fix (see curses resource). + # Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- + # RCM). + # Ps = 4 4 -> Turn On Margin Bell. + # Ps = 4 5 -> Reverse-wraparound Mode. + # Ps = 4 6 -> Start Logging. This is normally disabled by a + # compile-time option. + # Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- + # abled by the titeInhibit resource). + # Ps = 6 6 -> Application keypad (DECNKM). + # Ps = 6 7 -> Backarrow key sends backspace (DECBKM). + # Ps = 1 0 0 0 -> Send Mouse X & Y on button press and + # release. See the section Mouse Tracking. + # Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. + # Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. + # Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. + # Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. + # Ps = 1 0 0 5 -> Enable Extended Mouse Mode. + # Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). + # Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). + # Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. + # (enables the eightBitInput resource). + # Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- + # Lock keys. (This enables the numLock resource). + # Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This + # enables the metaSendsEscape resource). + # Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete + # key. + # Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This + # enables the altSendsEscape resource). + # Ps = 1 0 4 0 -> Keep selection even if not highlighted. + # (This enables the keepSelection resource). + # Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables + # the selectToClipboard resource). + # Ps = 1 0 4 2 -> Enable Urgency window manager hint when + # Control-G is received. (This enables the bellIsUrgent + # resource). + # Ps = 1 0 4 3 -> Enable raising of the window when Control-G + # is received. (enables the popOnBell resource). + # Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be + # disabled by the titeInhibit resource). + # Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- + # abled by the titeInhibit resource). + # Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate + # Screen Buffer, clearing it first. (This may be disabled by + # the titeInhibit resource). This combines the effects of the 1 + # 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based + # applications rather than the 4 7 mode. + # Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. + # Ps = 1 0 5 1 -> Set Sun function-key mode. + # Ps = 1 0 5 2 -> Set HP function-key mode. + # Ps = 1 0 5 3 -> Set SCO function-key mode. + # Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). + # Ps = 1 0 6 1 -> Set VT220 keyboard emulation. + # Ps = 2 0 0 4 -> Set bracketed paste mode. + # Modes: + # http://vt100.net/docs/vt220-rm/chapter4.html + setMode: (params) -> + if typeof params is "object" + l = params.length + i = 0 + while i < l + @setMode params[i] + i++ + return + if @prefix is "?" + switch params + when 1 + @applicationCursor = true + when 2 + @setgCharset 0, BackTerminal::charsets.US + @setgCharset 1, BackTerminal::charsets.US + @setgCharset 2, BackTerminal::charsets.US + @setgCharset 3, BackTerminal::charsets.US + # set VT100 mode here + when 3 # 132 col mode + @savedCols = @cols + @resize 132, @rows + when 6 + @originMode = true + when 7 + @wraparoundMode = true + when 66 + @applicationKeypad = true + # X10 Mouse + # no release, no motion, no wheel, no modifiers. + when 9, 1000, 1002, 1003 # any event mouse + # any event - sends motion events, + # even if there is no button held down. + @x10Mouse = params is 9 + @vt200Mouse = params is 1000 + @normalMouse = params > 1000 + @mouseEvents = true + @element.style.cursor = "default" + when 1004 # send focusin/focusout events + # focusin: ^[[I + # focusout: ^[[O + @sendFocus = true + when 1005 # utf8 ext mode mouse + @utfMouse = true + # for wide terminals + # simply encodes large values as utf8 characters + when 1006 # sgr ext mode mouse + @sgrMouse = true + # for wide terminals + # does not add 32 to fields + # press: ^[[ Keyboard Action Mode (AM). + # Ps = 4 -> Replace Mode (IRM). + # Ps = 1 2 -> Send/receive (SRM). + # Ps = 2 0 -> Normal Linefeed (LNM). + # CSI ? Pm l + # DEC Private Mode Reset (DECRST). + # Ps = 1 -> Normal Cursor Keys (DECCKM). + # Ps = 2 -> Designate VT52 mode (DECANM). + # Ps = 3 -> 80 Column Mode (DECCOLM). + # Ps = 4 -> Jump (Fast) Scroll (DECSCLM). + # Ps = 5 -> Normal Video (DECSCNM). + # Ps = 6 -> Normal Cursor Mode (DECOM). + # Ps = 7 -> No Wraparound Mode (DECAWM). + # Ps = 8 -> No Auto-repeat Keys (DECARM). + # Ps = 9 -> Don't send Mouse X & Y on button press. + # Ps = 1 0 -> Hide toolbar (rxvt). + # Ps = 1 2 -> Stop Blinking Cursor (att610). + # Ps = 1 8 -> Don't print form feed (DECPFF). + # Ps = 1 9 -> Limit print to scrolling region (DECPEX). + # Ps = 2 5 -> Hide Cursor (DECTCEM). + # Ps = 3 0 -> Don't show scrollbar (rxvt). + # Ps = 3 5 -> Disable font-shifting functions (rxvt). + # Ps = 4 0 -> Disallow 80 -> 132 Mode. + # Ps = 4 1 -> No more(1) fix (see curses resource). + # Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- + # NRCM). + # Ps = 4 4 -> Turn Off Margin Bell. + # Ps = 4 5 -> No Reverse-wraparound Mode. + # Ps = 4 6 -> Stop Logging. (This is normally disabled by a + # compile-time option). + # Ps = 4 7 -> Use Normal Screen Buffer. + # Ps = 6 6 -> Numeric keypad (DECNKM). + # Ps = 6 7 -> Backarrow key sends delete (DECBKM). + # Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and + # release. See the section Mouse Tracking. + # Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. + # Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. + # Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. + # Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. + # Ps = 1 0 0 5 -> Disable Extended Mouse Mode. + # Ps = 1 0 1 0 -> Don't scroll to bottom on tty output + # (rxvt). + # Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). + # Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables + # the eightBitInput resource). + # Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- + # Lock keys. (This disables the numLock resource). + # Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. + # (This disables the metaSendsEscape resource). + # Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad + # Delete key. + # Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. + # (This disables the altSendsEscape resource). + # Ps = 1 0 4 0 -> Do not keep selection when not highlighted. + # (This disables the keepSelection resource). + # Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables + # the selectToClipboard resource). + # Ps = 1 0 4 2 -> Disable Urgency window manager hint when + # Control-G is received. (This disables the bellIsUrgent + # resource). + # Ps = 1 0 4 3 -> Disable raising of the window when Control- + # G is received. (This disables the popOnBell resource). + # Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen + # first if in the Alternate Screen. (This may be disabled by + # the titeInhibit resource). + # Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be + # disabled by the titeInhibit resource). + # Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor + # as in DECRC. (This may be disabled by the titeInhibit + # resource). This combines the effects of the 1 0 4 7 and 1 0 + # 4 8 modes. Use this with terminfo-based applications rather + # than the 4 7 mode. + # Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. + # Ps = 1 0 5 1 -> Reset Sun function-key mode. + # Ps = 1 0 5 2 -> Reset HP function-key mode. + # Ps = 1 0 5 3 -> Reset SCO function-key mode. + # Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). + # Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. + # Ps = 2 0 0 4 -> Reset bracketed paste mode. + resetMode: (params) -> + if typeof params is "object" + l = params.length + i = 0 + while i < l + @resetMode params[i] + i++ + return + + if @prefix is "?" + switch params + when 1 + @applicationCursor = false + when 3 + @resize @savedCols, @rows if @cols is 132 and @savedCols + delete @savedCols + when 6 + @originMode = false + when 7 + @wraparoundMode = false + when 66 + @applicationKeypad = false + when 9, 1000, 1002 , 1003 # any event mouse + @x10Mouse = false + @vt200Mouse = false + @normalMouse = false + @mouseEvents = false + @element.style.cursor = "" + when 1004 # send focusin/focusout events + @sendFocus = false + when 1005 # utf8 ext mode mouse + @utfMouse = false + when 1006 # sgr ext mode mouse + @sgrMouse = false + when 1015 # urxvt ext mode mouse + @urxvtMouse = false + when 25 # hide cursor + @cursorHidden = true + when 1049, 47, 1047 # normal screen buffer - clearing it first + if @normal + @lines = @normal.lines + @ybase = @normal.ybase + @ydisp = @normal.ydisp + @x = @normal.x + @y = @normal.y + @scrollTop = @normal.scrollTop + @scrollBottom = @normal.scrollBottom + @tabs = @normal.tabs + @normal = null + @refresh 0, @rows - 1 + @showCursor() + + + # CSI Ps ; Ps r + # Set Scrolling Region [top;bottom] (default = full size of win- + # dow) (DECSTBM). + # CSI ? Pm r + setScrollRegion: (params) -> + return if @prefix + @scrollTop = (params[0] or 1) - 1 + @scrollBottom = (params[1] or @rows) - 1 + @x = 0 + @y = 0 + + + # CSI s + # Save cursor (ANSI.SYS). + saveCursor: (params) -> + @savedX = @x + @savedY = @y + + + # CSI u + # Restore cursor (ANSI.SYS). + restoreCursor: (params) -> + @x = @savedX or 0 + @y = @savedY or 0 + + ## Lesser Used ## + + # CSI Ps I + # Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). + cursorForwardTab: (params) -> + param = params[0] or 1 + @x = @nextStop() while param-- + + + # CSI Ps S Scroll up Ps lines (default = 1) (SU). + scrollUp: (params) -> + param = params[0] or 1 + while param-- + @lines.splice @ybase + @scrollTop, 1 + @lines.splice @ybase + @scrollBottom, 0, @blankLine() + + @updateRange @scrollTop + @updateRange @scrollBottom + + + # CSI Ps T Scroll down Ps lines (default = 1) (SD). + scrollDown: (params) -> + param = params[0] or 1 + while param-- + @lines.splice @ybase + @scrollBottom, 1 + @lines.splice @ybase + @scrollTop, 0, @blankLine() + + @updateRange @scrollTop + @updateRange @scrollBottom + + + # CSI Ps ; Ps ; Ps ; Ps ; Ps T + # Initiate highlight mouse tracking. Parameters are + # [func;startx;starty;firstrow;lastrow]. See the section Mouse + # Tracking. + initMouseTracking: (params) -> + + # Relevant: DECSET 1001 + + # CSI > Ps; Ps T + # Reset one or more features of the title modes to the default + # value. Normally, "reset" disables the feature. It is possi- + # ble to disable the ability to reset features by compiling a + # different default for the title modes into xterm. + # Ps = 0 -> Do not set window/icon labels using hexadecimal. + # Ps = 1 -> Do not query window/icon labels using hexadeci- + # mal. + # Ps = 2 -> Do not set window/icon labels using UTF-8. + # Ps = 3 -> Do not query window/icon labels using UTF-8. + # (See discussion of "Title Modes"). + resetTitleModes: (params) -> + + + # CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + cursorBackwardTab: (params) -> + param = params[0] or 1 + @x = @prevStop() while param-- + + + # CSI Ps b Repeat the preceding graphic character Ps times (REP). + repeatPrecedingCharacter: (params) -> + param = params[0] or 1 + line = @lines[@ybase + @y] + ch = line[@x - 1] or [@defAttr, " "] + line[@x++] = ch while param-- + + + # CSI Ps g Tab Clear (TBC). + # Ps = 0 -> Clear Current Column (default). + # Ps = 3 -> Clear All. + # Potentially: + # Ps = 2 -> Clear Stops on Line. + # http://vt100.net/annarbor/aaa-ug/section6.html + tabClear: (params) -> + param = params[0] + if param <= 0 + delete @tabs[@x] + else + @tabs = {} if param is 3 + + # CSI Pm i Media Copy (MC). + # Ps = 0 -> Print screen (default). + # Ps = 4 -> Turn off printer controller mode. + # Ps = 5 -> Turn on printer controller mode. + # CSI ? Pm i + # Media Copy (MC, DEC-specific). + # Ps = 1 -> Print line containing cursor. + # Ps = 4 -> Turn off autoprint mode. + # Ps = 5 -> Turn on autoprint mode. + # Ps = 1 0 -> Print composed display, ignores DECPEX. + # Ps = 1 1 -> Print all pages. + mediaCopy: (params) -> + + + # CSI > Ps; Ps m + # Set or reset resource-values used by xterm to decide whether + # to construct escape sequences holding information about the + # modifiers pressed with a given key. The first parameter iden- + # tifies the resource to set/reset. The second parameter is the + # value to assign to the resource. If the second parameter is + # omitted, the resource is reset to its initial value. + # Ps = 1 -> modifyCursorKeys. + # Ps = 2 -> modifyFunctionKeys. + # Ps = 4 -> modifyOtherKeys. + # If no parameters are given, all resources are reset to their + # initial values. + setResources: (params) -> + + + # CSI > Ps n + # Disable modifiers which may be enabled via the CSI > Ps; Ps m + # sequence. This corresponds to a resource value of "-1", which + # cannot be set with the other sequence. The parameter identi- + # fies the resource to be disabled: + # Ps = 1 -> modifyCursorKeys. + # Ps = 2 -> modifyFunctionKeys. + # Ps = 4 -> modifyOtherKeys. + # If the parameter is omitted, modifyFunctionKeys is disabled. + # When modifyFunctionKeys is disabled, xterm uses the modifier + # keys to make an extended sequence of functions rather than + # adding a parameter to each function key to denote the modi- + # fiers. + disableModifiers: (params) -> + + + # CSI > Ps p + # Set resource value pointerMode. This is used by xterm to + # decide whether to hide the pointer cursor as the user types. + # Valid values for the parameter: + # Ps = 0 -> never hide the pointer. + # Ps = 1 -> hide if the mouse tracking mode is not enabled. + # Ps = 2 -> always hide the pointer. If no parameter is + # given, xterm uses the default, which is 1 . + setPointerMode: (params) -> + + + # CSI ! p Soft terminal reset (DECSTR). + # http://vt100.net/docs/vt220-rm/table4-10.html + softReset: (params) -> + @cursorHidden = false + @insertMode = false + @originMode = false + @wraparoundMode = false # autowrap + @applicationKeypad = false # ? + @applicationCursor = false + @scrollTop = 0 + @scrollBottom = @rows - 1 + @curAttr = @defAttr + @x = @y = 0 # ? + @charset = null + @glevel = 0 # ?? + @charsets = [null] # ?? + + + # CSI Ps$ p + # Request ANSI mode (DECRQM). For VT300 and up, reply is + # CSI Ps; Pm$ y + # where Ps is the mode number as in RM, and Pm is the mode + # value: + # 0 - not recognized + # 1 - set + # 2 - reset + # 3 - permanently set + # 4 - permanently reset + requestAnsiMode: (params) -> + + + # CSI ? Ps$ p + # Request DEC private mode (DECRQM). For VT300 and up, reply is + # CSI ? Ps; Pm$ p + # where Ps is the mode number as in DECSET, Pm is the mode value + # as in the ANSI DECRQM. + requestPrivateMode: (params) -> + + + # CSI Ps ; Ps " p + # Set conformance level (DECSCL). Valid values for the first + # parameter: + # Ps = 6 1 -> VT100. + # Ps = 6 2 -> VT200. + # Ps = 6 3 -> VT300. + # Valid values for the second parameter: + # Ps = 0 -> 8-bit controls. + # Ps = 1 -> 7-bit controls (always set for VT100). + # Ps = 2 -> 8-bit controls. + setConformanceLevel: (params) -> + + + # CSI Ps q Load LEDs (DECLL). + # Ps = 0 -> Clear all LEDS (default). + # Ps = 1 -> Light Num Lock. + # Ps = 2 -> Light Caps Lock. + # Ps = 3 -> Light Scroll Lock. + # Ps = 2 1 -> Extinguish Num Lock. + # Ps = 2 2 -> Extinguish Caps Lock. + # Ps = 2 3 -> Extinguish Scroll Lock. + loadLEDs: (params) -> + + + # CSI Ps SP q + # Set cursor style (DECSCUSR, VT520). + # Ps = 0 -> blinking block. + # Ps = 1 -> blinking block (default). + # Ps = 2 -> steady block. + # Ps = 3 -> blinking underline. + # Ps = 4 -> steady underline. + setCursorStyle: (params) -> + + + # CSI Ps " q + # Select character protection attribute (DECSCA). Valid values + # for the parameter: + # Ps = 0 -> DECSED and DECSEL can erase (default). + # Ps = 1 -> DECSED and DECSEL cannot erase. + # Ps = 2 -> DECSED and DECSEL can erase. + setCharProtectionAttr: (params) -> + + + # CSI ? Pm r + # Restore DEC Private Mode Values. The value of Ps previously + # saved is restored. Ps values are the same as for DECSET. + restorePrivateValues: (params) -> + + + # CSI Pt; Pl; Pb; Pr; Ps$ r + # Change Attributes in Rectangular Area (DECCARA), VT400 and up. + # Pt; Pl; Pb; Pr denotes the rectangle. + # Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. + # NOTE: xterm doesn't enable this code by default. + setAttrInRectangle: (params) -> + t = params[0] + l = params[1] + b = params[2] + r = params[3] + attr = params[4] + while t < b + 1 + line = @lines[@ybase + t] + i = l + while i < r + line[i] = [attr, line[i][1]] + i++ + t++ + + @updateRange params[0] + @updateRange params[2] + + + # CSI ? Pm s + # Save DEC Private Mode Values. Ps values are the same as for + # DECSET. + savePrivateValues: (params) -> + + + # CSI Ps ; Ps ; Ps t + # Window manipulation (from dtterm, as well as extensions). + # These controls may be disabled using the allowWindowOps + # resource. Valid values for the first (and any additional + # parameters) are: + # Ps = 1 -> De-iconify window. + # Ps = 2 -> Iconify window. + # Ps = 3 ; x ; y -> Move window to [x, y]. + # Ps = 4 ; height ; width -> Resize the xterm window to + # height and width in pixels. + # Ps = 5 -> Raise the xterm window to the front of the stack- + # ing order. + # Ps = 6 -> Lower the xterm window to the bottom of the + # stacking order. + # Ps = 7 -> Refresh the xterm window. + # Ps = 8 ; height ; width -> Resize the text area to + # [height;width] in characters. + # Ps = 9 ; 0 -> Restore maximized window. + # Ps = 9 ; 1 -> Maximize window (i.e., resize to screen + # size). + # Ps = 1 0 ; 0 -> Undo full-screen mode. + # Ps = 1 0 ; 1 -> Change to full-screen. + # Ps = 1 1 -> Report xterm window state. If the xterm window + # is open (non-iconified), it returns CSI 1 t . If the xterm + # window is iconified, it returns CSI 2 t . + # Ps = 1 3 -> Report xterm window position. Result is CSI 3 + # ; x ; y t + # Ps = 1 4 -> Report xterm window in pixels. Result is CSI + # 4 ; height ; width t + # Ps = 1 8 -> Report the size of the text area in characters. + # Result is CSI 8 ; height ; width t + # Ps = 1 9 -> Report the size of the screen in characters. + # Result is CSI 9 ; height ; width t + # Ps = 2 0 -> Report xterm window's icon label. Result is + # OSC L label ST + # Ps = 2 1 -> Report xterm window's title. Result is OSC l + # label ST + # Ps = 2 2 ; 0 -> Save xterm icon and window title on + # stack. + # Ps = 2 2 ; 1 -> Save xterm icon title on stack. + # Ps = 2 2 ; 2 -> Save xterm window title on stack. + # Ps = 2 3 ; 0 -> Restore xterm icon and window title from + # stack. + # Ps = 2 3 ; 1 -> Restore xterm icon title from stack. + # Ps = 2 3 ; 2 -> Restore xterm window title from stack. + # Ps >= 2 4 -> Resize to Ps lines (DECSLPP). + manipulateWindow: (params) -> + + + # CSI Pt; Pl; Pb; Pr; Ps$ t + # Reverse Attributes in Rectangular Area (DECRARA), VT400 and + # up. + # Pt; Pl; Pb; Pr denotes the rectangle. + # Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7. + # NOTE: xterm doesn't enable this code by default. + reverseAttrInRectangle: (params) -> + + + # CSI > Ps; Ps t + # Set one or more features of the title modes. Each parameter + # enables a single feature. + # Ps = 0 -> Set window/icon labels using hexadecimal. + # Ps = 1 -> Query window/icon labels using hexadecimal. + # Ps = 2 -> Set window/icon labels using UTF-8. + # Ps = 3 -> Query window/icon labels using UTF-8. (See dis- + # cussion of "Title Modes") + setTitleModeFeature: (params) -> + + + # CSI Ps SP t + # Set warning-bell volume (DECSWBV, VT520). + # Ps = 0 or 1 -> off. + # Ps = 2 , 3 or 4 -> low. + # Ps = 5 , 6 , 7 , or 8 -> high. + setWarningBellVolume: (params) -> + + + # CSI Ps SP u + # Set margin-bell volume (DECSMBV, VT520). + # Ps = 1 -> off. + # Ps = 2 , 3 or 4 -> low. + # Ps = 0 , 5 , 6 , 7 , or 8 -> high. + setMarginBellVolume: (params) -> + + + # CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v + # Copy Rectangular Area (DECCRA, VT400 and up). + # Pt; Pl; Pb; Pr denotes the rectangle. + # Pp denotes the source page. + # Pt; Pl denotes the target location. + # Pp denotes the target page. + # NOTE: xterm doesn't enable this code by default. + copyRectangle: (params) -> + + + # CSI Pt ; Pl ; Pb ; Pr ' w + # Enable Filter Rectangle (DECEFR), VT420 and up. + # Parameters are [top;left;bottom;right]. + # Defines the coordinates of a filter rectangle and activates + # it. Anytime the locator is detected outside of the filter + # rectangle, an outside rectangle event is generated and the + # rectangle is disabled. Filter rectangles are always treated + # as "one-shot" events. Any parameters that are omitted default + # to the current locator position. If all parameters are omit- + # ted, any locator motion will be reported. DECELR always can- + # cels any prevous rectangle definition. + enableFilterRectangle: (params) -> + + + # CSI Ps x Request Terminal Parameters (DECREQTPARM). + # if Ps is a "0" (default) or "1", and xterm is emulating VT100, + # the control sequence elicits a response of the same form whose + # parameters describe the terminal: + # Ps -> the given Ps incremented by 2. + # Pn = 1 <- no parity. + # Pn = 1 <- eight bits. + # Pn = 1 <- 2 8 transmit 38.4k baud. + # Pn = 1 <- 2 8 receive 38.4k baud. + # Pn = 1 <- clock multiplier. + # Pn = 0 <- STP flags. + requestParameters: (params) -> + + + # CSI Ps x Select Attribute Change Extent (DECSACE). + # Ps = 0 -> from start to end position, wrapped. + # Ps = 1 -> from start to end position, wrapped. + # Ps = 2 -> rectangle (exact). + selectChangeExtent: (params) -> + + + # CSI Pc; Pt; Pl; Pb; Pr$ x + # Fill Rectangular Area (DECFRA), VT420 and up. + # Pc is the character to use. + # Pt; Pl; Pb; Pr denotes the rectangle. + # NOTE: xterm doesn't enable this code by default. + fillRectangle: (params) -> + ch = params[0] + t = params[1] + l = params[2] + b = params[3] + r = params[4] + while t < b + 1 + line = @lines[@ybase + t] + i = l + while i < r + line[i] = [line[i][0], String.fromCharCode(ch)] + i++ + t++ + + @updateRange params[1] + @updateRange params[3] + + + # CSI Ps ; Pu ' z + # Enable Locator Reporting (DECELR). + # Valid values for the first parameter: + # Ps = 0 -> Locator disabled (default). + # Ps = 1 -> Locator enabled. + # Ps = 2 -> Locator enabled for one report, then disabled. + # The second parameter specifies the coordinate unit for locator + # reports. + # Valid values for the second parameter: + # Pu = 0 <- or omitted -> default to character cells. + # Pu = 1 <- device physical pixels. + # Pu = 2 <- character cells. + enableLocatorReporting: (params) -> + val = params[0] > 0 + + + # CSI Pt; Pl; Pb; Pr$ z + # Erase Rectangular Area (DECERA), VT400 and up. + # Pt; Pl; Pb; Pr denotes the rectangle. + # NOTE: xterm doesn't enable this code by default. + eraseRectangle: (params) -> + t = params[0] + l = params[1] + b = params[2] + r = params[3] + ch = [@eraseAttr(), " "] + while t < b + 1 + line = @lines[@ybase + t] + i = l + while i < r + line[i] = ch + i++ + t++ + + @updateRange params[0] + @updateRange params[2] + + + # CSI Pm ' { + # Select Locator Events (DECSLE). + # Valid values for the first (and any additional parameters) + # are: + # Ps = 0 -> only respond to explicit host requests (DECRQLP). + # (This is default). It also cancels any filter + # rectangle. + # Ps = 1 -> report button down transitions. + # Ps = 2 -> do not report button down transitions. + # Ps = 3 -> report button up transitions. + # Ps = 4 -> do not report button up transitions. + setLocatorEvents: (params) -> + + + # CSI Pt; Pl; Pb; Pr$ { + # Selective Erase Rectangular Area (DECSERA), VT400 and up. + # Pt; Pl; Pb; Pr denotes the rectangle. + selectiveEraseRectangle: (params) -> + + + # CSI Ps ' | + # Request Locator Position (DECRQLP). + # Valid values for the parameter are: + # Ps = 0 , 1 or omitted -> transmit a single DECLRP locator + # report. + + # If Locator Reporting has been enabled by a DECELR, xterm will + # respond with a DECLRP Locator Report. This report is also + # generated on button up and down events if they have been + # enabled with a DECSLE, or when the locator is detected outside + # of a filter rectangle, if filter rectangles have been enabled + # with a DECEFR. + + # -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w + + # Parameters are [event;button;row;column;page]. + # Valid values for the event: + # Pe = 0 -> locator unavailable - no other parameters sent. + # Pe = 1 -> request - xterm received a DECRQLP. + # Pe = 2 -> left button down. + # Pe = 3 -> left button up. + # Pe = 4 -> middle button down. + # Pe = 5 -> middle button up. + # Pe = 6 -> right button down. + # Pe = 7 -> right button up. + # Pe = 8 -> M4 button down. + # Pe = 9 -> M4 button up. + # Pe = 1 0 -> locator outside filter rectangle. + # ``button'' parameter is a bitmask indicating which buttons are + # pressed: + # Pb = 0 <- no buttons down. + # Pb & 1 <- right button down. + # Pb & 2 <- middle button down. + # Pb & 4 <- left button down. + # Pb & 8 <- M4 button down. + # ``row'' and ``column'' parameters are the coordinates of the + # locator position in the xterm window, encoded as ASCII deci- + # mal. + # The ``page'' parameter is not used by xterm, and will be omit- + # ted. + requestLocatorPosition: (params) -> + + + # CSI P m SP } + # Insert P s Column(s) (default = 1) (DECIC), VT420 and up. + # NOTE: xterm doesn't enable this code by default. + insertColumns: -> + param = params[0] + l = @ybase + @rows + ch = [@eraseAttr(), " "] + while param-- + i = @ybase + while i < l + @lines[i].splice @x + 1, 0, ch + @lines[i].pop() + i++ + @maxRange() + + + # CSI P m SP ~ + # Delete P s Column(s) (default = 1) (DECDC), VT420 and up + # NOTE: xterm doesn't enable this code by default. + deleteColumns: -> + param = params[0] + l = @ybase + @rows + ch = [@eraseAttr(), " "] + while param-- + i = @ybase + while i < l + @lines[i].splice @x, 1 + @lines[i].push ch + i++ + @maxRange() + + + # DEC Special Character and Line Drawing Set. + # http://vt100.net/docs/vt102-ug/table5-13.html + # A lot of curses apps use this if they see TERM=xterm. + # testing: echo -e '\e(0a\e(B' + # The xterm output sometimes seems to conflict with the + # reference above. xterm seems in line with the reference + # when running vttest however. + # The table below now uses xterm's output from vttest. + charsets: + SCLD:# (0 + "`": "◆" # '◆' + a: "▒" # '▒' + b: "\t" # '\t' + c: "\f" # '\f' + d: "\r" # '\r' + e: "\n" # '\n' + f: "°" # '°' + g: "±" # '±' + h: "␤" # '\u2424' (NL) + i: "\x0b" # '\v' + j: "┘" # '┘' + k: "┐" # '┐' + l: "┌" # '┌' + m: "└" # '└' + n: "┼" # '┼' + o: "⎺" # '⎺' + p: "⎻" # '⎻' + q: "─" # '─' + r: "⎼" # '⎼' + s: "⎽" # '⎽' + t: "├" # '├' + u: "┤" # '┤' + v: "┴" # '┴' + w: "┬" # '┬' + x: "│" # '│' + y: "≤" # '≤' + z: "≥" # '≥' + "{": "π" # 'π' + "|": "≠" # '≠' + "}": "£" # '£' + "~": "·" # '·' + UK: null # (A + US: null # (B (USASCII) + Dutch: null # (4 + Finnish: null # (C or (5 + French: null # (R + FrenchCanadian: null # (Q + German: null # (K + Italian: null # (Y + NorwegianDanish: null # (E or (6 + Spanish: null # (Z + Swedish: null # (H or (7 + Swiss: null # (= + ISOLatin: null # /A diff --git a/butterfly/static/javascripts/main.js b/butterfly/static/javascripts/main.js index 0130c11..182dbf5 100644 --- a/butterfly/static/javascripts/main.js +++ b/butterfly/static/javascripts/main.js @@ -1,7 +1,144 @@ // Generated by CoffeeScript 1.6.3 -var $, State, Terminal, alt, bench, cancel, cbench, cols, ctl, ctrl, first, quit, rows, s, send, state, term, virtual_input, ws, ws_url, +var $, FrontTerminal, alt, cancel, cols, ctl, ctrl, first, frontterm, quit, rows, send, state, virtual_input, worker, __slice = [].slice; +state = { + x: null, + y: null +}; + +document.addEventListener('keydown', function(e) { + var _ref; + if (e.shiftKey && ((37 <= (_ref = e.keyCode) && _ref <= 40))) { + if (state.y === null) { + state.y = term.ybase + term.y; + } + if (e.keyCode === 38) { + state.y--; + if (state.y < term.ybase) { + state.y = term.ybase; + } + } else if (e.keyCode === 40) { + state.y++; + if (state.y > term.ybase + term.y) { + state.y = term.ybase + term.y; + } + } + term.emit('data', ' \x0b\x15'); + if (state.y !== term.ybase + term.y) { + term.emit('data', term.grabText(0, term.cols - 1, state.y, state.y).replace('\n', '')); + } + e.stopPropagation(); + return false; + } else { + return state.x = state.y = null; + } +}); + +if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { + 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; + }); +} + +worker = new Worker('/static/javascripts/worker.js'); + +worker.addEventListener('message', function(e) { + switch (e.data.cmd) { + case 'refresh': + return frontterm.refresh(e.data.lines, e.data.start, e.data.end); + } +}); + +cols = rows = null; + +quit = false; + +$ = document.querySelectorAll.bind(document); + +send = function(data) { + return worker.postMessage({ + cmd: 'data', + data: 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 worker.postMessage('R' + params); + } +}; + +addEventListener('beforeunload', function() { + if (!quit) { + return 'This will exit the terminal session'; + } +}); + cancel = function(ev) { if (ev.preventDefault) { ev.preventDefault(); @@ -13,25 +150,11 @@ cancel = function(ev) { return false; }; -s = 0; - -State = { - normal: s++, - escaped: s++, - csi: s++, - osc: s++, - charset: s++, - dcs: s++, - ignore: s++ -}; - -Terminal = (function() { - function Terminal(parent, out, ctl) { +FrontTerminal = (function() { + function FrontTerminal(parent) { var div, i, term_size, _this = this; this.parent = parent; - this.out = out; - this.ctl = ctl != null ? ctl : function() {}; this.context = this.parent.ownerDocument.defaultView; this.document = this.parent.ownerDocument; this.body = this.document.getElementsByTagName('body')[0]; @@ -57,15 +180,12 @@ Terminal = (function() { this.element.appendChild(div); this.children.push(div); } - this.scrollback = 100000; this.visualBell = 100; - this.convertEol = false; - this.termName = 'xterm'; - this.cursorBlink = true; - this.screenKeys = false; + this.cursorHidden = false; + this.queue = ''; + this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); + this.skipNextKey = null; this.cursorState = 0; - this.reset_vars(); - this.refresh(0, this.rows - 1); this.focus(); this.startBlink(); addEventListener('keydown', this.keyDown.bind(this)); @@ -87,41 +207,7 @@ Terminal = (function() { this.initmouse(); } - Terminal.prototype.reset_vars = function() { - var i; - this.ybase = 0; - this.ydisp = 0; - this.x = 0; - this.y = 0; - this.cursorHidden = false; - this.state = State.normal; - this.queue = ''; - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - this.applicationKeypad = false; - this.applicationCursor = false; - this.originMode = false; - this.wraparoundMode = false; - this.normal = null; - this.charset = null; - this.gcharset = null; - this.glevel = 0; - this.charsets = [null]; - this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); - this.curAttr = this.defAttr; - this.params = []; - this.currentParam = 0; - this.prefix = ""; - this.lines = []; - i = this.rows; - while (i--) { - this.lines.push(this.blankLine()); - } - this.setupStops(); - return this.skipNextKey = null; - }; - - Terminal.prototype.compute_char_size = function() { + FrontTerminal.prototype.compute_char_size = function() { var test_span; test_span = document.createElement('span'); test_span.textContent = '0123456789'; @@ -133,11 +219,7 @@ Terminal = (function() { return this.children[0].removeChild(test_span); }; - Terminal.prototype.eraseAttr = function() { - return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); - }; - - Terminal.prototype.focus = function() { + FrontTerminal.prototype.focus = function() { if (this.sendFocus) { this.send('\x1b[I'); } @@ -146,9 +228,8 @@ Terminal = (function() { return this.element.classList.remove('blur'); }; - Terminal.prototype.blur = function() { + FrontTerminal.prototype.blur = function() { this.cursorState = 1; - this.refresh(this.y, this.y); if (this.sendFocus) { this.send('\x1b[O'); } @@ -156,7 +237,7 @@ Terminal = (function() { return this.element.classList.remove('focus'); }; - Terminal.prototype.paste = function(ev) { + FrontTerminal.prototype.paste = function(ev) { if (ev.clipboardData) { this.send(ev.clipboardData.getData('text/plain')); } else if (this.context.clipboardData) { @@ -165,7 +246,7 @@ Terminal = (function() { return cancel(ev); }; - Terminal.prototype.initmouse = function() { + FrontTerminal.prototype.initmouse = function() { var encode, getButton, getCoords, pressed, sendButton, sendEvent, sendMove, _this = this; pressed = 32; @@ -241,7 +322,7 @@ Terminal = (function() { return _this.send("\x1b[M" + String.fromCharCode.apply(String, data)); }; getButton = function(ev) { - var button, ctrl, meta, mod, shift; + var button, meta, mod, shift; switch (ev.type) { case "mousedown": button = ev.button != null ? +ev.button : (ev.which != null ? ev.which - 1 : null); @@ -343,7 +424,7 @@ Terminal = (function() { }); }; - Terminal.prototype.refresh = function(start, end) { + FrontTerminal.prototype.refresh = function(lines, start, end) { var attr, bg, ch, classes, data, fg, flags, i, line, out, parent, row, width, x, y; if (end - start >= this.rows / 3) { parent = this.element.parentNode; @@ -353,12 +434,12 @@ Terminal = (function() { } width = this.cols; y = start; - if (end >= this.lines.length) { - end = this.lines.length - 1; + if (end >= lines.length) { + end = lines.length - 1; } while (y <= end) { - row = y + this.ydisp; - line = this.lines[row]; + row = y; + line = lines[row]; out = ""; if (y === this.y && (this.ydisp === this.ybase || this.selectMode) && !this.cursorHidden) { x = this.x; @@ -447,7 +528,7 @@ Terminal = (function() { return parent != null ? parent.appendChild(this.element) : void 0; }; - Terminal.prototype._cursorBlink = function() { + FrontTerminal.prototype._cursorBlink = function() { var cursor; this.cursorState ^= 1; cursor = this.element.querySelector(".cursor"); @@ -461,14 +542,9 @@ Terminal = (function() { } }; - Terminal.prototype.showCursor = function() { - if (!this.cursorState) { - this.cursorState = 1; - return this.refresh(this.y, this.y); - } - }; + FrontTerminal.prototype.showCursor = function() {}; - Terminal.prototype.startBlink = function() { + FrontTerminal.prototype.startBlink = function() { var _this = this; if (!this.cursorBlink) { return; @@ -479,7 +555,7 @@ Terminal = (function() { return this._blink = setInterval(this._blinker, 500); }; - Terminal.prototype.refreshBlink = function() { + FrontTerminal.prototype.refreshBlink = function() { if (!this.cursorBlink) { return; } @@ -487,543 +563,18 @@ Terminal = (function() { return this._blink = setInterval(this._blinker, 500); }; - Terminal.prototype.scroll = function() { - var row; - if (++this.ybase === this.scrollback) { - this.ybase = this.ybase / 2 | 0; - this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); - } - this.ydisp = this.ybase; - row = this.ybase + this.rows - 1; - row -= this.rows - 1 - this.scrollBottom; - if (row === this.lines.length) { - this.lines.push(this.blankLine()); - } else { - this.lines.splice(row, 0, this.blankLine()); - } - if (this.scrollTop !== 0) { - if (this.ybase !== 0) { - this.ybase--; - this.ydisp = this.ybase; - } - this.lines.splice(this.ybase + this.scrollTop, 1); - } - this.updateRange(this.scrollTop); - return this.updateRange(this.scrollBottom); - }; - - Terminal.prototype.scrollDisp = function(disp) { + FrontTerminal.prototype.scrollDisp = function(disp) { this.ydisp += disp; if (this.ydisp > this.ybase) { - this.ydisp = this.ybase; + return this.ydisp = this.ybase; } else { if (this.ydisp < 0) { - this.ydisp = 0; + return this.ydisp = 0; } } - return this.refresh(0, this.rows - 1); }; - Terminal.prototype.write = function(data) { - var ch, cs, html, i, j, l, line, pt, valid, _ref; - this.refreshStart = this.y; - this.refreshEnd = this.y; - if (this.ybase !== this.ydisp) { - this.ydisp = this.ybase; - this.maxRange(); - } - i = 0; - l = data.length; - while (i < l) { - ch = data[i]; - switch (this.state) { - case State.normal: - switch (ch) { - case "\x07": - this.bell(); - break; - case "\n": - case "\x0b": - case "\x0c": - if (this.convertEol) { - this.x = 0; - } - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - break; - case "\r": - this.x = 0; - break; - case "\b": - if (this.x > 0) { - this.x--; - } - break; - case "\t": - this.x = this.nextStop(); - break; - case "\x0e": - this.setgLevel(1); - break; - case "\x0f": - this.setgLevel(0); - break; - case "\x1b": - this.state = State.escaped; - break; - default: - if (ch >= " ") { - if ((_ref = this.charset) != null ? _ref[ch] : void 0) { - ch = this.charset[ch]; - } - if (this.x >= this.cols) { - this.x = 0; - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - } - this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; - this.x++; - this.updateRange(this.y); - 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, " "]; - break; - } - this.lines[j][this.x] = [this.curAttr, " "]; - this.x++; - } - } - } - break; - case State.escaped: - switch (ch) { - case "[": - this.params = []; - this.currentParam = 0; - this.state = State.csi; - break; - case "]": - this.params = []; - this.currentParam = 0; - this.state = State.osc; - break; - case "P": - this.params = []; - this.currentParam = 0; - this.state = State.dcs; - break; - case "_": - this.state = State.ignore; - break; - case "^": - this.state = State.ignore; - break; - case "c": - this.reset(); - break; - case "E": - this.x = 0; - this.index(); - break; - case "D": - this.index(); - break; - case "M": - this.reverseIndex(); - break; - case "%": - this.setgLevel(0); - this.setgCharset(0, Terminal.prototype.charsets.US); - this.state = State.normal; - i++; - break; - case "(": - case ")": - case "*": - case "+": - case "-": - case ".": - switch (ch) { - case "(": - this.gcharset = 0; - break; - case ")": - case "-": - this.gcharset = 1; - break; - case "*": - case ".": - this.gcharset = 2; - break; - case "+": - this.gcharset = 3; - } - this.state = State.charset; - break; - case "/": - this.gcharset = 3; - this.state = State.charset; - i--; - break; - case "n": - this.setgLevel(2); - break; - case "o": - this.setgLevel(3); - break; - case "|": - this.setgLevel(3); - break; - case "}": - this.setgLevel(2); - break; - case "~": - this.setgLevel(1); - break; - case "7": - this.saveCursor(); - this.state = State.normal; - break; - case "8": - this.restoreCursor(); - this.state = State.normal; - break; - case "#": - this.state = State.normal; - i++; - break; - case "H": - this.tabSet(); - break; - case "=": - this.applicationKeypad = true; - this.state = State.normal; - break; - case ">": - this.applicationKeypad = false; - this.state = State.normal; - break; - default: - this.state = State.normal; - console.log("Unknown ESC control:", ch); - } - break; - case State.charset: - switch (ch) { - case "0": - cs = Terminal.prototype.charsets.SCLD; - break; - case "A": - cs = Terminal.prototype.charsets.UK; - break; - case "B": - cs = Terminal.prototype.charsets.US; - break; - case "4": - cs = Terminal.prototype.charsets.Dutch; - break; - case "C": - case "5": - cs = Terminal.prototype.charsets.Finnish; - break; - case "R": - cs = Terminal.prototype.charsets.French; - break; - case "Q": - cs = Terminal.prototype.charsets.FrenchCanadian; - break; - case "K": - cs = Terminal.prototype.charsets.German; - break; - case "Y": - cs = Terminal.prototype.charsets.Italian; - break; - case "E": - case "6": - cs = Terminal.prototype.charsets.NorwegianDanish; - break; - case "Z": - cs = Terminal.prototype.charsets.Spanish; - break; - case "H": - case "7": - cs = Terminal.prototype.charsets.Swedish; - break; - case "=": - cs = Terminal.prototype.charsets.Swiss; - break; - case "/": - cs = Terminal.prototype.charsets.ISOLatin; - i++; - break; - default: - cs = Terminal.prototype.charsets.US; - } - this.setgCharset(this.gcharset, cs); - this.gcharset = null; - this.state = State.normal; - break; - case State.osc: - if (ch === "\x1b" || ch === "\x07") { - if (ch === "\x1b") { - i++; - } - this.params.push(this.currentParam); - switch (this.params[0]) { - case 0: - case 1: - case 2: - if (this.params[1]) { - this.title = this.params[1] + " - ƸӜƷ butterfly"; - this.handleTitle(this.title); - } - break; - case 99: - html = "
" + this.params[1] + "
"; - this.lines[this.y + this.ybase][this.x] = [this.curAttr, html]; - line = 0; - while (line < this.get_html_height_in_lines(html) - 1) { - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - line++; - } - this.updateRange(this.y); - } - this.params = []; - this.currentParam = 0; - this.state = State.normal; - } else { - if (!this.params.length) { - if (ch >= "0" && ch <= "9") { - this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; - } else if (ch === ";") { - this.params.push(this.currentParam); - this.currentParam = ""; - } - } else { - this.currentParam += ch; - } - } - break; - case State.csi: - if (ch === "?" || ch === ">" || ch === "!") { - this.prefix = ch; - break; - } - if (ch >= "0" && ch <= "9") { - this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; - break; - } - if (ch === "$" || ch === "\"" || ch === " " || ch === "'") { - break; - } - this.params.push(this.currentParam); - this.currentParam = 0; - if (ch === ";") { - break; - } - this.state = State.normal; - switch (ch) { - case "A": - this.cursorUp(this.params); - break; - case "B": - this.cursorDown(this.params); - break; - case "C": - this.cursorForward(this.params); - break; - case "D": - this.cursorBackward(this.params); - break; - case "H": - this.cursorPos(this.params); - break; - case "J": - this.eraseInDisplay(this.params); - break; - case "K": - this.eraseInLine(this.params); - break; - case "m": - if (!this.prefix) { - this.charAttributes(this.params); - } - break; - case "n": - if (!this.prefix) { - this.deviceStatus(this.params); - } - break; - case "@": - this.insertChars(this.params); - break; - case "E": - this.cursorNextLine(this.params); - break; - case "F": - this.cursorPrecedingLine(this.params); - break; - case "G": - this.cursorCharAbsolute(this.params); - break; - case "L": - this.insertLines(this.params); - break; - case "M": - this.deleteLines(this.params); - break; - case "P": - this.deleteChars(this.params); - break; - case "X": - this.eraseChars(this.params); - break; - case "`": - this.charPosAbsolute(this.params); - break; - case "a": - this.HPositionRelative(this.params); - break; - case "c": - this.sendDeviceAttributes(this.params); - break; - case "d": - this.linePosAbsolute(this.params); - break; - case "e": - this.VPositionRelative(this.params); - break; - case "f": - this.HVPosition(this.params); - break; - case "h": - this.setMode(this.params); - break; - case "l": - this.resetMode(this.params); - break; - case "r": - this.setScrollRegion(this.params); - break; - case "s": - this.saveCursor(this.params); - break; - case "u": - this.restoreCursor(this.params); - break; - case "I": - this.cursorForwardTab(this.params); - break; - case "S": - this.scrollUp(this.params); - break; - case "T": - if (this.params.length < 2 && !this.prefix) { - this.scrollDown(this.params); - } - break; - case "Z": - this.cursorBackwardTab(this.params); - break; - case "b": - this.repeatPrecedingCharacter(this.params); - break; - case "g": - this.tabClear(this.params); - break; - case "p": - if (this.prefix === '!') { - this.softReset(this.params); - } - break; - default: - this.error("Unknown CSI code: %s.", ch); - } - this.prefix = ""; - break; - case State.dcs: - if (ch === "\x1b" || ch === "\x07") { - if (ch === "\x1b") { - i++; - } - switch (this.prefix) { - case "": - break; - case "$q": - pt = this.currentParam; - valid = false; - switch (pt) { - case "\"q": - pt = "0\"q"; - break; - case "\"p": - pt = "61\"p"; - break; - case "r": - pt = "" + (this.scrollTop + 1) + ";" + (this.scrollBottom + 1) + "r"; - break; - case "m": - pt = "0m"; - break; - default: - this.error("Unknown DCS Pt: %s.", pt); - pt = ""; - } - this.send("\x1bP" + +valid + "$r" + pt + "\x1b\\"); - break; - case "+q": - pt = this.currentParam; - valid = false; - this.send("\x1bP" + +valid + "+r" + pt + "\x1b\\"); - break; - default: - this.error("Unknown DCS prefix: %s.", this.prefix); - } - this.currentParam = 0; - this.prefix = ""; - this.state = State.normal; - } else if (!this.currentParam) { - if (!this.prefix && ch !== "$" && ch !== "+") { - this.currentParam = ch; - } else if (this.prefix.length === 2) { - this.currentParam = ch; - } else { - this.prefix += ch; - } - } else { - this.currentParam += ch; - } - break; - case State.ignore: - if (ch === "\x1b" || ch === "\x07") { - if (ch === "\x1b") { - i++; - } - this.state = State.normal; - } - } - i++; - } - this.updateRange(this.y); - return this.refresh(this.refreshStart, this.refreshEnd); - }; - - Terminal.prototype.writeln = function(data) { - return this.write("" + data + "\r\n"); - }; - - Terminal.prototype.keyDown = function(ev) { + FrontTerminal.prototype.keyDown = function(ev) { var key, _this = this; if (ev.keyCode > 15 && ev.keyCode < 19) { @@ -1258,19 +809,7 @@ Terminal = (function() { return cancel(ev); }; - Terminal.prototype.setgLevel = function(g) { - this.glevel = g; - return this.charset = this.charsets[g]; - }; - - Terminal.prototype.setgCharset = function(g, charset) { - this.charsets[g] = charset; - if (this.glevel === g) { - return this.charset = charset; - } - }; - - Terminal.prototype.keyPress = function(ev) { + FrontTerminal.prototype.keyPress = function(ev) { var key; if (this.skipNextKey === false) { this.skipNextKey = null; @@ -1295,7 +834,14 @@ Terminal = (function() { return false; }; - Terminal.prototype.send = function(data) { + FrontTerminal.prototype.handler = function(data) { + return worker.postMessage({ + cmd: 'data', + data: data + }); + }; + + FrontTerminal.prototype.send = function(data) { var _this = this; if (!this.queue) { setTimeout((function() { @@ -1306,7 +852,7 @@ Terminal = (function() { return this.queue += data; }; - Terminal.prototype.bell = function() { + FrontTerminal.prototype.bell = function() { var _this = this; if (!this.visualBell) { return; @@ -1317,8 +863,8 @@ Terminal = (function() { }), this.visualBell); }; - Terminal.prototype.resize = function() { - var ch, el, i, j, line, old_cols, old_rows, term_size; + FrontTerminal.prototype.resize = function() { + var el, j, line, old_cols, old_rows, term_size, _results, _results1; old_cols = this.cols; old_rows = this.rows; term_size = this.parent.getBoundingClientRect(); @@ -1328,1033 +874,40 @@ Terminal = (function() { return; } 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 < this.cols) { - this.lines[i].push(ch); - } - } - } else if (old_cols > this.cols) { - i = this.lines.length; - while (i--) { - while (this.lines[i].length > this.cols) { - this.lines[i].pop(); - } - } - } - this.setupStops(old_cols); j = old_rows; if (j < this.rows) { el = this.element; + _results = []; while (j++ < this.rows) { - if (this.lines.length < this.rows + this.ybase) { - this.lines.push(this.blankLine()); - } if (this.children.length < this.rows) { line = this.document.createElement("div"); line.className = 'line'; line.style.height = this.char_size.height + 'px'; el.appendChild(line); - this.children.push(line); + _results.push(this.children.push(line)); + } else { + _results.push(void 0); } } + return _results; } else if (j > this.rows) { + _results1 = []; while (j-- > this.rows) { - if (this.lines.length > this.rows + this.ybase) { - this.lines.pop(); - } if (this.children.length > this.rows) { el = this.children.pop(); if (!el) { continue; } - el.parentNode.removeChild(el); + _results1.push(el.parentNode.removeChild(el)); + } else { + _results1.push(void 0); } } - } - if (this.y >= this.rows) { - this.y = this.rows - 1; - } - if (this.x >= this.cols) { - this.x = this.cols - 1; - } - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - this.refresh(0, this.rows - 1); - return this.normal = null; - }; - - Terminal.prototype.updateRange = function(y) { - if (y < this.refreshStart) { - this.refreshStart = y; - } - if (y > this.refreshEnd) { - return this.refreshEnd = y; + return _results1; } }; - Terminal.prototype.maxRange = function() { - this.refreshStart = 0; - return this.refreshEnd = this.rows - 1; - }; - - Terminal.prototype.setupStops = function(i) { - var _results; - if (i != null) { - if (!this.tabs[i]) { - i = this.prevStop(i); - } - } else { - this.tabs = {}; - i = 0; - } - _results = []; - while (i < this.cols) { - this.tabs[i] = true; - _results.push(i += 8); - } - return _results; - }; - - Terminal.prototype.prevStop = function(x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[--x] && x > 0) { - 1; - } - if (x >= this.cols) { - return this.cols - 1; - } else { - if (x < 0) { - return 0; - } else { - return x; - } - } - }; - - Terminal.prototype.nextStop = function(x) { - if (x == null) { - x = this.x; - } - while (!this.tabs[++x] && x < this.cols) { - 1; - } - if (x >= this.cols) { - return this.cols - 1; - } else { - if (x < 0) { - return 0; - } else { - return x; - } - } - }; - - Terminal.prototype.eraseRight = function(x, y) { - var ch, line; - line = this.lines[this.ybase + y]; - ch = [this.eraseAttr(), " "]; - while (x < this.cols) { - line[x] = ch; - x++; - } - return this.updateRange(y); - }; - - Terminal.prototype.eraseLeft = function(x, y) { - var ch, line; - line = this.lines[this.ybase + y]; - ch = [this.eraseAttr(), " "]; - x++; - while (x--) { - line[x] = ch; - } - return this.updateRange(y); - }; - - Terminal.prototype.eraseLine = function(y) { - return this.eraseRight(0, y); - }; - - Terminal.prototype.blankLine = function(cur) { - var attr, ch, i, line; - attr = (cur ? this.eraseAttr() : this.defAttr); - ch = [attr, " "]; - line = []; - i = 0; - while (i < this.cols) { - line[i] = ch; - i++; - } - return line; - }; - - Terminal.prototype.ch = function(cur) { - if (cur) { - return [this.eraseAttr(), " "]; - } else { - return [this.defAttr, " "]; - } - }; - - Terminal.prototype.isterm = function(term) { - return ("" + this.termName).indexOf(term) === 0; - }; - - Terminal.prototype.handler = function(data) { - return this.out(data); - }; - - Terminal.prototype.handleTitle = function(title) { - return document.title = title; - }; - - Terminal.prototype.index = function() { - this.y++; - if (this.y > this.scrollBottom) { - this.y--; - this.scroll(); - } - return this.state = State.normal; - }; - - Terminal.prototype.reverseIndex = function() { - var j; - this.y--; - if (this.y < this.scrollTop) { - this.y++; - this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); - j = this.rows - 1 - this.scrollBottom; - this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); - } - return this.state = State.normal; - }; - - Terminal.prototype.reset = function() { - this.reset_vars(); - return this.refresh(0, this.rows - 1); - }; - - Terminal.prototype.tabSet = function() { - this.tabs[this.x] = true; - return this.state = State.normal; - }; - - Terminal.prototype.cursorUp = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y -= param; - if (this.y < 0) { - return this.y = 0; - } - }; - - Terminal.prototype.cursorDown = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y += param; - if (this.y >= this.rows) { - return this.y = this.rows - 1; - } - }; - - Terminal.prototype.cursorForward = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.x += param; - if (this.x >= this.cols) { - return this.x = this.cols - 1; - } - }; - - Terminal.prototype.cursorBackward = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.x -= param; - if (this.x < 0) { - return this.x = 0; - } - }; - - Terminal.prototype.cursorPos = function(params) { - var col, row; - row = params[0] - 1; - if (params.length >= 2) { - col = params[1] - 1; - } else { - col = 0; - } - if (row < 0) { - row = 0; - } else { - if (row >= this.rows) { - row = this.rows - 1; - } - } - if (col < 0) { - col = 0; - } else { - if (col >= this.cols) { - col = this.cols - 1; - } - } - this.x = col; - return this.y = row; - }; - - Terminal.prototype.eraseInDisplay = function(params) { - var j, _results, _results1, _results2; - switch (params[0]) { - case 0: - this.eraseRight(this.x, this.y); - j = this.y + 1; - _results = []; - while (j < this.rows) { - this.eraseLine(j); - _results.push(j++); - } - return _results; - break; - case 1: - this.eraseLeft(this.x, this.y); - j = this.y; - _results1 = []; - while (j--) { - _results1.push(this.eraseLine(j)); - } - return _results1; - break; - case 2: - j = this.rows; - _results2 = []; - while (j--) { - _results2.push(this.eraseLine(j)); - } - return _results2; - } - }; - - Terminal.prototype.eraseInLine = function(params) { - switch (params[0]) { - case 0: - return this.eraseRight(this.x, this.y); - case 1: - return this.eraseLeft(this.x, this.y); - case 2: - return this.eraseLine(this.y); - } - }; - - Terminal.prototype.charAttributes = function(params) { - var bg, fg, flags, i, l, p; - if (params.length === 1 && params[0] === 0) { - this.curAttr = this.defAttr; - return; - } - flags = this.curAttr >> 18; - fg = (this.curAttr >> 9) & 0x1ff; - bg = this.curAttr & 0x1ff; - l = params.length; - i = 0; - while (i < l) { - p = params[i]; - if (p >= 30 && p <= 37) { - fg = p - 30; - } else if (p >= 40 && p <= 47) { - bg = p - 40; - } else if (p >= 90 && p <= 97) { - p += 8; - fg = p - 90; - } else if (p >= 100 && p <= 107) { - p += 8; - bg = p - 100; - } else if (p === 0) { - flags = this.defAttr >> 18; - fg = (this.defAttr >> 9) & 0x1ff; - bg = this.defAttr & 0x1ff; - } else if (p === 1) { - flags |= 1; - } else if (p === 4) { - flags |= 2; - } else if (p === 5) { - flags |= 4; - } else if (p === 7) { - flags |= 8; - } else if (p === 8) { - flags |= 16; - } else if (p === 22) { - flags &= ~1; - } else if (p === 24) { - flags &= ~2; - } else if (p === 25) { - flags &= ~4; - } else if (p === 27) { - flags &= ~8; - } else if (p === 28) { - flags &= ~16; - } else if (p === 39) { - fg = (this.defAttr >> 9) & 0x1ff; - } else if (p === 49) { - bg = this.defAttr & 0x1ff; - } else if (p === 38) { - if (params[i + 1] === 2) { - i += 2; - fg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff; - i += 2; - } else if (params[i + 1] === 5) { - i += 2; - fg = params[i] & 0xff; - } - } else if (p === 48) { - if (params[i + 1] === 2) { - i += 2; - bg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff; - i += 2; - } else if (params[i + 1] === 5) { - i += 2; - bg = params[i] & 0xff; - } - } else if (p === 100) { - fg = (this.defAttr >> 9) & 0x1ff; - bg = this.defAttr & 0x1ff; - } else { - this.error("Unknown SGR attribute: %d.", p); - } - i++; - } - return this.curAttr = (flags << 18) | (fg << 9) | bg; - }; - - Terminal.prototype.deviceStatus = function(params) { - if (!this.prefix) { - switch (params[0]) { - case 5: - return this.send("\x1b[0n"); - case 6: - return this.send("\x1b[" + (this.y + 1) + ";" + (this.x + 1) + "R"); - } - } else if (this.prefix === "?") { - if (params[0] === 6) { - return this.send("\x1b[?" + (this.y + 1) + ";" + (this.x + 1) + "R"); - } - } - }; - - Terminal.prototype.insertChars = function(params) { - var ch, j, param, row, _results; - param = params[0]; - if (param < 1) { - param = 1; - } - row = this.y + this.ybase; - j = this.x; - ch = [this.eraseAttr(), " "]; - _results = []; - while (param-- && j < this.cols) { - this.lines[row].splice(j++, 0, ch); - _results.push(this.lines[row].pop()); - } - return _results; - }; - - Terminal.prototype.cursorNextLine = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y += param; - if (this.y >= this.rows) { - this.y = this.rows - 1; - } - return this.x = 0; - }; - - Terminal.prototype.cursorPrecedingLine = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y -= param; - if (this.y < 0) { - this.y = 0; - } - return this.x = 0; - }; - - Terminal.prototype.cursorCharAbsolute = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - return this.x = param - 1; - }; - - Terminal.prototype.insertLines = function(params) { - var j, param, row; - param = params[0]; - if (param < 1) { - param = 1; - } - row = this.y + this.ybase; - j = this.rows - 1 - this.scrollBottom; - j = this.rows - 1 + this.ybase - j + 1; - while (param--) { - this.lines.splice(row, 0, this.blankLine(true)); - this.lines.splice(j, 1); - } - this.updateRange(this.y); - return this.updateRange(this.scrollBottom); - }; - - Terminal.prototype.deleteLines = function(params) { - var j, param, row; - param = params[0]; - if (param < 1) { - param = 1; - } - row = this.y + this.ybase; - j = this.rows - 1 - this.scrollBottom; - j = this.rows - 1 + this.ybase - j; - while (param--) { - this.lines.splice(j + 1, 0, this.blankLine(true)); - this.lines.splice(row, 1); - } - this.updateRange(this.y); - return this.updateRange(this.scrollBottom); - }; - - Terminal.prototype.deleteChars = function(params) { - var ch, param, row, _results; - param = params[0]; - if (param < 1) { - param = 1; - } - row = this.y + this.ybase; - ch = [this.eraseAttr(), " "]; - _results = []; - while (param--) { - this.lines[row].splice(this.x, 1); - _results.push(this.lines[row].push(ch)); - } - return _results; - }; - - Terminal.prototype.eraseChars = function(params) { - var ch, j, param, row, _results; - param = params[0]; - if (param < 1) { - param = 1; - } - row = this.y + this.ybase; - j = this.x; - ch = [this.eraseAttr(), " "]; - _results = []; - while (param-- && j < this.cols) { - _results.push(this.lines[row][j++] = ch); - } - return _results; - }; - - Terminal.prototype.charPosAbsolute = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.x = param - 1; - if (this.x >= this.cols) { - return this.x = this.cols - 1; - } - }; - - Terminal.prototype.HPositionRelative = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.x += param; - if (this.x >= this.cols) { - return this.x = this.cols - 1; - } - }; - - Terminal.prototype.sendDeviceAttributes = function(params) { - if (params[0] > 0) { - return; - } - if (!this.prefix) { - if (this.isterm("xterm") || this.isterm("rxvt-unicode") || this.isterm("screen")) { - return this.send("\x1b[?1;2c"); - } else { - if (this.isterm("linux")) { - return this.send("\x1b[?6c"); - } - } - } else if (this.prefix === ">") { - if (this.isterm("xterm")) { - return this.send("\x1b[>0;276;0c"); - } else if (this.isterm("rxvt-unicode")) { - return this.send("\x1b[>85;95;0c"); - } else if (this.isterm("linux")) { - return this.send(params[0] + "c"); - } else { - if (this.isterm("screen")) { - return this.send("\x1b[>83;40003;0c"); - } - } - } - }; - - Terminal.prototype.linePosAbsolute = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y = param - 1; - if (this.y >= this.rows) { - return this.y = this.rows - 1; - } - }; - - Terminal.prototype.VPositionRelative = function(params) { - var param; - param = params[0]; - if (param < 1) { - param = 1; - } - this.y += param; - if (this.y >= this.rows) { - return this.y = this.rows - 1; - } - }; - - Terminal.prototype.HVPosition = function(params) { - if (params[0] < 1) { - params[0] = 1; - } - if (params[1] < 1) { - params[1] = 1; - } - this.y = params[0] - 1; - if (this.y >= this.rows) { - this.y = this.rows - 1; - } - this.x = params[1] - 1; - if (this.x >= this.cols) { - return this.x = this.cols - 1; - } - }; - - Terminal.prototype.setMode = function(params) { - var i, l, normal; - if (typeof params === "object") { - l = params.length; - i = 0; - while (i < l) { - this.setMode(params[i]); - i++; - } - return; - } - if (this.prefix === "?") { - switch (params) { - case 1: - return this.applicationCursor = true; - case 2: - this.setgCharset(0, Terminal.prototype.charsets.US); - this.setgCharset(1, Terminal.prototype.charsets.US); - this.setgCharset(2, Terminal.prototype.charsets.US); - return this.setgCharset(3, Terminal.prototype.charsets.US); - case 3: - this.savedCols = this.cols; - return this.resize(132, this.rows); - case 6: - return this.originMode = true; - case 7: - return this.wraparoundMode = true; - case 66: - return this.applicationKeypad = true; - case 9: - case 1000: - case 1002: - case 1003: - this.x10Mouse = params === 9; - this.vt200Mouse = params === 1000; - this.normalMouse = params > 1000; - this.mouseEvents = true; - return this.element.style.cursor = "default"; - case 1004: - return this.sendFocus = true; - case 1005: - return this.utfMouse = true; - case 1006: - return this.sgrMouse = true; - case 1015: - return this.urxvtMouse = true; - case 25: - return this.cursorHidden = false; - case 1049: - case 47: - case 1047: - if (!this.normal) { - normal = { - lines: this.lines, - ybase: this.ybase, - ydisp: this.ydisp, - x: this.x, - y: this.y, - scrollTop: this.scrollTop, - scrollBottom: this.scrollBottom, - tabs: this.tabs - }; - this.reset(); - this.normal = normal; - return this.showCursor(); - } - } - } - }; - - Terminal.prototype.resetMode = function(params) { - var i, l; - if (typeof params === "object") { - l = params.length; - i = 0; - while (i < l) { - this.resetMode(params[i]); - i++; - } - return; - } - if (this.prefix === "?") { - switch (params) { - case 1: - return this.applicationCursor = false; - case 3: - if (this.cols === 132 && this.savedCols) { - this.resize(this.savedCols, this.rows); - } - return delete this.savedCols; - case 6: - return this.originMode = false; - case 7: - return this.wraparoundMode = false; - case 66: - return this.applicationKeypad = false; - case 9: - case 1000: - case 1002: - case 1003: - this.x10Mouse = false; - this.vt200Mouse = false; - this.normalMouse = false; - this.mouseEvents = false; - return this.element.style.cursor = ""; - case 1004: - return this.sendFocus = false; - case 1005: - return this.utfMouse = false; - case 1006: - return this.sgrMouse = false; - case 1015: - return this.urxvtMouse = false; - case 25: - return this.cursorHidden = true; - case 1049: - case 47: - case 1047: - if (this.normal) { - this.lines = this.normal.lines; - this.ybase = this.normal.ybase; - this.ydisp = this.normal.ydisp; - this.x = this.normal.x; - this.y = this.normal.y; - this.scrollTop = this.normal.scrollTop; - this.scrollBottom = this.normal.scrollBottom; - this.tabs = this.normal.tabs; - this.normal = null; - this.refresh(0, this.rows - 1); - return this.showCursor(); - } - } - } - }; - - Terminal.prototype.setScrollRegion = function(params) { - if (this.prefix) { - return; - } - this.scrollTop = (params[0] || 1) - 1; - this.scrollBottom = (params[1] || this.rows) - 1; - this.x = 0; - return this.y = 0; - }; - - Terminal.prototype.saveCursor = function(params) { - this.savedX = this.x; - return this.savedY = this.y; - }; - - Terminal.prototype.restoreCursor = function(params) { - this.x = this.savedX || 0; - return this.y = this.savedY || 0; - }; - - Terminal.prototype.cursorForwardTab = function(params) { - var param, _results; - param = params[0] || 1; - _results = []; - while (param--) { - _results.push(this.x = this.nextStop()); - } - return _results; - }; - - Terminal.prototype.scrollUp = function(params) { - var param; - param = params[0] || 1; - while (param--) { - this.lines.splice(this.ybase + this.scrollTop, 1); - this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); - } - this.updateRange(this.scrollTop); - return this.updateRange(this.scrollBottom); - }; - - Terminal.prototype.scrollDown = function(params) { - var param; - param = params[0] || 1; - while (param--) { - this.lines.splice(this.ybase + this.scrollBottom, 1); - this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); - } - this.updateRange(this.scrollTop); - return this.updateRange(this.scrollBottom); - }; - - Terminal.prototype.initMouseTracking = function(params) {}; - - Terminal.prototype.resetTitleModes = function(params) {}; - - Terminal.prototype.cursorBackwardTab = function(params) { - var param, _results; - param = params[0] || 1; - _results = []; - while (param--) { - _results.push(this.x = this.prevStop()); - } - return _results; - }; - - Terminal.prototype.repeatPrecedingCharacter = function(params) { - var ch, line, param, _results; - param = params[0] || 1; - line = this.lines[this.ybase + this.y]; - ch = line[this.x - 1] || [this.defAttr, " "]; - _results = []; - while (param--) { - _results.push(line[this.x++] = ch); - } - return _results; - }; - - Terminal.prototype.tabClear = function(params) { - var param; - param = params[0]; - if (param <= 0) { - return delete this.tabs[this.x]; - } else { - if (param === 3) { - return this.tabs = {}; - } - } - }; - - Terminal.prototype.mediaCopy = function(params) {}; - - Terminal.prototype.setResources = function(params) {}; - - Terminal.prototype.disableModifiers = function(params) {}; - - Terminal.prototype.setPointerMode = function(params) {}; - - Terminal.prototype.softReset = function(params) { - this.cursorHidden = false; - this.insertMode = false; - this.originMode = false; - this.wraparoundMode = false; - this.applicationKeypad = false; - this.applicationCursor = false; - this.scrollTop = 0; - this.scrollBottom = this.rows - 1; - this.curAttr = this.defAttr; - this.x = this.y = 0; - this.charset = null; - this.glevel = 0; - return this.charsets = [null]; - }; - - Terminal.prototype.requestAnsiMode = function(params) {}; - - Terminal.prototype.requestPrivateMode = function(params) {}; - - Terminal.prototype.setConformanceLevel = function(params) {}; - - Terminal.prototype.loadLEDs = function(params) {}; - - Terminal.prototype.setCursorStyle = function(params) {}; - - Terminal.prototype.setCharProtectionAttr = function(params) {}; - - Terminal.prototype.restorePrivateValues = function(params) {}; - - Terminal.prototype.setAttrInRectangle = function(params) { - var attr, b, i, l, line, r, t; - t = params[0]; - l = params[1]; - b = params[2]; - r = params[3]; - attr = params[4]; - while (t < b + 1) { - line = this.lines[this.ybase + t]; - i = l; - while (i < r) { - line[i] = [attr, line[i][1]]; - i++; - } - t++; - } - this.updateRange(params[0]); - return this.updateRange(params[2]); - }; - - Terminal.prototype.savePrivateValues = function(params) {}; - - Terminal.prototype.manipulateWindow = function(params) {}; - - Terminal.prototype.reverseAttrInRectangle = function(params) {}; - - Terminal.prototype.setTitleModeFeature = function(params) {}; - - Terminal.prototype.setWarningBellVolume = function(params) {}; - - Terminal.prototype.setMarginBellVolume = function(params) {}; - - Terminal.prototype.copyRectangle = function(params) {}; - - Terminal.prototype.enableFilterRectangle = function(params) {}; - - Terminal.prototype.requestParameters = function(params) {}; - - Terminal.prototype.selectChangeExtent = function(params) {}; - - Terminal.prototype.fillRectangle = function(params) { - var b, ch, i, l, line, r, t; - ch = params[0]; - t = params[1]; - l = params[2]; - b = params[3]; - r = params[4]; - while (t < b + 1) { - line = this.lines[this.ybase + t]; - i = l; - while (i < r) { - line[i] = [line[i][0], String.fromCharCode(ch)]; - i++; - } - t++; - } - this.updateRange(params[1]); - return this.updateRange(params[3]); - }; - - Terminal.prototype.enableLocatorReporting = function(params) { - var val; - return val = params[0] > 0; - }; - - Terminal.prototype.eraseRectangle = function(params) { - var b, ch, i, l, line, r, t; - t = params[0]; - l = params[1]; - b = params[2]; - r = params[3]; - ch = [this.eraseAttr(), " "]; - while (t < b + 1) { - line = this.lines[this.ybase + t]; - i = l; - while (i < r) { - line[i] = ch; - i++; - } - t++; - } - this.updateRange(params[0]); - return this.updateRange(params[2]); - }; - - Terminal.prototype.setLocatorEvents = function(params) {}; - - Terminal.prototype.selectiveEraseRectangle = function(params) {}; - - Terminal.prototype.requestLocatorPosition = function(params) {}; - - Terminal.prototype.insertColumns = function() { - var ch, i, l, param; - param = params[0]; - l = this.ybase + this.rows; - ch = [this.eraseAttr(), " "]; - while (param--) { - i = this.ybase; - while (i < l) { - this.lines[i].splice(this.x + 1, 0, ch); - this.lines[i].pop(); - i++; - } - } - return this.maxRange(); - }; - - Terminal.prototype.deleteColumns = function() { - var ch, i, l, param; - param = params[0]; - l = this.ybase + this.rows; - ch = [this.eraseAttr(), " "]; - while (param--) { - i = this.ybase; - while (i < l) { - this.lines[i].splice(this.x, 1); - this.lines[i].push(ch); - i++; - } - } - return this.maxRange(); - }; - - Terminal.prototype.get_html_height_in_lines = function(html) { + FrontTerminal.prototype.get_html_height_in_lines = function(html) { var html_height, temp_node; temp_node = document.createElement("div"); temp_node.innerHTML = html; @@ -2364,241 +917,15 @@ Terminal = (function() { return Math.ceil(html_height / this.char_size.height); }; - Terminal.prototype.charsets = { - SCLD: { - "`": "◆", - a: "▒", - b: "\t", - c: "\f", - d: "\r", - e: "\n", - f: "°", - g: "±", - h: "␤", - i: "\x0b", - j: "┘", - k: "┐", - l: "┌", - m: "└", - n: "┼", - o: "⎺", - p: "⎻", - q: "─", - r: "⎼", - s: "⎽", - t: "├", - u: "┤", - v: "┴", - w: "┬", - x: "│", - y: "≤", - z: "≥", - "{": "π", - "|": "≠", - "}": "£", - "~": "·" - }, - UK: null, - US: null, - Dutch: null, - Finnish: null, - French: null, - FrenchCanadian: null, - German: null, - Italian: null, - NorwegianDanish: null, - Spanish: null, - Swedish: null, - Swiss: null, - ISOLatin: null - }; - - return Terminal; + return FrontTerminal; })(); -state = { - x: null, - y: null -}; +frontterm = new FrontTerminal($('#wrapper')[0], send, ctl); -document.addEventListener('keydown', function(e) { - var _ref; - if (e.shiftKey && ((37 <= (_ref = e.keyCode) && _ref <= 40))) { - if (state.y === null) { - state.y = term.ybase + term.y; - } - if (e.keyCode === 38) { - state.y--; - if (state.y < term.ybase) { - state.y = term.ybase; - } - } else if (e.keyCode === 40) { - state.y++; - if (state.y > term.ybase + term.y) { - state.y = term.ybase + term.y; - } - } - term.emit('data', ' \x0b\x15'); - if (state.y !== term.ybase + term.y) { - term.emit('data', term.grabText(0, term.cols - 1, state.y, state.y).replace('\n', '')); - } - e.stopPropagation(); - return false; - } else { - return state.x = state.y = null; - } +worker.postMessage({ + cmd: 'init', + cols: frontterm.cols, + rows: frontterm.rows, + wsurl: 'ws://' + document.location.host + '/ws' + location.pathname }); - -if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { - 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); - -ws.addEventListener('open', function() { - console.log("WebSocket open", arguments); - ws.send('R' + term.cols + ',' + term.rows); - if (location.hash) { - return setTimeout(function() { - return ws.send('S' + location.hash.slice(1) + '\n'); - }, 100); - } -}); - -ws.addEventListener('error', function() { - return console.log("WebSocket error", arguments); -}); - -ws.addEventListener('message', function(e) { - return setTimeout(function() { - return term.write(e.data); - }, 1); -}); - -ws.addEventListener('close', function() { - console.log("WebSocket closed", arguments); - quit = true; - return open('', '_self').close(); -}); - -term = new Terminal($('#wrapper')[0], send, ctl); - -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"); -}; diff --git a/butterfly/static/javascripts/worker.js b/butterfly/static/javascripts/worker.js new file mode 100644 index 0000000..f1bc4d6 --- /dev/null +++ b/butterfly/static/javascripts/worker.js @@ -0,0 +1,1712 @@ +// Generated by CoffeeScript 1.6.3 +var BackTerminal, State, backterm, s, ws; + +ws = null; + +backterm = null; + +self.addEventListener('message', function(e) { + switch (e.data.cmd) { + case 'init': + backterm = new BackTerminal(e.data.cols, e.data.rows); + ws = new WebSocket(e.data.wsurl); + ws.addEventListener('open', function() { + console.log("WebSocket open", arguments); + return ws.send('R' + e.data.cols + ',' + e.data.rows); + }); + ws.addEventListener('error', function() { + return console.log("WebSocket error", arguments); + }); + ws.addEventListener('message', function(e) { + return backterm.write(e.data); + }); + return ws.addEventListener('close', function() { + return console.log("WebSocket closed", arguments); + }); + case 'data': + return ws.send('S' + e.data.data); + } +}); + +s = 0; + +State = { + normal: s++, + escaped: s++, + csi: s++, + osc: s++, + charset: s++, + dcs: s++, + ignore: s++ +}; + +BackTerminal = (function() { + function BackTerminal(cols, rows) { + this.cols = cols; + this.rows = rows; + this.scrollback = 100000; + this.visualBell = 100; + this.convertEol = false; + this.termName = 'xterm'; + this.cursorBlink = true; + this.screenKeys = false; + this.cursorState = 0; + this.reset_vars(); + } + + BackTerminal.prototype.reset_vars = function() { + var i; + this.ybase = 0; + this.ydisp = 0; + this.x = 0; + this.y = 0; + this.state = State.normal; + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + this.applicationKeypad = false; + this.applicationCursor = false; + this.originMode = false; + this.wraparoundMode = false; + this.normal = null; + this.charset = null; + this.gcharset = null; + this.glevel = 0; + this.charsets = [null]; + this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); + this.curAttr = this.defAttr; + this.params = []; + this.currentParam = 0; + this.prefix = ""; + this.lines = []; + i = this.rows; + while (i--) { + this.lines.push(this.blankLine()); + } + return this.setupStops(); + }; + + BackTerminal.prototype.eraseAttr = function() { + return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); + }; + + BackTerminal.prototype.scroll = function() { + var row; + if (++this.ybase === this.scrollback) { + this.ybase = this.ybase / 2 | 0; + this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); + } + this.ydisp = this.ybase; + row = this.ybase + this.rows - 1; + row -= this.rows - 1 - this.scrollBottom; + if (row === this.lines.length) { + this.lines.push(this.blankLine()); + } else { + this.lines.splice(row, 0, this.blankLine()); + } + if (this.scrollTop !== 0) { + if (this.ybase !== 0) { + this.ybase--; + this.ydisp = this.ybase; + } + this.lines.splice(this.ybase + this.scrollTop, 1); + } + this.updateRange(this.scrollTop); + return this.updateRange(this.scrollBottom); + }; + + BackTerminal.prototype.scrollDisp = function(disp) { + this.ydisp += disp; + if (this.ydisp > this.ybase) { + this.ydisp = this.ybase; + } else { + if (this.ydisp < 0) { + this.ydisp = 0; + } + } + return this.refresh(0, this.rows - 1); + }; + + BackTerminal.prototype.refresh = function(start, end) { + return self.postMessage({ + cmd: 'refresh', + lines: this.lines.slice(this.ydisp, this.ydisp + this.rows), + start: start, + end: end + }); + }; + + BackTerminal.prototype.write = function(data) { + var ch, cs, html, i, j, l, line, pt, valid, _ref; + this.refreshStart = this.y; + this.refreshEnd = this.y; + if (this.ybase !== this.ydisp) { + this.ydisp = this.ybase; + this.maxRange(); + } + i = 0; + l = data.length; + while (i < l) { + ch = data[i]; + switch (this.state) { + case State.normal: + switch (ch) { + case "\x07": + this.bell(); + break; + case "\n": + case "\x0b": + case "\x0c": + if (this.convertEol) { + this.x = 0; + } + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + break; + case "\r": + this.x = 0; + break; + case "\b": + if (this.x > 0) { + this.x--; + } + break; + case "\t": + this.x = this.nextStop(); + break; + case "\x0e": + this.setgLevel(1); + break; + case "\x0f": + this.setgLevel(0); + break; + case "\x1b": + this.state = State.escaped; + break; + default: + if (ch >= " ") { + if ((_ref = this.charset) != null ? _ref[ch] : void 0) { + ch = this.charset[ch]; + } + if (this.x >= this.cols) { + this.x = 0; + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + } + this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; + this.x++; + this.updateRange(this.y); + 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, " "]; + break; + } + this.lines[j][this.x] = [this.curAttr, " "]; + this.x++; + } + } + } + break; + case State.escaped: + switch (ch) { + case "[": + this.params = []; + this.currentParam = 0; + this.state = State.csi; + break; + case "]": + this.params = []; + this.currentParam = 0; + this.state = State.osc; + break; + case "P": + this.params = []; + this.currentParam = 0; + this.state = State.dcs; + break; + case "_": + this.state = State.ignore; + break; + case "^": + this.state = State.ignore; + break; + case "c": + this.reset(); + break; + case "E": + this.x = 0; + this.index(); + break; + case "D": + this.index(); + break; + case "M": + this.reverseIndex(); + break; + case "%": + this.setgLevel(0); + this.setgCharset(0, BackTerminal.prototype.charsets.US); + this.state = State.normal; + i++; + break; + case "(": + case ")": + case "*": + case "+": + case "-": + case ".": + switch (ch) { + case "(": + this.gcharset = 0; + break; + case ")": + case "-": + this.gcharset = 1; + break; + case "*": + case ".": + this.gcharset = 2; + break; + case "+": + this.gcharset = 3; + } + this.state = State.charset; + break; + case "/": + this.gcharset = 3; + this.state = State.charset; + i--; + break; + case "n": + this.setgLevel(2); + break; + case "o": + this.setgLevel(3); + break; + case "|": + this.setgLevel(3); + break; + case "}": + this.setgLevel(2); + break; + case "~": + this.setgLevel(1); + break; + case "7": + this.saveCursor(); + this.state = State.normal; + break; + case "8": + this.restoreCursor(); + this.state = State.normal; + break; + case "#": + this.state = State.normal; + i++; + break; + case "H": + this.tabSet(); + break; + case "=": + this.applicationKeypad = true; + this.state = State.normal; + break; + case ">": + this.applicationKeypad = false; + this.state = State.normal; + break; + default: + this.state = State.normal; + console.log("Unknown ESC control:", ch); + } + break; + case State.charset: + switch (ch) { + case "0": + cs = BackTerminal.prototype.charsets.SCLD; + break; + case "A": + cs = BackTerminal.prototype.charsets.UK; + break; + case "B": + cs = BackTerminal.prototype.charsets.US; + break; + case "4": + cs = BackTerminal.prototype.charsets.Dutch; + break; + case "C": + case "5": + cs = BackTerminal.prototype.charsets.Finnish; + break; + case "R": + cs = BackTerminal.prototype.charsets.French; + break; + case "Q": + cs = BackTerminal.prototype.charsets.FrenchCanadian; + break; + case "K": + cs = BackTerminal.prototype.charsets.German; + break; + case "Y": + cs = BackTerminal.prototype.charsets.Italian; + break; + case "E": + case "6": + cs = BackTerminal.prototype.charsets.NorwegianDanish; + break; + case "Z": + cs = BackTerminal.prototype.charsets.Spanish; + break; + case "H": + case "7": + cs = BackTerminal.prototype.charsets.Swedish; + break; + case "=": + cs = BackTerminal.prototype.charsets.Swiss; + break; + case "/": + cs = BackTerminal.prototype.charsets.ISOLatin; + i++; + break; + default: + cs = BackTerminal.prototype.charsets.US; + } + this.setgCharset(this.gcharset, cs); + this.gcharset = null; + this.state = State.normal; + break; + case State.osc: + if (ch === "\x1b" || ch === "\x07") { + if (ch === "\x1b") { + i++; + } + this.params.push(this.currentParam); + switch (this.params[0]) { + case 0: + case 1: + case 2: + if (this.params[1]) { + this.title = this.params[1] + " - ƸӜƷ butterfly"; + this.handleTitle(this.title); + } + break; + case 99: + html = "
" + this.params[1] + "
"; + this.lines[this.y + this.ybase][this.x] = [this.curAttr, html]; + line = 0; + while (line < this.get_html_height_in_lines(html) - 1) { + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + line++; + } + this.updateRange(this.y); + } + this.params = []; + this.currentParam = 0; + this.state = State.normal; + } else { + if (!this.params.length) { + if (ch >= "0" && ch <= "9") { + this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; + } else if (ch === ";") { + this.params.push(this.currentParam); + this.currentParam = ""; + } + } else { + this.currentParam += ch; + } + } + break; + case State.csi: + if (ch === "?" || ch === ">" || ch === "!") { + this.prefix = ch; + break; + } + if (ch >= "0" && ch <= "9") { + this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; + break; + } + if (ch === "$" || ch === "\"" || ch === " " || ch === "'") { + break; + } + this.params.push(this.currentParam); + this.currentParam = 0; + if (ch === ";") { + break; + } + this.state = State.normal; + switch (ch) { + case "A": + this.cursorUp(this.params); + break; + case "B": + this.cursorDown(this.params); + break; + case "C": + this.cursorForward(this.params); + break; + case "D": + this.cursorBackward(this.params); + break; + case "H": + this.cursorPos(this.params); + break; + case "J": + this.eraseInDisplay(this.params); + break; + case "K": + this.eraseInLine(this.params); + break; + case "m": + if (!this.prefix) { + this.charAttributes(this.params); + } + break; + case "n": + if (!this.prefix) { + this.deviceStatus(this.params); + } + break; + case "@": + this.insertChars(this.params); + break; + case "E": + this.cursorNextLine(this.params); + break; + case "F": + this.cursorPrecedingLine(this.params); + break; + case "G": + this.cursorCharAbsolute(this.params); + break; + case "L": + this.insertLines(this.params); + break; + case "M": + this.deleteLines(this.params); + break; + case "P": + this.deleteChars(this.params); + break; + case "X": + this.eraseChars(this.params); + break; + case "`": + this.charPosAbsolute(this.params); + break; + case "a": + this.HPositionRelative(this.params); + break; + case "c": + this.sendDeviceAttributes(this.params); + break; + case "d": + this.linePosAbsolute(this.params); + break; + case "e": + this.VPositionRelative(this.params); + break; + case "f": + this.HVPosition(this.params); + break; + case "h": + this.setMode(this.params); + break; + case "l": + this.resetMode(this.params); + break; + case "r": + this.setScrollRegion(this.params); + break; + case "s": + this.saveCursor(this.params); + break; + case "u": + this.restoreCursor(this.params); + break; + case "I": + this.cursorForwardTab(this.params); + break; + case "S": + this.scrollUp(this.params); + break; + case "T": + if (this.params.length < 2 && !this.prefix) { + this.scrollDown(this.params); + } + break; + case "Z": + this.cursorBackwardTab(this.params); + break; + case "b": + this.repeatPrecedingCharacter(this.params); + break; + case "g": + this.tabClear(this.params); + break; + case "p": + if (this.prefix === '!') { + this.softReset(this.params); + } + break; + default: + this.error("Unknown CSI code: %s.", ch); + } + this.prefix = ""; + break; + case State.dcs: + if (ch === "\x1b" || ch === "\x07") { + if (ch === "\x1b") { + i++; + } + switch (this.prefix) { + case "": + break; + case "$q": + pt = this.currentParam; + valid = false; + switch (pt) { + case "\"q": + pt = "0\"q"; + break; + case "\"p": + pt = "61\"p"; + break; + case "r": + pt = "" + (this.scrollTop + 1) + ";" + (this.scrollBottom + 1) + "r"; + break; + case "m": + pt = "0m"; + break; + default: + this.error("Unknown DCS Pt: %s.", pt); + pt = ""; + } + this.send("\x1bP" + +valid + "$r" + pt + "\x1b\\"); + break; + case "+q": + pt = this.currentParam; + valid = false; + this.send("\x1bP" + +valid + "+r" + pt + "\x1b\\"); + break; + default: + this.error("Unknown DCS prefix: %s.", this.prefix); + } + this.currentParam = 0; + this.prefix = ""; + this.state = State.normal; + } else if (!this.currentParam) { + if (!this.prefix && ch !== "$" && ch !== "+") { + this.currentParam = ch; + } else if (this.prefix.length === 2) { + this.currentParam = ch; + } else { + this.prefix += ch; + } + } else { + this.currentParam += ch; + } + break; + case State.ignore: + if (ch === "\x1b" || ch === "\x07") { + if (ch === "\x1b") { + i++; + } + this.state = State.normal; + } + } + i++; + } + this.updateRange(this.y); + return this.refresh(this.refreshStart, this.refreshEnd); + }; + + BackTerminal.prototype.writeln = function(data) { + return this.write("" + data + "\r\n"); + }; + + BackTerminal.prototype.setgLevel = function(g) { + this.glevel = g; + return this.charset = this.charsets[g]; + }; + + BackTerminal.prototype.setgCharset = function(g, charset) { + this.charsets[g] = charset; + if (this.glevel === g) { + return this.charset = charset; + } + }; + + BackTerminal.prototype.resize = function(cols, rows) { + var ch, i, j; + this.cols = cols; + this.rows = rows; + if (old_cols < this.cols) { + ch = [this.defAttr, " "]; + i = this.lines.length; + while (i--) { + while (this.lines[i].length < this.cols) { + this.lines[i].push(ch); + } + } + } else if (old_cols > this.cols) { + i = this.lines.length; + while (i--) { + while (this.lines[i].length > this.cols) { + this.lines[i].pop(); + } + } + } + this.setupStops(old_cols); + j = old_rows; + if (j < this.rows) { + while (j++ < this.rows) { + if (this.lines.length < this.rows + this.ybase) { + this.lines.push(this.blankLine()); + } + } + } else if (j > this.rows) { + while (j-- > this.rows) { + if (this.lines.length > this.rows + this.ybase) { + this.lines.pop(); + } + } + } + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + if (this.x >= this.cols) { + this.x = this.cols - 1; + } + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + return this.normal = null; + }; + + BackTerminal.prototype.updateRange = function(y) { + if (y < this.refreshStart) { + this.refreshStart = y; + } + if (y > this.refreshEnd) { + return this.refreshEnd = y; + } + }; + + BackTerminal.prototype.maxRange = function() { + this.refreshStart = 0; + return this.refreshEnd = this.rows - 1; + }; + + BackTerminal.prototype.setupStops = function(i) { + var _results; + if (i != null) { + if (!this.tabs[i]) { + i = this.prevStop(i); + } + } else { + this.tabs = {}; + i = 0; + } + _results = []; + while (i < this.cols) { + this.tabs[i] = true; + _results.push(i += 8); + } + return _results; + }; + + BackTerminal.prototype.prevStop = function(x) { + if (x == null) { + x = this.x; + } + while (!this.tabs[--x] && x > 0) { + 1; + } + if (x >= this.cols) { + return this.cols - 1; + } else { + if (x < 0) { + return 0; + } else { + return x; + } + } + }; + + BackTerminal.prototype.nextStop = function(x) { + if (x == null) { + x = this.x; + } + while (!this.tabs[++x] && x < this.cols) { + 1; + } + if (x >= this.cols) { + return this.cols - 1; + } else { + if (x < 0) { + return 0; + } else { + return x; + } + } + }; + + BackTerminal.prototype.eraseRight = function(x, y) { + var ch, line; + line = this.lines[this.ybase + y]; + ch = [this.eraseAttr(), " "]; + while (x < this.cols) { + line[x] = ch; + x++; + } + return this.updateRange(y); + }; + + BackTerminal.prototype.eraseLeft = function(x, y) { + var ch, line; + line = this.lines[this.ybase + y]; + ch = [this.eraseAttr(), " "]; + x++; + while (x--) { + line[x] = ch; + } + return this.updateRange(y); + }; + + BackTerminal.prototype.eraseLine = function(y) { + return this.eraseRight(0, y); + }; + + BackTerminal.prototype.blankLine = function(cur) { + var attr, ch, i, line; + attr = (cur ? this.eraseAttr() : this.defAttr); + ch = [attr, " "]; + line = []; + i = 0; + while (i < this.cols) { + line[i] = ch; + i++; + } + return line; + }; + + BackTerminal.prototype.ch = function(cur) { + if (cur) { + return [this.eraseAttr(), " "]; + } else { + return [this.defAttr, " "]; + } + }; + + BackTerminal.prototype.isterm = function(term) { + return ("" + this.termName).indexOf(term) === 0; + }; + + BackTerminal.prototype.handler = function(data) { + return this.out(data); + }; + + BackTerminal.prototype.handleTitle = function(title) {}; + + BackTerminal.prototype.index = function() { + this.y++; + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } + return this.state = State.normal; + }; + + BackTerminal.prototype.reverseIndex = function() { + var j; + this.y--; + if (this.y < this.scrollTop) { + this.y++; + this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); + j = this.rows - 1 - this.scrollBottom; + this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); + this.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + } + return this.state = State.normal; + }; + + BackTerminal.prototype.reset = function() { + this.reset_vars(); + return this.refresh(0, this.rows - 1); + }; + + BackTerminal.prototype.tabSet = function() { + this.tabs[this.x] = true; + return this.state = State.normal; + }; + + BackTerminal.prototype.cursorUp = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y -= param; + if (this.y < 0) { + return this.y = 0; + } + }; + + BackTerminal.prototype.cursorDown = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y += param; + if (this.y >= this.rows) { + return this.y = this.rows - 1; + } + }; + + BackTerminal.prototype.cursorForward = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.x += param; + if (this.x >= this.cols) { + return this.x = this.cols - 1; + } + }; + + BackTerminal.prototype.cursorBackward = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.x -= param; + if (this.x < 0) { + return this.x = 0; + } + }; + + BackTerminal.prototype.cursorPos = function(params) { + var col, row; + row = params[0] - 1; + if (params.length >= 2) { + col = params[1] - 1; + } else { + col = 0; + } + if (row < 0) { + row = 0; + } else { + if (row >= this.rows) { + row = this.rows - 1; + } + } + if (col < 0) { + col = 0; + } else { + if (col >= this.cols) { + col = this.cols - 1; + } + } + this.x = col; + return this.y = row; + }; + + BackTerminal.prototype.eraseInDisplay = function(params) { + var j, _results, _results1, _results2; + switch (params[0]) { + case 0: + this.eraseRight(this.x, this.y); + j = this.y + 1; + _results = []; + while (j < this.rows) { + this.eraseLine(j); + _results.push(j++); + } + return _results; + break; + case 1: + this.eraseLeft(this.x, this.y); + j = this.y; + _results1 = []; + while (j--) { + _results1.push(this.eraseLine(j)); + } + return _results1; + break; + case 2: + j = this.rows; + _results2 = []; + while (j--) { + _results2.push(this.eraseLine(j)); + } + return _results2; + } + }; + + BackTerminal.prototype.eraseInLine = function(params) { + switch (params[0]) { + case 0: + return this.eraseRight(this.x, this.y); + case 1: + return this.eraseLeft(this.x, this.y); + case 2: + return this.eraseLine(this.y); + } + }; + + BackTerminal.prototype.charAttributes = function(params) { + var bg, fg, flags, i, l, p; + if (params.length === 1 && params[0] === 0) { + this.curAttr = this.defAttr; + return; + } + flags = this.curAttr >> 18; + fg = (this.curAttr >> 9) & 0x1ff; + bg = this.curAttr & 0x1ff; + l = params.length; + i = 0; + while (i < l) { + p = params[i]; + if (p >= 30 && p <= 37) { + fg = p - 30; + } else if (p >= 40 && p <= 47) { + bg = p - 40; + } else if (p >= 90 && p <= 97) { + p += 8; + fg = p - 90; + } else if (p >= 100 && p <= 107) { + p += 8; + bg = p - 100; + } else if (p === 0) { + flags = this.defAttr >> 18; + fg = (this.defAttr >> 9) & 0x1ff; + bg = this.defAttr & 0x1ff; + } else if (p === 1) { + flags |= 1; + } else if (p === 4) { + flags |= 2; + } else if (p === 5) { + flags |= 4; + } else if (p === 7) { + flags |= 8; + } else if (p === 8) { + flags |= 16; + } else if (p === 22) { + flags &= ~1; + } else if (p === 24) { + flags &= ~2; + } else if (p === 25) { + flags &= ~4; + } else if (p === 27) { + flags &= ~8; + } else if (p === 28) { + flags &= ~16; + } else if (p === 39) { + fg = (this.defAttr >> 9) & 0x1ff; + } else if (p === 49) { + bg = this.defAttr & 0x1ff; + } else if (p === 38) { + if (params[i + 1] === 2) { + i += 2; + fg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff; + i += 2; + } else if (params[i + 1] === 5) { + i += 2; + fg = params[i] & 0xff; + } + } else if (p === 48) { + if (params[i + 1] === 2) { + i += 2; + bg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff; + i += 2; + } else if (params[i + 1] === 5) { + i += 2; + bg = params[i] & 0xff; + } + } else if (p === 100) { + fg = (this.defAttr >> 9) & 0x1ff; + bg = this.defAttr & 0x1ff; + } else { + this.error("Unknown SGR attribute: %d.", p); + } + i++; + } + return this.curAttr = (flags << 18) | (fg << 9) | bg; + }; + + BackTerminal.prototype.deviceStatus = function(params) { + if (!this.prefix) { + switch (params[0]) { + case 5: + return this.send("\x1b[0n"); + case 6: + return this.send("\x1b[" + (this.y + 1) + ";" + (this.x + 1) + "R"); + } + } else if (this.prefix === "?") { + if (params[0] === 6) { + return this.send("\x1b[?" + (this.y + 1) + ";" + (this.x + 1) + "R"); + } + } + }; + + BackTerminal.prototype.insertChars = function(params) { + var ch, j, param, row, _results; + param = params[0]; + if (param < 1) { + param = 1; + } + row = this.y + this.ybase; + j = this.x; + ch = [this.eraseAttr(), " "]; + _results = []; + while (param-- && j < this.cols) { + this.lines[row].splice(j++, 0, ch); + _results.push(this.lines[row].pop()); + } + return _results; + }; + + BackTerminal.prototype.cursorNextLine = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y += param; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + return this.x = 0; + }; + + BackTerminal.prototype.cursorPrecedingLine = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y -= param; + if (this.y < 0) { + this.y = 0; + } + return this.x = 0; + }; + + BackTerminal.prototype.cursorCharAbsolute = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + return this.x = param - 1; + }; + + BackTerminal.prototype.insertLines = function(params) { + var j, param, row; + param = params[0]; + if (param < 1) { + param = 1; + } + row = this.y + this.ybase; + j = this.rows - 1 - this.scrollBottom; + j = this.rows - 1 + this.ybase - j + 1; + while (param--) { + this.lines.splice(row, 0, this.blankLine(true)); + this.lines.splice(j, 1); + } + this.updateRange(this.y); + return this.updateRange(this.scrollBottom); + }; + + BackTerminal.prototype.deleteLines = function(params) { + var j, param, row; + param = params[0]; + if (param < 1) { + param = 1; + } + row = this.y + this.ybase; + j = this.rows - 1 - this.scrollBottom; + j = this.rows - 1 + this.ybase - j; + while (param--) { + this.lines.splice(j + 1, 0, this.blankLine(true)); + this.lines.splice(row, 1); + } + this.updateRange(this.y); + return this.updateRange(this.scrollBottom); + }; + + BackTerminal.prototype.deleteChars = function(params) { + var ch, param, row, _results; + param = params[0]; + if (param < 1) { + param = 1; + } + row = this.y + this.ybase; + ch = [this.eraseAttr(), " "]; + _results = []; + while (param--) { + this.lines[row].splice(this.x, 1); + _results.push(this.lines[row].push(ch)); + } + return _results; + }; + + BackTerminal.prototype.eraseChars = function(params) { + var ch, j, param, row, _results; + param = params[0]; + if (param < 1) { + param = 1; + } + row = this.y + this.ybase; + j = this.x; + ch = [this.eraseAttr(), " "]; + _results = []; + while (param-- && j < this.cols) { + _results.push(this.lines[row][j++] = ch); + } + return _results; + }; + + BackTerminal.prototype.charPosAbsolute = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.x = param - 1; + if (this.x >= this.cols) { + return this.x = this.cols - 1; + } + }; + + BackTerminal.prototype.HPositionRelative = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.x += param; + if (this.x >= this.cols) { + return this.x = this.cols - 1; + } + }; + + BackTerminal.prototype.sendDeviceAttributes = function(params) { + if (params[0] > 0) { + return; + } + if (!this.prefix) { + if (this.isterm("xterm") || this.isterm("rxvt-unicode") || this.isterm("screen")) { + return this.send("\x1b[?1;2c"); + } else { + if (this.isterm("linux")) { + return this.send("\x1b[?6c"); + } + } + } else if (this.prefix === ">") { + if (this.isterm("xterm")) { + return this.send("\x1b[>0;276;0c"); + } else if (this.isterm("rxvt-unicode")) { + return this.send("\x1b[>85;95;0c"); + } else if (this.isterm("linux")) { + return this.send(params[0] + "c"); + } else { + if (this.isterm("screen")) { + return this.send("\x1b[>83;40003;0c"); + } + } + } + }; + + BackTerminal.prototype.linePosAbsolute = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y = param - 1; + if (this.y >= this.rows) { + return this.y = this.rows - 1; + } + }; + + BackTerminal.prototype.VPositionRelative = function(params) { + var param; + param = params[0]; + if (param < 1) { + param = 1; + } + this.y += param; + if (this.y >= this.rows) { + return this.y = this.rows - 1; + } + }; + + BackTerminal.prototype.HVPosition = function(params) { + if (params[0] < 1) { + params[0] = 1; + } + if (params[1] < 1) { + params[1] = 1; + } + this.y = params[0] - 1; + if (this.y >= this.rows) { + this.y = this.rows - 1; + } + this.x = params[1] - 1; + if (this.x >= this.cols) { + return this.x = this.cols - 1; + } + }; + + BackTerminal.prototype.setMode = function(params) { + var i, l, normal; + if (typeof params === "object") { + l = params.length; + i = 0; + while (i < l) { + this.setMode(params[i]); + i++; + } + return; + } + if (this.prefix === "?") { + switch (params) { + case 1: + return this.applicationCursor = true; + case 2: + this.setgCharset(0, BackTerminal.prototype.charsets.US); + this.setgCharset(1, BackTerminal.prototype.charsets.US); + this.setgCharset(2, BackTerminal.prototype.charsets.US); + return this.setgCharset(3, BackTerminal.prototype.charsets.US); + case 3: + this.savedCols = this.cols; + return this.resize(132, this.rows); + case 6: + return this.originMode = true; + case 7: + return this.wraparoundMode = true; + case 66: + return this.applicationKeypad = true; + case 9: + case 1000: + case 1002: + case 1003: + this.x10Mouse = params === 9; + this.vt200Mouse = params === 1000; + this.normalMouse = params > 1000; + this.mouseEvents = true; + return this.element.style.cursor = "default"; + case 1004: + return this.sendFocus = true; + case 1005: + return this.utfMouse = true; + case 1006: + return this.sgrMouse = true; + case 1015: + return this.urxvtMouse = true; + case 25: + return this.cursorHidden = false; + case 1049: + case 47: + case 1047: + if (!this.normal) { + normal = { + lines: this.lines, + ybase: this.ybase, + ydisp: this.ydisp, + x: this.x, + y: this.y, + scrollTop: this.scrollTop, + scrollBottom: this.scrollBottom, + tabs: this.tabs + }; + this.reset(); + this.normal = normal; + return this.showCursor(); + } + } + } + }; + + BackTerminal.prototype.resetMode = function(params) { + var i, l; + if (typeof params === "object") { + l = params.length; + i = 0; + while (i < l) { + this.resetMode(params[i]); + i++; + } + return; + } + if (this.prefix === "?") { + switch (params) { + case 1: + return this.applicationCursor = false; + case 3: + if (this.cols === 132 && this.savedCols) { + this.resize(this.savedCols, this.rows); + } + return delete this.savedCols; + case 6: + return this.originMode = false; + case 7: + return this.wraparoundMode = false; + case 66: + return this.applicationKeypad = false; + case 9: + case 1000: + case 1002: + case 1003: + this.x10Mouse = false; + this.vt200Mouse = false; + this.normalMouse = false; + this.mouseEvents = false; + return this.element.style.cursor = ""; + case 1004: + return this.sendFocus = false; + case 1005: + return this.utfMouse = false; + case 1006: + return this.sgrMouse = false; + case 1015: + return this.urxvtMouse = false; + case 25: + return this.cursorHidden = true; + case 1049: + case 47: + case 1047: + if (this.normal) { + this.lines = this.normal.lines; + this.ybase = this.normal.ybase; + this.ydisp = this.normal.ydisp; + this.x = this.normal.x; + this.y = this.normal.y; + this.scrollTop = this.normal.scrollTop; + this.scrollBottom = this.normal.scrollBottom; + this.tabs = this.normal.tabs; + this.normal = null; + this.refresh(0, this.rows - 1); + return this.showCursor(); + } + } + } + }; + + BackTerminal.prototype.setScrollRegion = function(params) { + if (this.prefix) { + return; + } + this.scrollTop = (params[0] || 1) - 1; + this.scrollBottom = (params[1] || this.rows) - 1; + this.x = 0; + return this.y = 0; + }; + + BackTerminal.prototype.saveCursor = function(params) { + this.savedX = this.x; + return this.savedY = this.y; + }; + + BackTerminal.prototype.restoreCursor = function(params) { + this.x = this.savedX || 0; + return this.y = this.savedY || 0; + }; + + BackTerminal.prototype.cursorForwardTab = function(params) { + var param, _results; + param = params[0] || 1; + _results = []; + while (param--) { + _results.push(this.x = this.nextStop()); + } + return _results; + }; + + BackTerminal.prototype.scrollUp = function(params) { + var param; + param = params[0] || 1; + while (param--) { + this.lines.splice(this.ybase + this.scrollTop, 1); + this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); + } + this.updateRange(this.scrollTop); + return this.updateRange(this.scrollBottom); + }; + + BackTerminal.prototype.scrollDown = function(params) { + var param; + param = params[0] || 1; + while (param--) { + this.lines.splice(this.ybase + this.scrollBottom, 1); + this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); + } + this.updateRange(this.scrollTop); + return this.updateRange(this.scrollBottom); + }; + + BackTerminal.prototype.initMouseTracking = function(params) {}; + + BackTerminal.prototype.resetTitleModes = function(params) {}; + + BackTerminal.prototype.cursorBackwardTab = function(params) { + var param, _results; + param = params[0] || 1; + _results = []; + while (param--) { + _results.push(this.x = this.prevStop()); + } + return _results; + }; + + BackTerminal.prototype.repeatPrecedingCharacter = function(params) { + var ch, line, param, _results; + param = params[0] || 1; + line = this.lines[this.ybase + this.y]; + ch = line[this.x - 1] || [this.defAttr, " "]; + _results = []; + while (param--) { + _results.push(line[this.x++] = ch); + } + return _results; + }; + + BackTerminal.prototype.tabClear = function(params) { + var param; + param = params[0]; + if (param <= 0) { + return delete this.tabs[this.x]; + } else { + if (param === 3) { + return this.tabs = {}; + } + } + }; + + BackTerminal.prototype.mediaCopy = function(params) {}; + + BackTerminal.prototype.setResources = function(params) {}; + + BackTerminal.prototype.disableModifiers = function(params) {}; + + BackTerminal.prototype.setPointerMode = function(params) {}; + + BackTerminal.prototype.softReset = function(params) { + this.cursorHidden = false; + this.insertMode = false; + this.originMode = false; + this.wraparoundMode = false; + this.applicationKeypad = false; + this.applicationCursor = false; + this.scrollTop = 0; + this.scrollBottom = this.rows - 1; + this.curAttr = this.defAttr; + this.x = this.y = 0; + this.charset = null; + this.glevel = 0; + return this.charsets = [null]; + }; + + BackTerminal.prototype.requestAnsiMode = function(params) {}; + + BackTerminal.prototype.requestPrivateMode = function(params) {}; + + BackTerminal.prototype.setConformanceLevel = function(params) {}; + + BackTerminal.prototype.loadLEDs = function(params) {}; + + BackTerminal.prototype.setCursorStyle = function(params) {}; + + BackTerminal.prototype.setCharProtectionAttr = function(params) {}; + + BackTerminal.prototype.restorePrivateValues = function(params) {}; + + BackTerminal.prototype.setAttrInRectangle = function(params) { + var attr, b, i, l, line, r, t; + t = params[0]; + l = params[1]; + b = params[2]; + r = params[3]; + attr = params[4]; + while (t < b + 1) { + line = this.lines[this.ybase + t]; + i = l; + while (i < r) { + line[i] = [attr, line[i][1]]; + i++; + } + t++; + } + this.updateRange(params[0]); + return this.updateRange(params[2]); + }; + + BackTerminal.prototype.savePrivateValues = function(params) {}; + + BackTerminal.prototype.manipulateWindow = function(params) {}; + + BackTerminal.prototype.reverseAttrInRectangle = function(params) {}; + + BackTerminal.prototype.setTitleModeFeature = function(params) {}; + + BackTerminal.prototype.setWarningBellVolume = function(params) {}; + + BackTerminal.prototype.setMarginBellVolume = function(params) {}; + + BackTerminal.prototype.copyRectangle = function(params) {}; + + BackTerminal.prototype.enableFilterRectangle = function(params) {}; + + BackTerminal.prototype.requestParameters = function(params) {}; + + BackTerminal.prototype.selectChangeExtent = function(params) {}; + + BackTerminal.prototype.fillRectangle = function(params) { + var b, ch, i, l, line, r, t; + ch = params[0]; + t = params[1]; + l = params[2]; + b = params[3]; + r = params[4]; + while (t < b + 1) { + line = this.lines[this.ybase + t]; + i = l; + while (i < r) { + line[i] = [line[i][0], String.fromCharCode(ch)]; + i++; + } + t++; + } + this.updateRange(params[1]); + return this.updateRange(params[3]); + }; + + BackTerminal.prototype.enableLocatorReporting = function(params) { + var val; + return val = params[0] > 0; + }; + + BackTerminal.prototype.eraseRectangle = function(params) { + var b, ch, i, l, line, r, t; + t = params[0]; + l = params[1]; + b = params[2]; + r = params[3]; + ch = [this.eraseAttr(), " "]; + while (t < b + 1) { + line = this.lines[this.ybase + t]; + i = l; + while (i < r) { + line[i] = ch; + i++; + } + t++; + } + this.updateRange(params[0]); + return this.updateRange(params[2]); + }; + + BackTerminal.prototype.setLocatorEvents = function(params) {}; + + BackTerminal.prototype.selectiveEraseRectangle = function(params) {}; + + BackTerminal.prototype.requestLocatorPosition = function(params) {}; + + BackTerminal.prototype.insertColumns = function() { + var ch, i, l, param; + param = params[0]; + l = this.ybase + this.rows; + ch = [this.eraseAttr(), " "]; + while (param--) { + i = this.ybase; + while (i < l) { + this.lines[i].splice(this.x + 1, 0, ch); + this.lines[i].pop(); + i++; + } + } + return this.maxRange(); + }; + + BackTerminal.prototype.deleteColumns = function() { + var ch, i, l, param; + param = params[0]; + l = this.ybase + this.rows; + ch = [this.eraseAttr(), " "]; + while (param--) { + i = this.ybase; + while (i < l) { + this.lines[i].splice(this.x, 1); + this.lines[i].push(ch); + i++; + } + } + return this.maxRange(); + }; + + BackTerminal.prototype.charsets = { + SCLD: { + "`": "◆", + a: "▒", + b: "\t", + c: "\f", + d: "\r", + e: "\n", + f: "°", + g: "±", + h: "␤", + i: "\x0b", + j: "┘", + k: "┐", + l: "┌", + m: "└", + n: "┼", + o: "⎺", + p: "⎻", + q: "─", + r: "⎼", + s: "⎽", + t: "├", + u: "┤", + v: "┴", + w: "┬", + x: "│", + y: "≤", + z: "≥", + "{": "π", + "|": "≠", + "}": "£", + "~": "·" + }, + UK: null, + US: null, + Dutch: null, + Finnish: null, + French: null, + FrenchCanadian: null, + German: null, + Italian: null, + NorwegianDanish: null, + Spanish: null, + Swedish: null, + Swiss: null, + ISOLatin: null + }; + + return BackTerminal; + +})(); diff --git a/dev.py b/dev.py index c47f43a..188ecbf 100755 --- a/dev.py +++ b/dev.py @@ -8,7 +8,7 @@ import shlex commands = [ 'coffee -wcb -j butterfly/static/javascripts/main.js ' + - 'butterfly/static/coffees/term.coffee ' + + # 'butterfly/static/coffees/term.coffee ' + 'butterfly/static/coffees/backsel.coffee ' + 'butterfly/static/coffees/virtual_input.coffee ' + 'butterfly/static/coffees/main.coffee ',