Update the player to use backend-generated JSON frames
parent
a1cd6ca2ae
commit
ee636bf958
@ -1,286 +0,0 @@
|
|||||||
class AsciiIo.AnsiInterpreter
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@sgrInterpreter = new AsciiIo.SgrInterpreter()
|
|
||||||
|
|
||||||
parse: (data) ->
|
|
||||||
@commands = []
|
|
||||||
|
|
||||||
while data.length > 0
|
|
||||||
processed = @handleData data
|
|
||||||
|
|
||||||
if processed is 0
|
|
||||||
# console.log "no kurwa: #{@formattedData(@data)}"
|
|
||||||
break
|
|
||||||
|
|
||||||
data = data.slice processed
|
|
||||||
|
|
||||||
[@commands, data]
|
|
||||||
|
|
||||||
cb: ->
|
|
||||||
@commands.push arguments
|
|
||||||
|
|
||||||
handleData: (data) ->
|
|
||||||
if data.match(/^\x1b[\x00-\x1f]/)
|
|
||||||
@handleControlCharacter(data[1])
|
|
||||||
return 2
|
|
||||||
|
|
||||||
else if match = data.match(/^(\x1b\x5d|\x9d).*?(\x1b\\|\x9c|\x07)/)
|
|
||||||
# OSC seq
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if match = data.match(/^(\x1b[PX_^]|[\x90\x98\x9e\x9f]).*?(\x1b\\|\x9c)/)
|
|
||||||
# DCS/SOS/PM/APC seq
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if match = data.match(/^(?:\x1b\x5b|\x9b)([\x30-\x3f]*?)[\x20-\x2f]*?[\x40-\x7e]/)
|
|
||||||
# Control sequences
|
|
||||||
@handleControlSequence(match[0], match[1], match)
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x30-\x3f]/)
|
|
||||||
@handlePrivateEscSeq(match[0])
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x40-\x5a\x5c\x5e-\x7e]/)
|
|
||||||
# excluding \x5b ([) and \x5d (])
|
|
||||||
# they're both handled above
|
|
||||||
@handleStandardEscSeq(match[0])
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if data.match(/^\x1b\x7f/) # DELETE
|
|
||||||
return 2
|
|
||||||
|
|
||||||
else if data.match(/^[\x00-\x1a\x1c-\x1f]/) # excluding \x1b "ESC"
|
|
||||||
@handleControlCharacter(data[0])
|
|
||||||
return 1
|
|
||||||
|
|
||||||
else if match = data.match(/^([\x20-\x7e]|[\xe2-\xe8]..|[\xc2-\xc5].|[\xa1-\xfe])+/)
|
|
||||||
@handlePrintableCharacters(match[0])
|
|
||||||
return match[0].length
|
|
||||||
|
|
||||||
else if data[0] is "\x7f"
|
|
||||||
# DELETE, always and everywhere ignored
|
|
||||||
return 1
|
|
||||||
|
|
||||||
else if data.match(/^[\x80-\x9f]/)
|
|
||||||
@handleControlCharacter(data[0])
|
|
||||||
return 1
|
|
||||||
|
|
||||||
else if data[0] is "\xa0"
|
|
||||||
# Same as SPACE (\x20)
|
|
||||||
@handlePrintableCharacters(' ')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
else if data[0] is "\xff"
|
|
||||||
# Same as DELETE (\x7f)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
|
|
||||||
handleControlCharacter: (char) ->
|
|
||||||
action = switch char
|
|
||||||
when "\x07"
|
|
||||||
'bell'
|
|
||||||
when "\x08"
|
|
||||||
'backspace'
|
|
||||||
when "\x09"
|
|
||||||
'goToNextHorizontalTabStop'
|
|
||||||
when "\x0a"
|
|
||||||
'lineFeed'
|
|
||||||
when "\x0b"
|
|
||||||
'verticalTab'
|
|
||||||
when "\x0c"
|
|
||||||
'formFeed'
|
|
||||||
when "\x0d"
|
|
||||||
'carriageReturn'
|
|
||||||
when "\x84"
|
|
||||||
'index' # "ESC D"
|
|
||||||
when "\x85"
|
|
||||||
'newLine' # "ESC E"
|
|
||||||
when "\x88"
|
|
||||||
'setHorizontalTabStop' # "ESC H"
|
|
||||||
when "\x8d"
|
|
||||||
'reverseIndex' # "ESC M"
|
|
||||||
|
|
||||||
@cb action if action
|
|
||||||
|
|
||||||
handlePrintableCharacters: (text) ->
|
|
||||||
@cb 'print', text
|
|
||||||
|
|
||||||
handleStandardEscSeq: (data) ->
|
|
||||||
last = data[data.length - 1]
|
|
||||||
intermediate = data[data.length - 2]
|
|
||||||
|
|
||||||
action = switch last
|
|
||||||
when "A"
|
|
||||||
if intermediate is '('
|
|
||||||
'setUkCharset'
|
|
||||||
when "B"
|
|
||||||
if intermediate is '('
|
|
||||||
'setUsCharset'
|
|
||||||
when "D"
|
|
||||||
'index'
|
|
||||||
when "E"
|
|
||||||
'newLine'
|
|
||||||
when "H"
|
|
||||||
'setHorizontalTabStop'
|
|
||||||
when "M"
|
|
||||||
'reverseIndex'
|
|
||||||
when "c"
|
|
||||||
'resetTerminal'
|
|
||||||
|
|
||||||
@cb action if action
|
|
||||||
|
|
||||||
handlePrivateEscSeq: (data) ->
|
|
||||||
last = data[data.length - 1]
|
|
||||||
intermediate = data[data.length - 2]
|
|
||||||
|
|
||||||
action = switch last
|
|
||||||
when "0"
|
|
||||||
if intermediate is '('
|
|
||||||
'setSpecialCharset'
|
|
||||||
when "7"
|
|
||||||
'saveTerminalState'
|
|
||||||
when "8"
|
|
||||||
'restoreTerminalState'
|
|
||||||
|
|
||||||
@cb action if action
|
|
||||||
|
|
||||||
handleControlSequence: (data, params, match) ->
|
|
||||||
if params and params.match(/^[\x3c-\x3f]/)
|
|
||||||
@handlePrivateControlSequence(data, params)
|
|
||||||
else
|
|
||||||
@handleStandardControlSequence(data, params)
|
|
||||||
|
|
||||||
handleStandardControlSequence: (data, params) ->
|
|
||||||
term = data[data.length - 1]
|
|
||||||
|
|
||||||
numbers = @parseParams params
|
|
||||||
n = numbers[0]
|
|
||||||
m = numbers[1]
|
|
||||||
|
|
||||||
switch term
|
|
||||||
when "@"
|
|
||||||
@cb 'reserveCharacters', n
|
|
||||||
when "A"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
@cb 'priorRow', n
|
|
||||||
when "B"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
@cb 'nextRow', n
|
|
||||||
when "C"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
@cb 'nextColumn', n
|
|
||||||
when "D"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
@cb 'priorColumn', n
|
|
||||||
when "E"
|
|
||||||
@cb 'nextRowFirstColumn', n
|
|
||||||
when "F"
|
|
||||||
@cb 'priorRowFirstColumn', n
|
|
||||||
when "G"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
@cb 'goToColumn', n
|
|
||||||
when "H"
|
|
||||||
n = 1 if n is undefined
|
|
||||||
m = 1 if m is undefined
|
|
||||||
@cb 'goToRowAndColumn', n, m
|
|
||||||
when "I"
|
|
||||||
@cb 'goToNextHorizontalTabStop', n
|
|
||||||
when "J"
|
|
||||||
if n is 2
|
|
||||||
@cb 'eraseScreen'
|
|
||||||
else if n is 1
|
|
||||||
@cb 'eraseFromScreenStart'
|
|
||||||
else
|
|
||||||
@cb 'eraseToScreenEnd'
|
|
||||||
when "K"
|
|
||||||
if n is 2
|
|
||||||
@cb 'eraseRow'
|
|
||||||
else if n is 1
|
|
||||||
@cb 'eraseFromRowStart'
|
|
||||||
else
|
|
||||||
@cb 'eraseToRowEnd'
|
|
||||||
when "L"
|
|
||||||
@cb 'insertLines', n or 1
|
|
||||||
when "M"
|
|
||||||
@cb 'deleteLines', n or 1
|
|
||||||
when "P" # DCH - Delete Character, from current position to end of field
|
|
||||||
@cb 'deleteCharacters', n or 1
|
|
||||||
when "S"
|
|
||||||
@cb 'scrollUp', n
|
|
||||||
when "T"
|
|
||||||
@cb 'scrollDown', n
|
|
||||||
when "X"
|
|
||||||
@cb 'eraseCharacters', n
|
|
||||||
when "Z"
|
|
||||||
@cb 'goToPriorHorizontalTabStop', n
|
|
||||||
when "b"
|
|
||||||
@cb 'repeatLastCharacter', n
|
|
||||||
when "d" # VPA - Vertical Position Absolute
|
|
||||||
@cb 'goToRow', n
|
|
||||||
when "f"
|
|
||||||
@cb 'goToRowAndColumn', n, m
|
|
||||||
when "g"
|
|
||||||
if !n or n is 0
|
|
||||||
@cb 'clearHorizontalTabStop'
|
|
||||||
else if n is 3
|
|
||||||
@cb 'clearAllHorizontalTabStops'
|
|
||||||
when "l" # l, Reset mode
|
|
||||||
console.log "(TODO) reset: " + n
|
|
||||||
when "m"
|
|
||||||
@handleSGR numbers
|
|
||||||
when "n"
|
|
||||||
@cb 'reportRowAndColumn'
|
|
||||||
when "r" # Set top and bottom margins (scroll region on VT100)
|
|
||||||
if n is undefined
|
|
||||||
n = 1
|
|
||||||
if m is undefined
|
|
||||||
m = @lines
|
|
||||||
@cb 'setScrollRegion', n, m
|
|
||||||
|
|
||||||
handlePrivateControlSequence: (data, params) ->
|
|
||||||
action = data[data.length - 1]
|
|
||||||
modes = @parseParams params
|
|
||||||
|
|
||||||
for mode in modes
|
|
||||||
if mode is 25
|
|
||||||
if action is "h"
|
|
||||||
@cb 'showCursor'
|
|
||||||
else if action is "l"
|
|
||||||
@cb 'hideCursor'
|
|
||||||
else if mode is 47
|
|
||||||
if action is "h"
|
|
||||||
@cb 'switchToAlternateBuffer'
|
|
||||||
else if action is "l"
|
|
||||||
@cb 'switchToNormalBuffer'
|
|
||||||
else if mode is 1049
|
|
||||||
if action is "h"
|
|
||||||
# Save cursor position, switch to alternate screen buffer, and clear screen.
|
|
||||||
@cb 'switchToAlternateBuffer'
|
|
||||||
@cb 'eraseScreen'
|
|
||||||
else if action is "l"
|
|
||||||
# Clear screen, switch to normal screen buffer, and restore cursor position.
|
|
||||||
@cb 'eraseScreen'
|
|
||||||
@cb 'switchToNormalBuffer'
|
|
||||||
|
|
||||||
parseParams: (params) ->
|
|
||||||
if params.length is 0
|
|
||||||
numbers = []
|
|
||||||
else
|
|
||||||
numbers = _(params.replace(/[^0-9;]/, '').split(';')).map (n) ->
|
|
||||||
if n is '' then undefined else parseInt(n, 10)
|
|
||||||
|
|
||||||
numbers
|
|
||||||
|
|
||||||
handleSGR: (numbers) ->
|
|
||||||
numbers = [0] if numbers.length is 0
|
|
||||||
changes = @sgrInterpreter.parse numbers
|
|
||||||
@cb 'updateBrush', changes
|
|
||||||
|
|
||||||
formattedData: (data) ->
|
|
||||||
head = data.slice(0, 100)
|
|
||||||
hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
|
|
||||||
Utf8.decode(head) + " (" + hex.join() + ")"
|
|
@ -1,365 +0,0 @@
|
|||||||
class AsciiIo.ScreenBuffer
|
|
||||||
|
|
||||||
constructor: (@cols, @lines, @scrollRegion, @tabStops) ->
|
|
||||||
@lineData = []
|
|
||||||
@dirtyLines = {}
|
|
||||||
|
|
||||||
@cursorX = 0
|
|
||||||
@cursorY = 0
|
|
||||||
|
|
||||||
@setBrush AsciiIo.Brush.default()
|
|
||||||
@setCharset 'us'
|
|
||||||
|
|
||||||
topMargin: ->
|
|
||||||
@scrollRegion.getTop()
|
|
||||||
|
|
||||||
bottomMargin: ->
|
|
||||||
@scrollRegion.getBottom()
|
|
||||||
|
|
||||||
updateLine: (n = @cursorY) ->
|
|
||||||
@dirtyLines[n] = true
|
|
||||||
|
|
||||||
updateLines: (a, b) ->
|
|
||||||
n = a
|
|
||||||
while n <= b
|
|
||||||
@updateLine n
|
|
||||||
n++
|
|
||||||
|
|
||||||
updateScreen: ->
|
|
||||||
@updateLine n for n in [0...@lines]
|
|
||||||
|
|
||||||
changes: ->
|
|
||||||
changes = {}
|
|
||||||
|
|
||||||
for n, _ of @dirtyLines
|
|
||||||
data = @lineData[n] || []
|
|
||||||
|
|
||||||
fragments = []
|
|
||||||
currentBrush = AsciiIo.Brush.default()
|
|
||||||
currentText = ''
|
|
||||||
|
|
||||||
for i in [0...@cols]
|
|
||||||
d = data[i]
|
|
||||||
|
|
||||||
if d
|
|
||||||
[char, brush] = d
|
|
||||||
else
|
|
||||||
[char, brush] = [' ', currentBrush]
|
|
||||||
|
|
||||||
if brush == currentBrush
|
|
||||||
currentText += char
|
|
||||||
else
|
|
||||||
fragments.push([currentText, currentBrush]) if currentText.length > 0
|
|
||||||
currentText = char
|
|
||||||
currentBrush = brush
|
|
||||||
|
|
||||||
fragments.push([currentText, currentBrush]) if currentText.length > 0
|
|
||||||
changes[n] = fragments
|
|
||||||
|
|
||||||
changes
|
|
||||||
|
|
||||||
clearChanges: ->
|
|
||||||
@dirtyLines = {}
|
|
||||||
|
|
||||||
getLine: (n = @cursorY) ->
|
|
||||||
throw "cant getLine " + n if n >= @lines
|
|
||||||
|
|
||||||
line = @lineData[n]
|
|
||||||
|
|
||||||
if typeof line is "undefined"
|
|
||||||
line = @lineData[n] = []
|
|
||||||
@fill n, 0, @cols, " "
|
|
||||||
|
|
||||||
line
|
|
||||||
|
|
||||||
_addEmptyLine: (l) ->
|
|
||||||
@lineData.splice l, 0, []
|
|
||||||
@clearLineData l
|
|
||||||
|
|
||||||
_removeLine: (l) ->
|
|
||||||
@lineData.splice l, 1
|
|
||||||
|
|
||||||
reserveCharacters: (n) ->
|
|
||||||
line = @getLine()
|
|
||||||
@lineData[@cursorY] = line.slice(0, @cursorX).concat(" ".times(n).split(""), line.slice(@cursorX, @cols - n))
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
fill: (line, col, n, char, brush=@brush) ->
|
|
||||||
lineArr = @getLine(line)
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < n
|
|
||||||
lineArr[col + i] = [char, brush]
|
|
||||||
i++
|
|
||||||
|
|
||||||
deleteCharacters: (n) ->
|
|
||||||
line = @getLine()
|
|
||||||
brush = line[line.length-1][1]
|
|
||||||
line.splice(@cursorX, n)
|
|
||||||
@fill(@cursorY, @cols - n, n, ' ', brush)
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
print: (text) ->
|
|
||||||
text = Utf8.decode(text)
|
|
||||||
|
|
||||||
startLine = @cursorY
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < text.length
|
|
||||||
if @cursorX >= @cols
|
|
||||||
@goToNextRowFirstColumn()
|
|
||||||
|
|
||||||
@fill @cursorY, @cursorX, 1, @charsetModifier(text[i])
|
|
||||||
@cursorX += 1
|
|
||||||
i++
|
|
||||||
|
|
||||||
endLine = @cursorY
|
|
||||||
|
|
||||||
@updateLines(startLine, endLine)
|
|
||||||
|
|
||||||
clearLineData: (n) ->
|
|
||||||
@fill n, 0, @cols, " "
|
|
||||||
|
|
||||||
# ----- Cursor control
|
|
||||||
|
|
||||||
correctCursorPos: ->
|
|
||||||
if @cursorX < 0
|
|
||||||
@cursorX = 0
|
|
||||||
|
|
||||||
if @cursorX >= @cols
|
|
||||||
@cursorX = @cols - 1
|
|
||||||
|
|
||||||
if @cursorY < 0
|
|
||||||
@cursorY = 0
|
|
||||||
|
|
||||||
if @cursorY >= @lines
|
|
||||||
@cursorY = @lines - 1
|
|
||||||
|
|
||||||
priorRow: (n = 1) ->
|
|
||||||
for i in [0...n]
|
|
||||||
if @cursorY > 0
|
|
||||||
@cursorY -= 1
|
|
||||||
@updateLine @cursorY
|
|
||||||
@updateLine @cursorY + 1
|
|
||||||
|
|
||||||
nextRow: (n = 1) ->
|
|
||||||
for i in [0...n]
|
|
||||||
if @cursorY + 1 < @lines
|
|
||||||
@cursorY += 1
|
|
||||||
@updateLine @cursorY - 1
|
|
||||||
@updateLine @cursorY
|
|
||||||
|
|
||||||
nextColumn: (n = 1) ->
|
|
||||||
@_cursorRight() for i in [0...n]
|
|
||||||
|
|
||||||
priorColumn: (n = 1) ->
|
|
||||||
@_cursorLeft() for i in [0...n]
|
|
||||||
|
|
||||||
_cursorLeft: ->
|
|
||||||
if @cursorX > 0
|
|
||||||
@cursorX -= 1
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
_cursorRight: ->
|
|
||||||
if @cursorX < @cols - 1
|
|
||||||
@cursorX += 1
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
priorRowFirstColumn: (n = 1) ->
|
|
||||||
@goToFirstColumn()
|
|
||||||
@priorRow n
|
|
||||||
|
|
||||||
nextRowFirstColumn: (n = 1) ->
|
|
||||||
@goToFirstColumn()
|
|
||||||
@nextRow n
|
|
||||||
|
|
||||||
goToColumn: (col = 1) ->
|
|
||||||
@cursorX = col - 1
|
|
||||||
@correctCursorPos()
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
goToRow: (line = 1) ->
|
|
||||||
oldLine = @cursorY
|
|
||||||
@cursorY = line - 1
|
|
||||||
@correctCursorPos()
|
|
||||||
|
|
||||||
if oldLine != @cursorY
|
|
||||||
@updateLine oldLine
|
|
||||||
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
goToRowAndColumn: (line = 1, col = 1) ->
|
|
||||||
@goToRow line
|
|
||||||
@goToColumn col
|
|
||||||
|
|
||||||
goToNextHorizontalTabStop: (n) ->
|
|
||||||
x = @tabStops.next(@cursorX)
|
|
||||||
@goToRowAndColumn(@cursorY + 1, x + 1)
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
goToPriorHorizontalTabStop: (n) ->
|
|
||||||
x = @tabStops.prev(@cursorX)
|
|
||||||
@goToRowAndColumn(@cursorY + 1, x + 1)
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
clearHorizontalTabStop: ->
|
|
||||||
console.log 'clearHorizontalTabStop'
|
|
||||||
|
|
||||||
clearAllHorizontalTabStops: ->
|
|
||||||
console.log 'clearAllHorizontalTabStops'
|
|
||||||
|
|
||||||
saveCursor: ->
|
|
||||||
@savedCol = @cursorX
|
|
||||||
@savedLine = @cursorY
|
|
||||||
|
|
||||||
restoreCursor: ->
|
|
||||||
oldLine = @cursorY
|
|
||||||
|
|
||||||
@cursorY = @savedLine
|
|
||||||
@cursorX = @savedCol
|
|
||||||
|
|
||||||
@updateLine oldLine
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
goToFirstColumn: ->
|
|
||||||
@cursorX = 0
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
# ----- Attribute control
|
|
||||||
|
|
||||||
setBrush: (brush) ->
|
|
||||||
@brush = brush
|
|
||||||
|
|
||||||
getBrush: ->
|
|
||||||
@brush
|
|
||||||
|
|
||||||
saveBrush: ->
|
|
||||||
@savedBrush = @brush
|
|
||||||
|
|
||||||
restoreBrush: ->
|
|
||||||
@brush = @savedBrush
|
|
||||||
|
|
||||||
repeatLastCharacter: (n = 1) ->
|
|
||||||
|
|
||||||
updateBrush: (changes) ->
|
|
||||||
@brush = @brush.applyChanges changes
|
|
||||||
|
|
||||||
# ----- Scroll control
|
|
||||||
|
|
||||||
inScrollRegion: ->
|
|
||||||
@cursorY >= @topMargin() and @cursorY <= @bottomMargin()
|
|
||||||
|
|
||||||
scrollUp: (n = 1) ->
|
|
||||||
@insertLines n, @topMargin()
|
|
||||||
|
|
||||||
scrollDown: (n = 1) ->
|
|
||||||
@deleteLines n, @topMargin()
|
|
||||||
|
|
||||||
insertLines: (n, l = @cursorY) ->
|
|
||||||
return unless @inScrollRegion()
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < n
|
|
||||||
@_removeLine @bottomMargin()
|
|
||||||
@_addEmptyLine l
|
|
||||||
i++
|
|
||||||
|
|
||||||
@updateLines(l, @bottomMargin())
|
|
||||||
|
|
||||||
deleteLines: (n, l = @cursorY) ->
|
|
||||||
return unless @inScrollRegion()
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < n
|
|
||||||
@_removeLine l
|
|
||||||
@_addEmptyLine @bottomMargin()
|
|
||||||
i++
|
|
||||||
|
|
||||||
@updateLines(l, @bottomMargin())
|
|
||||||
|
|
||||||
goToPriorRow: ->
|
|
||||||
if @cursorY is @topMargin()
|
|
||||||
@scrollUp()
|
|
||||||
else
|
|
||||||
@priorRow()
|
|
||||||
|
|
||||||
goToNextRow: ->
|
|
||||||
if @cursorY is @bottomMargin()
|
|
||||||
@scrollDown()
|
|
||||||
else
|
|
||||||
@nextRow()
|
|
||||||
|
|
||||||
goToNextRowFirstColumn: ->
|
|
||||||
@goToFirstColumn()
|
|
||||||
@goToNextRow()
|
|
||||||
|
|
||||||
setLineWrap: (linewrap) ->
|
|
||||||
|
|
||||||
# ----- Erase control
|
|
||||||
|
|
||||||
eraseScreen: ->
|
|
||||||
l = 0
|
|
||||||
while l < @lines
|
|
||||||
@clearLineData l
|
|
||||||
@updateLine l
|
|
||||||
l++
|
|
||||||
|
|
||||||
eraseFromScreenStart: ->
|
|
||||||
l = 0
|
|
||||||
while l < @cursorY
|
|
||||||
@clearLineData l
|
|
||||||
@updateLine l
|
|
||||||
l++
|
|
||||||
|
|
||||||
@eraseFromRowStart()
|
|
||||||
|
|
||||||
eraseToScreenEnd: ->
|
|
||||||
@eraseToRowEnd()
|
|
||||||
|
|
||||||
l = @cursorY + 1
|
|
||||||
while l < @lines
|
|
||||||
@clearLineData l
|
|
||||||
@updateLine l
|
|
||||||
l++
|
|
||||||
|
|
||||||
eraseRow: ->
|
|
||||||
@fill @cursorY, 0, @cols, " "
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
eraseFromRowStart: ->
|
|
||||||
@fill @cursorY, 0, @cursorX, " "
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
eraseToRowEnd: ->
|
|
||||||
@fill @cursorY, @cursorX, @cols - @cursorX, " "
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
eraseCharacters: (n = 1) ->
|
|
||||||
@fill @cursorY, @cursorX, n, " "
|
|
||||||
@updateLine()
|
|
||||||
|
|
||||||
# ------ Charset control
|
|
||||||
|
|
||||||
setCharset: (charset) ->
|
|
||||||
@charset = charset
|
|
||||||
|
|
||||||
switch charset
|
|
||||||
when 'uk'
|
|
||||||
@charsetModifier = @ukCharsetModifier
|
|
||||||
when 'us'
|
|
||||||
@charsetModifier = @usCharsetModifier
|
|
||||||
when 'special'
|
|
||||||
@charsetModifier = @specialCharsetModifier
|
|
||||||
|
|
||||||
getCharset: ->
|
|
||||||
@charset
|
|
||||||
|
|
||||||
usCharsetModifier: (char) ->
|
|
||||||
char
|
|
||||||
|
|
||||||
ukCharsetModifier: (char) ->
|
|
||||||
char
|
|
||||||
|
|
||||||
specialCharsetModifier: (char) ->
|
|
||||||
AsciiIo.SpecialCharset[char.charCodeAt(0)] or char
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
class AsciiIo.ScrollRegion
|
|
||||||
|
|
||||||
constructor: (@top, @bottom) ->
|
|
||||||
|
|
||||||
setTop: (@top) ->
|
|
||||||
|
|
||||||
setBottom: (@bottom) ->
|
|
||||||
|
|
||||||
getTop: ->
|
|
||||||
@top
|
|
||||||
|
|
||||||
getBottom: ->
|
|
||||||
@bottom
|
|
||||||
|
|
||||||
save: ->
|
|
||||||
@savedTop = @top
|
|
||||||
@savedBottom = @bottom
|
|
||||||
|
|
||||||
restore: ->
|
|
||||||
@top = @savedTop
|
|
||||||
@bottom = @savedBottom
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
class AsciiIo.SgrInterpreter
|
|
||||||
|
|
||||||
parse: (numbers) ->
|
|
||||||
changes = {}
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < numbers.length
|
|
||||||
n = numbers[i]
|
|
||||||
|
|
||||||
if n is 0
|
|
||||||
changes.fg = undefined
|
|
||||||
changes.bg = undefined
|
|
||||||
changes.blink = false
|
|
||||||
changes.bright = false
|
|
||||||
changes.italic = false
|
|
||||||
changes.underline = false
|
|
||||||
changes.reverse = false
|
|
||||||
else if n is 1
|
|
||||||
changes.bright = true
|
|
||||||
else if n is 3
|
|
||||||
changes.italic = true
|
|
||||||
else if n is 4
|
|
||||||
changes.underline = true
|
|
||||||
else if n is 5
|
|
||||||
changes.blink = true
|
|
||||||
else if n is 7
|
|
||||||
changes.reverse = true
|
|
||||||
else if n is 23
|
|
||||||
changes.italic = false
|
|
||||||
else if n is 24
|
|
||||||
changes.underline = false
|
|
||||||
else if n is 25
|
|
||||||
changes.blink = false
|
|
||||||
else if n is 27
|
|
||||||
changes.reverse = false
|
|
||||||
else if n >= 30 and n <= 37
|
|
||||||
changes.fg = n - 30
|
|
||||||
else if n is 38
|
|
||||||
changes.fg = numbers[i + 2]
|
|
||||||
i += 2
|
|
||||||
else if n is 39
|
|
||||||
changes.fg = undefined
|
|
||||||
else if n >= 40 and n <= 47
|
|
||||||
changes.bg = n - 40
|
|
||||||
else if n is 48
|
|
||||||
changes.bg = numbers[i + 2]
|
|
||||||
i += 2
|
|
||||||
else if n is 49
|
|
||||||
changes.bg = undefined
|
|
||||||
else if n >= 90 and n <= 97
|
|
||||||
changes.fg = n - 90
|
|
||||||
else if n >= 100 and n <= 107
|
|
||||||
changes.bg = n - 100
|
|
||||||
|
|
||||||
i++
|
|
||||||
|
|
||||||
changes
|
|
@ -1,95 +0,0 @@
|
|||||||
AsciiIo.SpecialCharset =
|
|
||||||
33: "!"
|
|
||||||
34: '"'
|
|
||||||
35: "#"
|
|
||||||
36: "$"
|
|
||||||
37: "%"
|
|
||||||
38: "&"
|
|
||||||
39: "'"
|
|
||||||
40: "("
|
|
||||||
41: ")"
|
|
||||||
42: "*"
|
|
||||||
43: "+"
|
|
||||||
44: ","
|
|
||||||
45: "-"
|
|
||||||
46: "."
|
|
||||||
47: "/"
|
|
||||||
48: "0"
|
|
||||||
49: "1"
|
|
||||||
50: "2"
|
|
||||||
51: "3"
|
|
||||||
52: "4"
|
|
||||||
53: "5"
|
|
||||||
54: "6"
|
|
||||||
55: "7"
|
|
||||||
56: "8"
|
|
||||||
57: "9"
|
|
||||||
58: ":"
|
|
||||||
59: ";"
|
|
||||||
60: "<"
|
|
||||||
61: "="
|
|
||||||
62: ">"
|
|
||||||
63: "?"
|
|
||||||
64: "@"
|
|
||||||
65: "↑"
|
|
||||||
66: "↓"
|
|
||||||
67: "→"
|
|
||||||
68: "←"
|
|
||||||
69: "█"
|
|
||||||
70: "▚"
|
|
||||||
71: "☃"
|
|
||||||
72: "H"
|
|
||||||
73: "I"
|
|
||||||
74: "J"
|
|
||||||
75: "K"
|
|
||||||
76: "L"
|
|
||||||
77: "M"
|
|
||||||
78: "N"
|
|
||||||
79: "O"
|
|
||||||
80: "P"
|
|
||||||
81: "Q"
|
|
||||||
82: "R"
|
|
||||||
83: "S"
|
|
||||||
84: "T"
|
|
||||||
85: "U"
|
|
||||||
86: "V"
|
|
||||||
87: "W"
|
|
||||||
88: "X"
|
|
||||||
89: "Y"
|
|
||||||
90: "Z"
|
|
||||||
91: "["
|
|
||||||
92: "\\"
|
|
||||||
93: "]"
|
|
||||||
94: "^"
|
|
||||||
95: "_"
|
|
||||||
96: "◆"
|
|
||||||
97: "▒"
|
|
||||||
98: "␉"
|
|
||||||
99: "␌"
|
|
||||||
100: "␍"
|
|
||||||
101: "␊"
|
|
||||||
102: "°"
|
|
||||||
103: "±"
|
|
||||||
104: ""
|
|
||||||
105: "␋"
|
|
||||||
106: "┘"
|
|
||||||
107: "┐"
|
|
||||||
108: "┌"
|
|
||||||
109: "└"
|
|
||||||
110: "┼"
|
|
||||||
111: "⎺"
|
|
||||||
112: "⎻"
|
|
||||||
113: "─"
|
|
||||||
114: "⎼"
|
|
||||||
115: "⎽"
|
|
||||||
116: "├"
|
|
||||||
117: "┤"
|
|
||||||
118: "┴"
|
|
||||||
119: "┬"
|
|
||||||
120: "│"
|
|
||||||
121: "≤"
|
|
||||||
122: "≥"
|
|
||||||
123: "π"
|
|
||||||
124: "≠"
|
|
||||||
125: "£"
|
|
||||||
126: "·"
|
|
@ -1,28 +0,0 @@
|
|||||||
class AsciiIo.TabStops
|
|
||||||
|
|
||||||
constructor: (@cols) ->
|
|
||||||
@stops = (x for x in [0...@cols] when x % 8 is 0)
|
|
||||||
|
|
||||||
add: (col) ->
|
|
||||||
unless _(@stops).include(col)
|
|
||||||
pos = _(@stops).sortedIndex(col)
|
|
||||||
@stops.splice(pos, 0, col)
|
|
||||||
|
|
||||||
next: (cursorX) ->
|
|
||||||
for x in @stops
|
|
||||||
if x > cursorX
|
|
||||||
return x
|
|
||||||
|
|
||||||
@cols
|
|
||||||
|
|
||||||
prev: (cursorX) ->
|
|
||||||
ret = 0
|
|
||||||
|
|
||||||
for x in @stops
|
|
||||||
if x > cursorX
|
|
||||||
break
|
|
||||||
|
|
||||||
ret = x
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
|||||||
class AsciiIo.VT
|
|
||||||
|
|
||||||
constructor: (@cols, @lines) ->
|
|
||||||
_.extend(this, Backbone.Events)
|
|
||||||
@interpreter = new AsciiIo.AnsiInterpreter
|
|
||||||
@reset()
|
|
||||||
|
|
||||||
feed: (data) ->
|
|
||||||
@data += data
|
|
||||||
[commands, @data] = @interpreter.parse @data
|
|
||||||
|
|
||||||
for command in commands
|
|
||||||
[name, args...] = command
|
|
||||||
@[name].apply this, args
|
|
||||||
|
|
||||||
@data.length is 0
|
|
||||||
|
|
||||||
reset: ->
|
|
||||||
@data = ''
|
|
||||||
@resetTerminal()
|
|
||||||
|
|
||||||
bell: ->
|
|
||||||
@trigger 'bell'
|
|
||||||
|
|
||||||
state: ->
|
|
||||||
changes: @buffer.changes()
|
|
||||||
cursorX: @buffer.cursorX
|
|
||||||
cursorY: @buffer.cursorY
|
|
||||||
|
|
||||||
clearChanges: ->
|
|
||||||
@buffer.clearChanges()
|
|
||||||
|
|
||||||
# ==== Screen buffer operations
|
|
||||||
|
|
||||||
switchToNormalBuffer: ->
|
|
||||||
@buffer = @normalBuffer
|
|
||||||
@updateScreen()
|
|
||||||
|
|
||||||
switchToAlternateBuffer: ->
|
|
||||||
@alternateBuffer.setBrush(@normalBuffer.getBrush())
|
|
||||||
@alternateBuffer.setCharset(@normalBuffer.getCharset())
|
|
||||||
@buffer = @alternateBuffer
|
|
||||||
@updateScreen()
|
|
||||||
|
|
||||||
updateScreen: ->
|
|
||||||
@buffer.updateScreen()
|
|
||||||
|
|
||||||
carriageReturn: ->
|
|
||||||
@buffer.goToFirstColumn()
|
|
||||||
|
|
||||||
backspace: ->
|
|
||||||
@buffer.priorColumn()
|
|
||||||
|
|
||||||
# === ANSI handlers
|
|
||||||
|
|
||||||
# ------ Cursor control
|
|
||||||
|
|
||||||
showCursor: ->
|
|
||||||
@trigger 'cursor-visibility', true
|
|
||||||
|
|
||||||
hideCursor: ->
|
|
||||||
@trigger 'cursor-visibility', false
|
|
||||||
|
|
||||||
# ----- Scroll control
|
|
||||||
|
|
||||||
reverseIndex: ->
|
|
||||||
@buffer.goToPriorRow()
|
|
||||||
|
|
||||||
lineFeed: ->
|
|
||||||
@buffer.goToNextRow()
|
|
||||||
|
|
||||||
verticalTab: ->
|
|
||||||
@buffer.goToNextRow()
|
|
||||||
|
|
||||||
formFeed: ->
|
|
||||||
@buffer.goToNextRow()
|
|
||||||
|
|
||||||
index: ->
|
|
||||||
@buffer.goToNextRow()
|
|
||||||
|
|
||||||
newLine: ->
|
|
||||||
@buffer.goToNextRowFirstColumn()
|
|
||||||
|
|
||||||
|
|
||||||
# === Commands
|
|
||||||
|
|
||||||
# ----- Scroll control
|
|
||||||
|
|
||||||
setScrollRegion: (top, bottom) ->
|
|
||||||
if top < 1
|
|
||||||
top = 1
|
|
||||||
|
|
||||||
if bottom > @lines
|
|
||||||
bottom = @lines
|
|
||||||
|
|
||||||
if bottom > top
|
|
||||||
@scrollRegion.setTop(top - 1)
|
|
||||||
@scrollRegion.setBottom(bottom - 1)
|
|
||||||
|
|
||||||
setHorizontalTabStop: ->
|
|
||||||
@tabStops.add(@cursorX)
|
|
||||||
|
|
||||||
# ----- Terminal control
|
|
||||||
|
|
||||||
resetTerminal: ->
|
|
||||||
@scrollRegion = new AsciiIo.ScrollRegion(0, @lines - 1)
|
|
||||||
@tabStops = new AsciiIo.TabStops(@cols)
|
|
||||||
|
|
||||||
@normalBuffer = new AsciiIo.ScreenBuffer(@cols, @lines, @scrollRegion, @tabStops)
|
|
||||||
@alternateBuffer = new AsciiIo.ScreenBuffer(@cols, @lines, @scrollRegion, @tabStops)
|
|
||||||
@buffer = @normalBuffer
|
|
||||||
|
|
||||||
@fg = @bg = undefined
|
|
||||||
@bright = false
|
|
||||||
@underline = false
|
|
||||||
@italic = false
|
|
||||||
|
|
||||||
@updateScreen()
|
|
||||||
|
|
||||||
saveTerminalState: ->
|
|
||||||
@buffer.saveCursor()
|
|
||||||
@scrollRegion.save()
|
|
||||||
@buffer.saveBrush()
|
|
||||||
|
|
||||||
restoreTerminalState: ->
|
|
||||||
@buffer.restoreBrush()
|
|
||||||
@scrollRegion.restore()
|
|
||||||
@buffer.restoreCursor()
|
|
||||||
|
|
||||||
reportRowAndColumn: ->
|
|
||||||
|
|
||||||
setUkCharset: ->
|
|
||||||
@buffer.setCharset('uk')
|
|
||||||
|
|
||||||
setUsCharset: ->
|
|
||||||
@buffer.setCharset('us')
|
|
||||||
|
|
||||||
setSpecialCharset: ->
|
|
||||||
@buffer.setCharset('special')
|
|
||||||
|
|
||||||
print: (text) ->
|
|
||||||
@buffer.print text
|
|
||||||
|
|
||||||
reserveCharacters: (n) ->
|
|
||||||
@buffer.reserveCharacters n
|
|
||||||
|
|
||||||
priorRow: (n) ->
|
|
||||||
@buffer.priorRow n
|
|
||||||
|
|
||||||
nextRow: (n) ->
|
|
||||||
@buffer.nextRow n
|
|
||||||
|
|
||||||
nextColumn: (n) ->
|
|
||||||
@buffer.nextColumn n
|
|
||||||
|
|
||||||
priorColumn: (n) ->
|
|
||||||
@buffer.priorColumn n
|
|
||||||
|
|
||||||
nextRowFirstColumn: (n) ->
|
|
||||||
@buffer.nextRowFirstColumn n
|
|
||||||
|
|
||||||
priorRowFirstColumn: (n) ->
|
|
||||||
@buffer.priorRowFirstColumn n
|
|
||||||
|
|
||||||
goToColumn: (n) ->
|
|
||||||
@buffer.goToColumn n
|
|
||||||
|
|
||||||
goToRowAndColumn: (n, m) ->
|
|
||||||
@buffer.goToRowAndColumn n, m
|
|
||||||
|
|
||||||
goToNextHorizontalTabStop: (n = 1) ->
|
|
||||||
@buffer.goToNextHorizontalTabStop n
|
|
||||||
|
|
||||||
eraseScreen: ->
|
|
||||||
@buffer.eraseScreen()
|
|
||||||
|
|
||||||
eraseFromScreenStart: ->
|
|
||||||
@buffer.eraseFromScreenStart()
|
|
||||||
|
|
||||||
eraseToScreenEnd: ->
|
|
||||||
@buffer.eraseToScreenEnd()
|
|
||||||
|
|
||||||
eraseRow: ->
|
|
||||||
@buffer.eraseRow()
|
|
||||||
|
|
||||||
eraseFromRowStart: ->
|
|
||||||
@buffer.eraseFromRowStart()
|
|
||||||
|
|
||||||
eraseToRowEnd: ->
|
|
||||||
@buffer.eraseToRowEnd()
|
|
||||||
|
|
||||||
insertLines: (n) ->
|
|
||||||
@buffer.insertLines n
|
|
||||||
|
|
||||||
deleteLines: (n) ->
|
|
||||||
@buffer.deleteLines n
|
|
||||||
|
|
||||||
deleteCharacters: (n) ->
|
|
||||||
@buffer.deleteCharacters n
|
|
||||||
|
|
||||||
scrollUp: (n) ->
|
|
||||||
@buffer.scrollUp n
|
|
||||||
|
|
||||||
scrollDown: (n) ->
|
|
||||||
@buffer.scrollDown n
|
|
||||||
|
|
||||||
eraseCharacters: (n) ->
|
|
||||||
@buffer.eraseCharacters n
|
|
||||||
|
|
||||||
goToPriorHorizontalTabStop: (n) ->
|
|
||||||
@buffer.goToPriorHorizontalTabStop n
|
|
||||||
|
|
||||||
repeatLastCharacter: (n) ->
|
|
||||||
@buffer.repeatLastCharacter n
|
|
||||||
|
|
||||||
goToRow: (n) ->
|
|
||||||
@buffer.goToRow n
|
|
||||||
|
|
||||||
clearHorizontalTabStop: ->
|
|
||||||
@buffer.clearHorizontalTabStop()
|
|
||||||
|
|
||||||
clearAllHorizontalTabStops: ->
|
|
||||||
@buffer.clearAllHorizontalTabStops()
|
|
||||||
|
|
||||||
updateBrush: (attrs) ->
|
|
||||||
@buffer.updateBrush attrs
|
|
||||||
|
|
||||||
# References:
|
|
||||||
# http://en.wikipedia.org/wiki/ANSI_escape_code
|
|
||||||
# http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html
|
|
||||||
# http://real-world-systems.com/docs/ANSIcode.html
|
|
||||||
# http://www.shaels.net/index.php/propterm/documents
|
|
||||||
# http://manpages.ubuntu.com/manpages/lucid/man7/urxvt.7.html
|
|
||||||
# http://vt100.net/docs/vt102-ug/chapter5.html
|
|
@ -1,15 +1,7 @@
|
|||||||
class AsciicastSerializer
|
class AsciicastSerializer < ActiveModel::Serializer
|
||||||
|
self.root = false
|
||||||
def initialize(asciicast)
|
|
||||||
@asciicast = asciicast
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_json(*)
|
|
||||||
asciicast.as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :asciicast
|
|
||||||
|
|
||||||
|
attributes :id, :duration, :stdout_frames_url, :snapshot
|
||||||
|
attribute :terminal_columns, key: :width
|
||||||
|
attribute :terminal_lines, key: :height
|
||||||
end
|
end
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
class AsciicastStreamer
|
|
||||||
|
|
||||||
delegate :each, :to => :json_streamer
|
|
||||||
|
|
||||||
def initialize(asciicast)
|
|
||||||
build_json_streamer(asciicast)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :json_streamer
|
|
||||||
|
|
||||||
def build_json_streamer(asciicast)
|
|
||||||
attributes = attributes_for_streaming(asciicast)
|
|
||||||
@json_streamer = JsonStreamer.new(attributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
def attributes_for_streaming(asciicast)
|
|
||||||
attributes = AsciicastSerializer.new(asciicast).as_json
|
|
||||||
saved_time = prepare_stdout(attributes, asciicast)
|
|
||||||
prepare_duration(attributes, asciicast, saved_time)
|
|
||||||
|
|
||||||
attributes
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_stdout(attributes, asciicast)
|
|
||||||
saved_time = 0
|
|
||||||
|
|
||||||
attributes['stdout'] = lambda do |&blk|
|
|
||||||
blk.call('[')
|
|
||||||
|
|
||||||
asciicast.stdout.each do |delay, frame_bytes|
|
|
||||||
if asciicast.time_compression && delay > Asciicast::MAX_DELAY
|
|
||||||
saved_time += (delay - Asciicast::MAX_DELAY)
|
|
||||||
delay = Asciicast::MAX_DELAY
|
|
||||||
end
|
|
||||||
|
|
||||||
blk.call(%([#{delay},#{frame_bytes.bytes.to_a}],))
|
|
||||||
end
|
|
||||||
|
|
||||||
blk.call('[]]')
|
|
||||||
end
|
|
||||||
|
|
||||||
saved_time
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_duration(attributes, asciicast, saved_time)
|
|
||||||
attributes['duration'] = lambda do |&blk|
|
|
||||||
blk.call(asciicast.duration - saved_time)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
class JsonStreamer
|
|
||||||
|
|
||||||
def initialize(object)
|
|
||||||
@object = object
|
|
||||||
end
|
|
||||||
|
|
||||||
def each(&blk)
|
|
||||||
yield '{'
|
|
||||||
|
|
||||||
@object.each_with_index do |key_value, i|
|
|
||||||
key, value = key_value
|
|
||||||
yield %("#{key}":)
|
|
||||||
|
|
||||||
if value.respond_to?(:call)
|
|
||||||
value.call do |v|
|
|
||||||
yield v.to_s
|
|
||||||
end
|
|
||||||
else
|
|
||||||
yield value.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
yield ',' unless i + 1 == @object.size
|
|
||||||
end
|
|
||||||
|
|
||||||
yield '}'
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -0,0 +1,7 @@
|
|||||||
|
class StdoutFramesUploader < BaseUploader
|
||||||
|
|
||||||
|
def filename
|
||||||
|
'stdout.json' if original_filename.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,19 +0,0 @@
|
|||||||
<div class="player"></div>
|
|
||||||
<script>
|
|
||||||
$(function() {
|
|
||||||
var playerClass = <%= player_class.html_safe %>;
|
|
||||||
window.player = new playerClass({
|
|
||||||
el: $('.player'),
|
|
||||||
cols: <%= cols %>,
|
|
||||||
lines: <%= lines %>,
|
|
||||||
speed: <%= speed %>,
|
|
||||||
benchmark: <%= benchmark %>,
|
|
||||||
model: new AsciiIo.Asciicast({ id: <%= asciicast_id %> }),
|
|
||||||
containerWidth: <%= container_width || 'null' %>,
|
|
||||||
rendererClass: <%= renderer_class.html_safe %>,
|
|
||||||
autoPlay: <%= auto_play %>,
|
|
||||||
hud: <%= hud %>,
|
|
||||||
snapshot: <%= snapshot.html_safe %>
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -0,0 +1,16 @@
|
|||||||
|
.player
|
||||||
|
|
||||||
|
javascript:
|
||||||
|
$(function() {
|
||||||
|
var playerClass = #{player_class.html_safe};
|
||||||
|
window.player = new playerClass({
|
||||||
|
el: $('.player'),
|
||||||
|
speed: #{speed},
|
||||||
|
benchmark: #{benchmark},
|
||||||
|
model: new AsciiIo.Asciicast(#{asciicast.html_safe}),
|
||||||
|
containerWidth: #{container_width || 'null'},
|
||||||
|
rendererClass: #{renderer_class.html_safe},
|
||||||
|
autoPlay: #{auto_play},
|
||||||
|
hud: #{hud}
|
||||||
|
});
|
||||||
|
});
|
@ -1,593 +0,0 @@
|
|||||||
describe 'AsciiIo.AnsiInterpreter', ->
|
|
||||||
data = interpreter = calls = checkNumber = undefined
|
|
||||||
|
|
||||||
parse = (data, expectedRest) ->
|
|
||||||
[commands, rest] = interpreter.parse data
|
|
||||||
|
|
||||||
calls = commands
|
|
||||||
checkNumber = 0
|
|
||||||
|
|
||||||
if arguments.length is 2
|
|
||||||
expect(rest).toEqual expectedRest
|
|
||||||
|
|
||||||
expectCall = (args...) ->
|
|
||||||
n = checkNumber
|
|
||||||
checkNumber += 1
|
|
||||||
# console.log calls[n]
|
|
||||||
expect(calls[n]).toEqual args
|
|
||||||
|
|
||||||
expectNoCall = ->
|
|
||||||
# console.log calls[0]
|
|
||||||
expect(calls.length).toEqual 0
|
|
||||||
|
|
||||||
isSwallowed = (d = data) ->
|
|
||||||
parse d, ''
|
|
||||||
expectNoCall()
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
calls = []
|
|
||||||
interpreter = new AsciiIo.AnsiInterpreter
|
|
||||||
|
|
||||||
describe '#parse', ->
|
|
||||||
|
|
||||||
it 'returns not parsed data back', ->
|
|
||||||
parse '\x1b', '\x1b'
|
|
||||||
expectNoCall()
|
|
||||||
|
|
||||||
it 'returns empty string if all data was parsed', ->
|
|
||||||
parse 'abc def', ''
|
|
||||||
|
|
||||||
describe 'C0 set control character', ->
|
|
||||||
# A single character with an ASCII code within the ranges: 000 to 037 and
|
|
||||||
# 200 to 237 octal, 00 - 1F and 80 - 9F hex.
|
|
||||||
|
|
||||||
describe 'x07', ->
|
|
||||||
it 'calls bell', ->
|
|
||||||
parse '\x07'
|
|
||||||
expectCall 'bell'
|
|
||||||
|
|
||||||
describe 'x08', ->
|
|
||||||
it 'calls backspace', ->
|
|
||||||
parse '\x08'
|
|
||||||
expectCall 'backspace'
|
|
||||||
|
|
||||||
describe 'x09', ->
|
|
||||||
it 'calls goToNextHorizontalTabStop', ->
|
|
||||||
parse '\x09'
|
|
||||||
expectCall 'goToNextHorizontalTabStop'
|
|
||||||
|
|
||||||
describe 'x0a', ->
|
|
||||||
it 'calls lineFeed', ->
|
|
||||||
parse '\x0a'
|
|
||||||
expectCall 'lineFeed'
|
|
||||||
|
|
||||||
describe 'x0b', ->
|
|
||||||
it 'calls verticalTab', ->
|
|
||||||
parse '\x0b'
|
|
||||||
expectCall 'verticalTab'
|
|
||||||
|
|
||||||
describe 'x0c', ->
|
|
||||||
it 'calls formFeed', ->
|
|
||||||
parse '\x0c'
|
|
||||||
expectCall 'formFeed'
|
|
||||||
|
|
||||||
describe 'x0d', ->
|
|
||||||
it 'calls carriageReturn', ->
|
|
||||||
parse '\x0d'
|
|
||||||
expectCall 'carriageReturn'
|
|
||||||
|
|
||||||
describe 'x84', ->
|
|
||||||
it 'calls index', ->
|
|
||||||
parse '\x84'
|
|
||||||
expectCall 'index'
|
|
||||||
|
|
||||||
describe 'x85', ->
|
|
||||||
it 'calls newLine', ->
|
|
||||||
parse '\x85'
|
|
||||||
expectCall 'newLine'
|
|
||||||
|
|
||||||
describe 'x88', ->
|
|
||||||
it 'calls setHorizontalTabStop', ->
|
|
||||||
parse '\x88'
|
|
||||||
expectCall 'setHorizontalTabStop'
|
|
||||||
|
|
||||||
describe 'x8d', ->
|
|
||||||
it 'calls reverseIndex', ->
|
|
||||||
parse '\x8d'
|
|
||||||
expectCall 'reverseIndex'
|
|
||||||
|
|
||||||
describe 'other', ->
|
|
||||||
it "is swallowed", ->
|
|
||||||
for c in ['\x00', '\x0e', '\x0f', '\x82', '\x94']
|
|
||||||
isSwallowed c
|
|
||||||
|
|
||||||
describe 'printable character', ->
|
|
||||||
|
|
||||||
describe 'from ASCII range (0x20-0x7e)', ->
|
|
||||||
it 'calls print', ->
|
|
||||||
parse '\x20foobar\x7e', ''
|
|
||||||
expectCall 'print', '\x20foobar\x7e'
|
|
||||||
|
|
||||||
describe 'from Unicode', ->
|
|
||||||
it 'calls print', ->
|
|
||||||
parse '\xe2ab\xe8ZZ', ''
|
|
||||||
expectCall 'print', '\xe2ab\xe8ZZ'
|
|
||||||
|
|
||||||
parse '\xc2\x09\xc4b\xc5c', ''
|
|
||||||
expectCall 'print', '\xc2\x09\xc4b\xc5c'
|
|
||||||
|
|
||||||
parse '\xa1A\xc9\xfe', ''
|
|
||||||
expectCall 'print', '\xa1A\xc9\xfe'
|
|
||||||
|
|
||||||
describe 'escape sequence', ->
|
|
||||||
# 2 or 3 character string starting with ESCape. (Four or more character
|
|
||||||
# strings are allowed but not defined.)
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data = '\x1b'
|
|
||||||
|
|
||||||
describe 'with C0 control nested inside another escape sequence', ->
|
|
||||||
# C0 Control = 00-1F
|
|
||||||
# Interpret the character, then resume processing the sequence.
|
|
||||||
# Example: CR, LF, XON, and XOFF work as normal within an ESCape
|
|
||||||
# sequence.
|
|
||||||
|
|
||||||
# it 'stops interpreting current seq and handles nested C0 control char', ->
|
|
||||||
# data += '[1\x1b\x0dm'
|
|
||||||
# parse data, ''
|
|
||||||
# expectCall 'cr'
|
|
||||||
# expectCall 'updateBrush', bright: true
|
|
||||||
|
|
||||||
describe 'with intermediate', ->
|
|
||||||
# Intermediate = 20-2F !"#$%&'()*+,-./
|
|
||||||
# Expect zero or more intermediates, a parameter terminates a private
|
|
||||||
# function, an alphabetic terminates a standard sequence. Example: ESC
|
|
||||||
# ( A defines standard character set, ESC ( 0 a DEC set.
|
|
||||||
|
|
||||||
describe '(', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += '('
|
|
||||||
|
|
||||||
describe '0', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += '0'
|
|
||||||
|
|
||||||
it 'calls setSpecialCharset', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'setSpecialCharset'
|
|
||||||
|
|
||||||
describe 'A', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += 'A'
|
|
||||||
|
|
||||||
it 'calls setUkCharset', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'setUkCharset'
|
|
||||||
|
|
||||||
describe 'B', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += 'B'
|
|
||||||
|
|
||||||
it 'calls setUsCharset', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'setUsCharset'
|
|
||||||
|
|
||||||
describe 'followed by parameter', ->
|
|
||||||
# private function
|
|
||||||
|
|
||||||
describe 'followed by an alphabetic', ->
|
|
||||||
# standard sequence
|
|
||||||
|
|
||||||
describe 'with parameter', ->
|
|
||||||
# Parameter = 30-3F 0123456789:;<=>?
|
|
||||||
# End of a private 2-character escape sequence. Example: ESC = sets
|
|
||||||
# special keypad mode, ESC > clears it.
|
|
||||||
|
|
||||||
describe '7', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '7'
|
|
||||||
|
|
||||||
it 'saves terminal state', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'saveTerminalState'
|
|
||||||
|
|
||||||
describe '8', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '8'
|
|
||||||
|
|
||||||
it 'restores terminal state', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'restoreTerminalState'
|
|
||||||
|
|
||||||
describe '=', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '='
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe '>', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '>'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe 'with uppercase', ->
|
|
||||||
# Uppercase = 40-5F @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
|
||||||
# Translate it into a C1 control character and act on it. Example: ESC
|
|
||||||
# D does indexes down, ESC M indexes up. (CSI is special)
|
|
||||||
|
|
||||||
describe 'M', ->
|
|
||||||
# Reverse Index, go up one line, reverse scroll if necessary
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += 'M'
|
|
||||||
|
|
||||||
it 'goes up 1 line', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'reverseIndex'
|
|
||||||
|
|
||||||
describe 'P', ->
|
|
||||||
# Device Control String, terminated by ST
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += 'Pfoobar\\'
|
|
||||||
|
|
||||||
# it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe ']', ->
|
|
||||||
# Operating system command
|
|
||||||
|
|
||||||
describe '0;...BELL', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += ']0;foobar\x07'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe '1;...BELL', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += ']1;foobar\x07'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe '2;...BELL', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += ']2;foobar\x07'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
|
|
||||||
describe 'with lowercase', ->
|
|
||||||
# Lowercase = 60-7E `abcdefghijlkmnopqrstuvwxyz{|}~
|
|
||||||
# End of a standard 2-character escape sequence. Example: ESC c resets
|
|
||||||
# the terminal.
|
|
||||||
|
|
||||||
describe 'with delete', ->
|
|
||||||
# Delete = 7F
|
|
||||||
# Ignore it, and continue interpreting the ESCape sequence C1 and G1:
|
|
||||||
# Treat the same as their 7-bit counterparts
|
|
||||||
|
|
||||||
describe 'with control sequence', ->
|
|
||||||
# A string starting with CSI (233 octal, 9B hex) or with ESC[
|
|
||||||
# (Left-Bracket) and terminated by an alphabetic character. Any number of
|
|
||||||
# parameter characters (digits 0 to 9, semicolon, and question mark) may
|
|
||||||
# appear within the Control Sequence. The terminating character may be
|
|
||||||
# preceded by an intermediate character (such as space).
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
data += '['
|
|
||||||
|
|
||||||
describe '@', ->
|
|
||||||
it 'calls reserveCharacters', ->
|
|
||||||
data += '3@'
|
|
||||||
parse data
|
|
||||||
expectCall 'reserveCharacters', 3
|
|
||||||
|
|
||||||
describe 'A', ->
|
|
||||||
it 'calls priorRow(1) if no number given', ->
|
|
||||||
data += 'A'
|
|
||||||
parse data
|
|
||||||
expectCall 'priorRow', 1
|
|
||||||
|
|
||||||
it 'calls priorRow(n) if number given', ->
|
|
||||||
data += '3A'
|
|
||||||
parse data
|
|
||||||
expectCall 'priorRow', 3
|
|
||||||
|
|
||||||
describe 'B', ->
|
|
||||||
it 'calls nextRow(1) if no number given', ->
|
|
||||||
data += 'B'
|
|
||||||
parse data
|
|
||||||
expectCall 'nextRow', 1
|
|
||||||
|
|
||||||
it 'calls nextRow(n) if number given', ->
|
|
||||||
data += '3B'
|
|
||||||
parse data
|
|
||||||
expectCall 'nextRow', 3
|
|
||||||
|
|
||||||
describe 'C', ->
|
|
||||||
it 'calls nextColumn(1) if no number given', ->
|
|
||||||
data += 'C'
|
|
||||||
parse data
|
|
||||||
expectCall 'nextColumn', 1
|
|
||||||
|
|
||||||
it 'calls nextColumn(n) if number given', ->
|
|
||||||
data += '3C'
|
|
||||||
parse data
|
|
||||||
expectCall 'nextColumn', 3
|
|
||||||
|
|
||||||
describe 'D', ->
|
|
||||||
it 'calls priorColumn(1) if no number given', ->
|
|
||||||
data += 'D'
|
|
||||||
parse data
|
|
||||||
expectCall 'priorColumn', 1
|
|
||||||
|
|
||||||
it 'calls priorColumn(n) if number given', ->
|
|
||||||
data += '3D'
|
|
||||||
parse data
|
|
||||||
expectCall 'priorColumn', 3
|
|
||||||
|
|
||||||
describe 'E', ->
|
|
||||||
it 'calls nextRowFirstColumn(n)', ->
|
|
||||||
data += '3E'
|
|
||||||
parse data
|
|
||||||
expectCall 'nextRowFirstColumn', 3
|
|
||||||
|
|
||||||
describe 'F', ->
|
|
||||||
it 'calls priorRowFirstColumn(n)', ->
|
|
||||||
data += '3F'
|
|
||||||
parse data
|
|
||||||
expectCall 'priorRowFirstColumn', 3
|
|
||||||
|
|
||||||
describe 'G', ->
|
|
||||||
it 'calls goToColumn(n)', ->
|
|
||||||
data += '3G'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToColumn', 3
|
|
||||||
|
|
||||||
describe 'H', ->
|
|
||||||
it 'calls goToRowAndColumn(n, m) when n and m given', ->
|
|
||||||
data += '3;4H'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRowAndColumn', 3, 4
|
|
||||||
|
|
||||||
it 'calls goToRowAndColumn(1, m) when no n given', ->
|
|
||||||
data += ';3H'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRowAndColumn', 1, 3
|
|
||||||
|
|
||||||
it 'calls goToRowAndColumn(n, 1) when no m given', ->
|
|
||||||
data += '3;H'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRowAndColumn', 3, 1
|
|
||||||
|
|
||||||
it 'calls goToRowAndColumn(n, 1) when no m given (no semicolon)', ->
|
|
||||||
data += '3H'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRowAndColumn', 3, 1
|
|
||||||
|
|
||||||
it 'calls goToRowAndColumn(1, 1) when no n nor m given', ->
|
|
||||||
data += 'H'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRowAndColumn', 1, 1
|
|
||||||
|
|
||||||
describe 'I', ->
|
|
||||||
it 'calls goToNextHorizontalTabStop', ->
|
|
||||||
data += '2I'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToNextHorizontalTabStop', 2
|
|
||||||
|
|
||||||
describe 'J', ->
|
|
||||||
it 'calls eraseToScreenEnd when no n given', ->
|
|
||||||
data += 'J'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseToScreenEnd'
|
|
||||||
|
|
||||||
it 'calls eraseToScreenEnd when 0 given', ->
|
|
||||||
data += '0J' # TODO check if it's 0 or 3
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseToScreenEnd'
|
|
||||||
|
|
||||||
it 'calls eraseFromScreenStart when 1 given', ->
|
|
||||||
data += '1J'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseFromScreenStart'
|
|
||||||
|
|
||||||
it 'calls eraseScreen when 2 given', ->
|
|
||||||
data += '2J'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseScreen'
|
|
||||||
|
|
||||||
describe 'K', ->
|
|
||||||
it 'calls eraseToRowEnd when no n given', ->
|
|
||||||
data += 'K'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseToRowEnd'
|
|
||||||
|
|
||||||
it 'calls eraseToRowEnd when 0 given', ->
|
|
||||||
data += '0K' # TODO: check if its 0 or 3
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseToRowEnd'
|
|
||||||
|
|
||||||
it 'calls eraseFromRowStart when 1 given', ->
|
|
||||||
data += '1K'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseFromRowStart'
|
|
||||||
|
|
||||||
it 'calls eraseRow when 2 given', ->
|
|
||||||
data += '2K'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseRow'
|
|
||||||
|
|
||||||
describe 'L', ->
|
|
||||||
it 'calls insertLines(1) when no n given', ->
|
|
||||||
data += 'L'
|
|
||||||
parse data
|
|
||||||
expectCall 'insertLines', 1
|
|
||||||
|
|
||||||
it 'calls insertLines(n) when n given', ->
|
|
||||||
data += '3L'
|
|
||||||
parse data
|
|
||||||
expectCall 'insertLines', 3
|
|
||||||
|
|
||||||
describe 'M', ->
|
|
||||||
it 'calls deleteLines(1) when no n given', ->
|
|
||||||
data += 'M'
|
|
||||||
parse data
|
|
||||||
expectCall 'deleteLines', 1
|
|
||||||
|
|
||||||
it 'calls deleteLines(n) when n given', ->
|
|
||||||
data += '3M'
|
|
||||||
parse data
|
|
||||||
expectCall 'deleteLines', 3
|
|
||||||
|
|
||||||
describe 'P', ->
|
|
||||||
it 'calls deleteCharacters(1) when no n given', ->
|
|
||||||
data += 'P'
|
|
||||||
parse data
|
|
||||||
expectCall 'deleteCharacters', 1
|
|
||||||
|
|
||||||
it 'calls deleteCharacters(n) when n given', ->
|
|
||||||
data += '3P'
|
|
||||||
parse data
|
|
||||||
expectCall 'deleteCharacters', 3
|
|
||||||
|
|
||||||
describe 'S', ->
|
|
||||||
it 'calls scrollUp(n)', ->
|
|
||||||
data += '4S'
|
|
||||||
parse data
|
|
||||||
expectCall 'scrollUp', 4
|
|
||||||
|
|
||||||
describe 'T', ->
|
|
||||||
it 'calls scrollDown(n)', ->
|
|
||||||
data += '4T'
|
|
||||||
parse data
|
|
||||||
expectCall 'scrollDown', 4
|
|
||||||
|
|
||||||
describe 'X', ->
|
|
||||||
it 'calls eraseCharacters(n)', ->
|
|
||||||
data += '4X'
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseCharacters', 4
|
|
||||||
|
|
||||||
describe 'Z', ->
|
|
||||||
it 'calls goToPriorHorizontalTabStop(n)', ->
|
|
||||||
data += '5Z'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToPriorHorizontalTabStop', 5
|
|
||||||
|
|
||||||
describe 'b', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'c', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '>c'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe 'd', ->
|
|
||||||
it 'calls goToRow(n)', ->
|
|
||||||
data += '3d'
|
|
||||||
parse data
|
|
||||||
expectCall 'goToRow', 3
|
|
||||||
|
|
||||||
describe 'f', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'g', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'l', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'm', ->
|
|
||||||
it 'calls handleSGR([n, m, ...]) when n and m given', ->
|
|
||||||
data += '1;4;33m'
|
|
||||||
spyOn interpreter, 'handleSGR'
|
|
||||||
parse data
|
|
||||||
expect(interpreter.handleSGR).toHaveBeenCalledWith([1, 4, 33])
|
|
||||||
|
|
||||||
it 'calls handleSGR([]) when no n nor m given', ->
|
|
||||||
data += 'm'
|
|
||||||
spyOn interpreter, 'handleSGR'
|
|
||||||
parse data
|
|
||||||
expect(interpreter.handleSGR).toHaveBeenCalledWith([])
|
|
||||||
|
|
||||||
describe 'n', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'r', ->
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
describe 'from private standards', ->
|
|
||||||
# first character after CSI is one of: " < = > (074-077 octal, 3C-3F )
|
|
||||||
|
|
||||||
describe 'DEC/xterm specific', ->
|
|
||||||
describe '$~', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '$~'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe '?', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '?'
|
|
||||||
|
|
||||||
describe '1h', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '1h'
|
|
||||||
|
|
||||||
it 'is swallowed', isSwallowed
|
|
||||||
|
|
||||||
describe '25h', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '25h'
|
|
||||||
|
|
||||||
it 'shows cursor', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'showCursor'
|
|
||||||
|
|
||||||
describe '25l', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '25l'
|
|
||||||
|
|
||||||
it 'hides cursor', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'hideCursor'
|
|
||||||
|
|
||||||
describe '47h', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '47h'
|
|
||||||
|
|
||||||
it 'switches to alternate buffer', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'switchToAlternateBuffer'
|
|
||||||
|
|
||||||
describe '47l', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '47l'
|
|
||||||
|
|
||||||
it 'switches to normal buffer', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'switchToNormalBuffer'
|
|
||||||
|
|
||||||
describe '1049h', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '1049h'
|
|
||||||
|
|
||||||
it 'saves cursor position, switches to alternate buffer and clear screen', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'switchToAlternateBuffer'
|
|
||||||
expectCall 'eraseScreen'
|
|
||||||
|
|
||||||
describe '1049l', ->
|
|
||||||
beforeEach ->
|
|
||||||
data += '1049l'
|
|
||||||
|
|
||||||
it 'clears screen, switches to normal buffer and restores cursor position', ->
|
|
||||||
parse data
|
|
||||||
expectCall 'eraseScreen'
|
|
||||||
expectCall 'switchToNormalBuffer'
|
|
@ -1,70 +0,0 @@
|
|||||||
describe 'AsciiIo.SgrInterpreter', ->
|
|
||||||
interpreter = new AsciiIo.SgrInterpreter
|
|
||||||
|
|
||||||
expectChange = (numbers, hash) ->
|
|
||||||
attrs = interpreter.parse numbers
|
|
||||||
expect(attrs).toEqual hash
|
|
||||||
|
|
||||||
describe '#parse', ->
|
|
||||||
|
|
||||||
it 'resets brush for 0', ->
|
|
||||||
expectChange [0], AsciiIo.Brush.default().attributes()
|
|
||||||
|
|
||||||
it 'sets bright attr for 1', ->
|
|
||||||
expectChange [1], bright: true
|
|
||||||
|
|
||||||
it 'sets italic attr for 3', ->
|
|
||||||
expectChange [3], italic: true
|
|
||||||
|
|
||||||
it 'sets underline attr for 4', ->
|
|
||||||
expectChange [4], underline: true
|
|
||||||
|
|
||||||
it 'sets blink attr for 5', ->
|
|
||||||
expectChange [5], blink: true
|
|
||||||
|
|
||||||
it 'sets reverse attr for 7', ->
|
|
||||||
expectChange [7], reverse: true
|
|
||||||
|
|
||||||
it 'unsets italic for 23', ->
|
|
||||||
expectChange [23], italic: false
|
|
||||||
|
|
||||||
it 'unsets underline attr for 24', ->
|
|
||||||
expectChange [24], underline: false
|
|
||||||
|
|
||||||
it 'unsets blink attr for 25', ->
|
|
||||||
expectChange [25], blink: false
|
|
||||||
|
|
||||||
it 'unsets reverse attr for 27', ->
|
|
||||||
expectChange [27], reverse: false
|
|
||||||
|
|
||||||
it 'sets foreground for 30-37', ->
|
|
||||||
expectChange [30], fg: 0
|
|
||||||
expectChange [32], fg: 2
|
|
||||||
expectChange [37], fg: 7
|
|
||||||
|
|
||||||
it 'sets foreground for 38;5;x', ->
|
|
||||||
expectChange [38, 5, 100], fg: 100
|
|
||||||
|
|
||||||
it 'resets foreground for 39', ->
|
|
||||||
expectChange [39], fg: undefined
|
|
||||||
|
|
||||||
it 'sets background for 40-47', ->
|
|
||||||
expectChange [40], bg: 0
|
|
||||||
expectChange [44], bg: 4
|
|
||||||
expectChange [47], bg: 7
|
|
||||||
|
|
||||||
it 'sets background for 48;5;x', ->
|
|
||||||
expectChange [48, 5, 200], bg: 200
|
|
||||||
|
|
||||||
it 'resets background for 49', ->
|
|
||||||
expectChange [49], bg: undefined
|
|
||||||
|
|
||||||
it 'sets foreground for 90-97', ->
|
|
||||||
expectChange [90], fg: 0
|
|
||||||
expectChange [93], fg: 3
|
|
||||||
expectChange [97], fg: 7
|
|
||||||
|
|
||||||
it 'sets background for 100-107', ->
|
|
||||||
expectChange [100], bg: 0
|
|
||||||
expectChange [103], bg: 3
|
|
||||||
expectChange [107], bg: 7
|
|
@ -0,0 +1,15 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe AsciicastSerializer do
|
||||||
|
let(:serializer) { described_class.new(asciicast) }
|
||||||
|
let(:asciicast) { create(:asciicast) }
|
||||||
|
|
||||||
|
describe '#to_json' do
|
||||||
|
subject { JSON.parse(serializer.to_json) }
|
||||||
|
|
||||||
|
it { should eq({ "id" => 1, "duration" => 11.146430015563965,
|
||||||
|
"stdout_frames_url" => nil, "snapshot" => nil,
|
||||||
|
"width" => 96, "height" => 26 }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,28 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe JsonStreamer do
|
|
||||||
|
|
||||||
let(:streamer) { JsonStreamer.new(object) }
|
|
||||||
let(:object) { {
|
|
||||||
:foo => 'bar',
|
|
||||||
'baz' => 1337,
|
|
||||||
'qux' => true,
|
|
||||||
'arr' => [1, 2],
|
|
||||||
'blk' => lambda { |&blk| blk['12.']; blk[3] }
|
|
||||||
} }
|
|
||||||
|
|
||||||
describe '#each' do
|
|
||||||
it 'calls supplied block with each key value pair' do
|
|
||||||
expect { |b| streamer.each(&b) }.
|
|
||||||
to yield_successive_args(
|
|
||||||
'{',
|
|
||||||
'"foo":', '"bar"', ',',
|
|
||||||
'"baz":', '1337' , ',',
|
|
||||||
'"qux":', 'true' , ',',
|
|
||||||
'"arr":', '[1,2]', ',',
|
|
||||||
'"blk":', '12.' , '3',
|
|
||||||
'}')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
Loading…
Reference in New Issue