diff options
| -rw-r--r-- | css/sally.css | 13 | ||||
| -rw-r--r-- | index.html | 458 | ||||
| -rw-r--r-- | js/app.js | 223 | ||||
| -rw-r--r-- | js/lex.js | 105 | ||||
| -rw-r--r-- | js/matrix.js | 90 | ||||
| -rw-r--r-- | js/tool.js | 16 |
6 files changed, 455 insertions, 450 deletions
diff --git a/css/sally.css b/css/sally.css index 81b4ba6..e5b5ed7 100644 --- a/css/sally.css +++ b/css/sally.css @@ -25,17 +25,24 @@ a:link, a:visited {text-decoration: none; color: #3b3740} {overflow: auto;} -.rapper { +.rapper, .block { float: left; height:auto; width:auto; padding:4px; background-color: #000000; - z-index:3; +} +.rapper { white-space:pre-wrap; word-wrap: break-word; } - +.block { + white-space:pre-line; + padding-left: 30px; +} +.tool { + cursor: pointer; +} @media screen and (-webkit-min-device-pixel-ratio:0) { #nvgovy{white-space:pre;} } @@ -9,6 +9,7 @@ body.grid div { border-top: 1px solid #444; border-left: 1px solid #444; } .tool.focused, .ed.focused { color: white; text-decoration: underline; } body.grid .focused { box-shadow: inset 0 0 2px white, inset 0 0 2px white, inset 0 0 2px white; } </style> + <body class="grid"> <div> @@ -17,455 +18,18 @@ body.grid .focused { box-shadow: inset 0 0 2px white, inset 0 0 2px white, inset <div style="clear:both"> <div id="palette_rapper" class="rapper"></div> <div id="brush_rapper" class="rapper"></div> -<div id="tools_rapper" class="rapper"> -<span id="square_el" class="tool">square</span> -<span id="circle_el" class="tool">circle</span> -<span id="text_el" class="tool">text</span> +<div id="tools_rapper" class="block"> + <span id="square_el" class="tool">square</span> + <span id="circle_el" class="tool">circle</span> + <span id="text_el" class="tool">text</span> -brush size: <span id="width_el" class="ed">5</span> x <span id="height_el" class="ed">5</span> + brush size: <span id="width_el" class="ed">5</span> x <span id="height_el" class="ed">5</span> </div> </div> -<script> -var contentType = 'text/plain;charset=utf-8' - -var cols = 80 -var rows = 24 -var dragging = false -var drawing = true -var erasing = false -var focused - -var canvas, tools, palette, brush, mode, current_tool - -var color_names = ("white black dark-blue green red dark-red purple orange" + - "yellow lime dark-cyan cyan blue magenta dark-gray light-gray").split(" "); -var letters = "abcdefghijklmnop"; -var colors = {}, controls = {} -color_names.forEach(function(name, i){ colors[name] = i }) - -// var tools = "square circle rectangle" - -function init () { - build() - bind() -} -function build () { - canvas = new Matrix (cols, rows, function(x,y){ - var lex = new Lex (x,y) - if (x > y || y > x + 20 || x > y / 4 + 10) { - lex.clear() - } - else { - lex.bg = x+y*y - lex.fg = (x+y)%2 - lex.char = ":" - } - lex.build() - return lex - }) - brush = new Matrix (5, 5, function(x,y){ - var lex = new Lex (x,y) - lex.build() - return lex - }) - palette = new Matrix (32, 2, function(x,y){ - var lex = new Lex (x,y) - lex.bg = y>>1 - lex.build() - return lex - }) - canvas.append(canvas_rapper) - brush.append(brush_rapper) - palette.append(palette_rapper) - - controls.circle = new Tool (circle_el) - controls.circle.use = function(){ - brush.generate = controls.circle.generate - brush.generate() - drawing = true - } - controls.circle.generate = function(){ - var fg = brush.fg, bg = brush.bg - var hw = brush.w/2|0, hh = brush.h/2|0 - brush.forEach(function(lex,x,y) { - var len = Math.sqrt(Math.pow(x-hw,2)+Math.pow(y-hh,2)) - if (len > Math.abs(hw)) { - lex.clear() - } - else { - lex.fill(fg,bg) - } - }) - } - - controls.square = new Tool (square_el) - controls.square.use = function(){ - brush.generate = controls.square.generate - brush.generate() - drawing = true - } - controls.square.generate = function(){ - var fg = brush.fg, bg = brush.bg - brush.fill(fg,bg) - } - - controls.text = new Tool (text_el) - controls.text.use = function(){ - brush.generate = controls.text.generate - brush.generate() - drawing = false - } - controls.text.generate = function(){ - } - - controls.width = new Lex (width_el) - controls.height = new Lex (height_el) - - controls.circle.focus() - - brush.bg = colors.red - brush.generate() - brush.build() -} -function bind () { - canvas.forEach(function(lex, x, y){ - lex.span.addEventListener('mousedown', function(e){ - e.preventDefault() - dragging = true - if (drawing) { - erasing = e.which == "3" - draw(lex, x, y, erasing) - } - else { - lex.focus() - } - }) - lex.span.addEventListener("mousemove", function(){ - if (! dragging) return - if (drawing) { - draw(lex, x, y, erasing) - } - else { - lex.focus() - } - }) - }) - palette.forEach(function(lex, x, y){ - lex.span.addEventListener('mousedown', function(e){ - e.preventDefault() - dragging = true - erasing = e.which == "3" - brush.fg = lex.fg - brush.bg = lex.bg - brush.generate() - }) - }) - brush.forEach(function(lex, x, y){ - lex.span.addEventListener('mousedown', function(e){ - e.preventDefault() - dragging = true -// lex.fill(lex.fg, lex.bg) - }) - }) - window.addEventListener('mouseup', function(){ - dragging = erasing = false - }); - - [controls.width, controls.height].forEach(function(lex){ - lex.span.addEventListener('mousedown', function(e){ - lex.focus() - }) - }); - - [controls.square, controls.circle, controls.text].forEach(function(tool){ - tool.span.addEventListener('mousedown', function(e){ - tool.focus() - }) - }) - - controls.width.key = int_key(function(n, keyCode){ - controls.width.blur() - controls.width.char = ""+n - controls.width.build() - brush.w = n - brush.rebuild() - }) - controls.height.key = int_key(function(n, keyCode){ - controls.height.blur() - controls.height.char = ""+n - controls.height.build() - brush.h = n - brush.rebuild() - }) - window.addEventListener('keydown', function(e){ - if (! e.metaKey && ! e.ctrlKey && ! e.altKey) { - e.preventDefault() - } - switch (e.keyCode) { - case 27: // esc - if (focused) focused.blur() - break - default: - if (focused) focused.key(String.fromCharCode(e.keyCode), e.keyCode) - break - } - }) -} -function int_key (f) { - return function (key, keyCode) { - var n = parseInt(key) - n && f(n) - } -} - -function Tool (span) { - this.lex = new Lex (span) - this.span = span -} -Tool.prototype.use = function(){} -Tool.prototype.focus = function(){ - focused && focused.blur() - current_tool && current_tool.blur() - current_tool = this - this.span.classList.add('focused') - this.use() -} -Tool.prototype.blur = function(){ - current_tool = null - this.span.classList.remove('focused') -} - -function Lex (x,y) { - if (typeof x == "number") { - this.x = x - this.y = y - this.span = document.createElement("span") - } - else { - this.span = x - } - this.fg = colors.white - this.bg = colors.black - this.char = " " -} -Lex.prototype.build = function(){ - this.span.className = this.css() - this.span.innerHTML = this.html() -} -Lex.prototype.css = function(){ - return "f" + letters[this.fg&15] + "b" + letters[this.bg&15] -} -Lex.prototype.html = function(){ - return this.char == " " ? " " : this.char -} -Lex.prototype.irc = function(){ - if (this.bg == 1 && this.fg == 0) { -// return "\\x03" + "," + (this.bg&15) + this.char - return this.char - } - else { - return "\\x03" + (this.fg&15) + "," + (this.bg&15) + this.char - } -} -Lex.prototype.clone = function (lex){ - if (! erasing && lex.isClear()) return - this.fg = lex.fg - this.bg = lex.bg - this.char = lex.char - this.build() -} -Lex.prototype.fill = function(fg,bg){ - this.fg = fg - this.bg = bg - this.build() -} -Lex.prototype.eq = function(lex){ - return lex && this.fg == lex.fg && this.bg == lex.fg && this.char == lex.char -} -Lex.prototype.clear = function(){ - this.bg = 1 - this.fg = 0 - this.char = " " - this.build() -} -Lex.prototype.isClear = function(){ - return this.bg == 1 && this.fg == 0 && this.char == " " -} -Lex.prototype.focus = function(){ - if (focused) focused.blur() - this.span.classList.add('focused') - focused = this -} -Lex.prototype.blur = function(){ - this.span.classList.remove('focused') - focused = null -} -Lex.prototype.key = function(char, keyCode) { - console.log(keyCode, this.y, this.x) - switch (keyCode) { - case 8: - canvas.focusLex(this.y-1, this.x) - focused.char = " " - focused.build() - return - case 13: // return - canvas.focusLex(0, this.x+1) - break - case 38: // up - canvas.focusLex(this.y, this.x-1) - break - case 40: // down - canvas.focusLex(this.y, this.x+1) - break - case 37: // left - canvas.focusLex(this.y-1, this.x) - break - case 39: // right - canvas.focusLex(this.y+1, this.x) - break - default: - this.char = char - this.fg = brush.bg - this.build() - this.blur() - if (this.y < canvas.w-1) { - canvas.aa[this.x][this.y+1].focus() - } - else { - canvas.focusLex(0, this.x+1) - } - } -} -Lex.prototype.demolish = function(){ - this.span.parentNode.removeChild(this.span) - this.span = null -} - -function Matrix (w,h,f){ - this.w = w - this.h = h - this.f = f - this.initialize() -} -Matrix.prototype.initialize = function(){ - var w = this.w, h = this.h, f = this.f - var aa = new Array (h) - for (var i = 0; i < h; i++) { - aa[i] = new Array (w) - for (var j = 0; j < w; j++) { - aa[i][j] = f(i,j) - } - } - this.aa = aa -} -Matrix.prototype.rebuild = function (){ - this.demolish() - this.initialize() - this.append() - this.generate && this.generate() -} -Matrix.prototype.demolish = function (){ - this.forEach(function(lex){ - lex.demolish() - }) - while (this.rapper.firstChild) { - this.rapper.removeChild(this.rapper.firstChild); - } - this.aa.forEach(function(row){ - row.length = 0 - }) - this.aa.length = 0 -} -Matrix.prototype.forEach = function(f){ - this.aa.forEach(function(row, y){ - row.forEach(function(lex, x){ - f(lex, x, y) - }) - }) -} -Matrix.prototype.focusLex = function(y,x){ - this.aa[mod(x,this.h)][mod(y,this.w)].focus() -} -Matrix.prototype.clear = function(){ - this.forEach(function(lex,x,y){ lex.clear() }) -} -Matrix.prototype.fill = function(fg, bg){ - this.fg = fg - this.bg = bg - this.forEach(function(lex,x,y){ - lex.fg = fg - lex.bg = bg - lex.build() - }) -} -Matrix.prototype.build = function(){ - this.forEach(function(lex,x,y){ - lex.build() - }) -} -Matrix.prototype.append = function(rapper){ - rapper = this.rapper = rapper || this.rapper - this.aa.forEach(function(row, y){ - var div = document.createElement("div") - row.forEach(function(lex, x) { - div.appendChild(lex.span) - }) - rapper.appendChild( div ) - }) -} - -function draw (lex, x, y, erasing) { - stamp (canvas, brush, x, y, erasing) -} -function stamp (canvas, brush, x, y, erasing) { - hh = brush.w/2|0 - brush.forEach(function(lex, s, t){ - s += x-hh - t += y-hh - if (s >= 0 && s < canvas.w && t >= 0 && t < canvas.h) { - canvas.aa[t][s].clone(lex) - } - }) -} -function mod (i,n) { - return i - n * Math.floor(i / n) -} -function ascii () { - var lines = canvas.aa.map(function(row, y){ - var last, line = "" - row.forEach(function(lex, x) { - if (lex.eq(last)) { - line += lex.char - } - else { - if (x > 0 && last && (last.bg != 1 || last.fg != 0)) line += "\\x03" - line += lex.irc() - last = lex - } - }) - return line.replace(/\s+$/,"") - }).filter(function(line){ return line.length > 0 }) - var txt = '/exec -out printf "' + lines.join("\\n") + '"\n' - return txt -} - -document.body.addEventListener('copy', function (e) { - if (e.clipboardData) { - e.preventDefault(); - e.clipboardData.setData(contentType, ascii()); - } - if (window.clipboardData) { - e.returnValue = false; - window.clipboardData.setData(contentType, ascii()); - } -}, false); - -window.cssRule = function (selector, declaration) { - var x = document.styleSheets,y=x.length-1; - x[y].insertRule(selector+"{"+declaration+"}",x[y].cssRules.length); -}; - -init() -</script> +</body> +<script src="js/lex.js"></script> +<script src="js/matrix.js"></script> +<script src="js/tool.js"></script> +<script src="js/app.js"></script> diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..480b20c --- /dev/null +++ b/js/app.js @@ -0,0 +1,223 @@ +var contentType = 'text/plain;charset=utf-8' + +var cols = 80 +var rows = 24 +var dragging = false +var drawing = true +var erasing = false +var focused + +var canvas, tools, palette, brush, mode, current_tool + +var color_names = ("white black dark-blue green red dark-red purple orange" + + "yellow lime dark-cyan cyan blue magenta dark-gray light-gray").split(" "); +var letters = "abcdefghijklmnop"; +var colors = {}, controls = {} +color_names.forEach(function(name, i){ colors[name] = i }) + +// var tools = "square circle rectangle" + +function init () { + build() + bind() +} +function build () { + canvas = new Matrix (cols, rows, function(x,y){ + var lex = new Lex (x,y) + if (x > y || y > x + 20 || x > y / 4 + 10) { + lex.clear() + } + else { + lex.bg = x+y*y + lex.fg = (x+y)%2 + lex.char = ":" + } + lex.build() + return lex + }) + brush = new Matrix (5, 5, function(x,y){ + var lex = new Lex (x,y) + lex.build() + return lex + }) + palette = new Matrix (32, 2, function(x,y){ + var lex = new Lex (x,y) + lex.bg = y>>1 + lex.build() + return lex + }) + canvas.append(canvas_rapper) + brush.append(brush_rapper) + palette.append(palette_rapper) + + controls.circle = new Tool (circle_el) + controls.circle.use = function(){ + brush.generate = controls.circle.generate + brush.generate() + drawing = true + } + controls.circle.generate = function(){ + var fg = brush.fg, bg = brush.bg + var hw = brush.w/2|0, hh = brush.h/2|0 + brush.forEach(function(lex,x,y) { + var len = Math.sqrt(Math.pow(x-hw,2)+Math.pow(y-hh,2)) + if (len > Math.abs(hw)) { + lex.clear() + } + else { + lex.fill(fg,bg) + } + }) + } + + controls.square = new Tool (square_el) + controls.square.use = function(){ + brush.generate = controls.square.generate + brush.generate() + drawing = true + } + controls.square.generate = function(){ + var fg = brush.fg, bg = brush.bg + brush.fill(fg,bg) + } + + controls.text = new Tool (text_el) + controls.text.use = function(){ + brush.generate = controls.text.generate + brush.generate() + drawing = false + } + controls.text.generate = function(){ + } + + controls.width = new Lex (width_el) + controls.height = new Lex (height_el) + + controls.circle.focus() + + brush.bg = colors.red + brush.generate() + brush.build() +} +function bind () { + canvas.forEach(function(lex, x, y){ + lex.span.addEventListener('mousedown', function(e){ + e.preventDefault() + dragging = true + if (drawing) { + erasing = e.which == "3" + draw(lex, x, y, erasing) + } + else { + lex.focus() + } + }) + lex.span.addEventListener("mousemove", function(){ + if (! dragging) return + if (drawing) { + draw(lex, x, y, erasing) + } + else { + lex.focus() + } + }) + }) + palette.forEach(function(lex, x, y){ + lex.span.addEventListener('mousedown', function(e){ + e.preventDefault() + dragging = true + erasing = e.which == "3" + brush.fg = lex.fg + brush.bg = lex.bg + brush.generate() + }) + }) + brush.forEach(function(lex, x, y){ + lex.span.addEventListener('mousedown', function(e){ + e.preventDefault() + dragging = true +// lex.fill(lex.fg, lex.bg) + }) + }) + window.addEventListener('mouseup', function(){ + dragging = erasing = false + }); + + [controls.width, controls.height].forEach(function(lex){ + lex.span.addEventListener('mousedown', function(e){ + lex.focus() + }) + }); + + [controls.square, controls.circle, controls.text].forEach(function(tool){ + tool.span.addEventListener('mousedown', function(e){ + tool.focus() + }) + }) + + controls.width.key = int_key(function(n, keyCode){ + controls.width.blur() + controls.width.char = ""+n + controls.width.build() + brush.w = n + brush.rebuild() + }) + controls.height.key = int_key(function(n, keyCode){ + controls.height.blur() + controls.height.char = ""+n + controls.height.build() + brush.h = n + brush.rebuild() + }) + window.addEventListener('keydown', function(e){ + if (! e.metaKey && ! e.ctrlKey && ! e.altKey) { + e.preventDefault() + } + switch (e.keyCode) { + case 27: // esc + if (focused) focused.blur() + break + default: + if (focused) focused.key(String.fromCharCode(e.keyCode), e.keyCode) + break + } + }) +} +function int_key (f) { + return function (key, keyCode) { + var n = parseInt(key) + n && f(n) + } +} + + + +function draw (lex, x, y, erasing) { + stamp (canvas, brush, x, y, erasing) +} +function stamp (canvas, brush, x, y, erasing) { + hh = brush.w/2|0 + brush.forEach(function(lex, s, t){ + s += x-hh + t += y-hh + if (s >= 0 && s < canvas.w && t >= 0 && t < canvas.h) { + canvas.aa[t][s].clone(lex) + } + }) +} +function mod (i,n) { + return i - n * Math.floor(i / n) +} + +document.body.addEventListener('copy', function (e) { + if (e.clipboardData) { + e.preventDefault(); + e.clipboardData.setData(contentType, canvas.ascii()); + } + if (window.clipboardData) { + e.returnValue = false; + window.clipboardData.setData(contentType, canvas.ascii()); + } +}, false); + +init() diff --git a/js/lex.js b/js/lex.js new file mode 100644 index 0000000..39a47cd --- /dev/null +++ b/js/lex.js @@ -0,0 +1,105 @@ +function Lex (x,y) { + if (typeof x == "number") { + this.x = x + this.y = y + this.span = document.createElement("span") + } + else { + this.span = x + } + this.fg = colors.white + this.bg = colors.black + this.char = " " +} +Lex.prototype.build = function(){ + this.span.className = this.css() + this.span.innerHTML = this.html() +} +Lex.prototype.css = function(){ + return "f" + letters[this.fg&15] + "b" + letters[this.bg&15] +} +Lex.prototype.html = function(){ + return this.char == " " ? " " : this.char +} +Lex.prototype.irc = function(){ + if (this.bg == 1 && this.fg == 0) { +// return "\\x03" + "," + (this.bg&15) + this.char + return this.char + } + else { + return "\\x03" + (this.fg&15) + "," + (this.bg&15) + this.char + } +} +Lex.prototype.clone = function (lex){ + if (! erasing && lex.isClear()) return + this.fg = lex.fg + this.bg = lex.bg + this.char = lex.char + this.build() +} +Lex.prototype.fill = function(fg,bg){ + this.fg = fg + this.bg = bg + this.build() +} +Lex.prototype.eq = function(lex){ + return lex && this.fg == lex.fg && this.bg == lex.fg && this.char == lex.char +} +Lex.prototype.clear = function(){ + this.bg = 1 + this.fg = 0 + this.char = " " + this.build() +} +Lex.prototype.isClear = function(){ + return this.bg == 1 && this.fg == 0 && this.char == " " +} +Lex.prototype.focus = function(){ + if (focused) focused.blur() + this.span.classList.add('focused') + focused = this +} +Lex.prototype.blur = function(){ + this.span.classList.remove('focused') + focused = null +} +Lex.prototype.key = function(char, keyCode) { + console.log(keyCode, this.y, this.x) + switch (keyCode) { + case 8: + canvas.focusLex(this.y-1, this.x) + focused.char = " " + focused.build() + return + case 13: // return + canvas.focusLex(0, this.x+1) + break + case 38: // up + canvas.focusLex(this.y, this.x-1) + break + case 40: // down + canvas.focusLex(this.y, this.x+1) + break + case 37: // left + canvas.focusLex(this.y-1, this.x) + break + case 39: // right + canvas.focusLex(this.y+1, this.x) + break + default: + this.char = char + this.fg = brush.bg + this.build() + this.blur() + if (this.y < canvas.w-1) { + canvas.aa[this.x][this.y+1].focus() + } + else { + canvas.focusLex(0, this.x+1) + } + } +} +Lex.prototype.demolish = function(){ + this.span.parentNode.removeChild(this.span) + this.span = null +} diff --git a/js/matrix.js b/js/matrix.js new file mode 100644 index 0000000..ff734bf --- /dev/null +++ b/js/matrix.js @@ -0,0 +1,90 @@ +function Matrix (w,h,f){ + this.w = w + this.h = h + this.f = f + this.initialize() +} +Matrix.prototype.initialize = function(){ + var w = this.w, h = this.h, f = this.f + var aa = new Array (h) + for (var i = 0; i < h; i++) { + aa[i] = new Array (w) + for (var j = 0; j < w; j++) { + aa[i][j] = f(i,j) + } + } + this.aa = aa +} +Matrix.prototype.rebuild = function (){ + this.demolish() + this.initialize() + this.append() + this.generate && this.generate() +} +Matrix.prototype.demolish = function (){ + this.forEach(function(lex){ + lex.demolish() + }) + while (this.rapper.firstChild) { + this.rapper.removeChild(this.rapper.firstChild); + } + this.aa.forEach(function(row){ + row.length = 0 + }) + this.aa.length = 0 +} +Matrix.prototype.forEach = function(f){ + this.aa.forEach(function(row, y){ + row.forEach(function(lex, x){ + f(lex, x, y) + }) + }) +} +Matrix.prototype.focusLex = function(y,x){ + this.aa[mod(x,this.h)][mod(y,this.w)].focus() +} +Matrix.prototype.clear = function(){ + this.forEach(function(lex,x,y){ lex.clear() }) +} +Matrix.prototype.fill = function(fg, bg){ + this.fg = fg + this.bg = bg + this.forEach(function(lex,x,y){ + lex.fg = fg + lex.bg = bg + lex.build() + }) +} +Matrix.prototype.build = function(){ + this.forEach(function(lex,x,y){ + lex.build() + }) +} +Matrix.prototype.append = function(rapper){ + rapper = this.rapper = rapper || this.rapper + this.aa.forEach(function(row, y){ + var div = document.createElement("div") + row.forEach(function(lex, x) { + div.appendChild(lex.span) + }) + rapper.appendChild( div ) + }) +} +Matrix.prototype.ascii = function () { + var lines = this.aa.map(function(row, y){ + var last, line = "" + row.forEach(function(lex, x) { + if (lex.eq(last)) { + line += lex.char + } + else { + if (x > 0 && last && (last.bg != 1 || last.fg != 0)) line += "\\x03" + line += lex.irc() + last = lex + } + }) + return line.replace(/\s+$/,"") + }).filter(function(line){ return line.length > 0 }) + var txt = '/exec -out printf "' + lines.join("\\n") + '"\n' + return txt +}
\ No newline at end of file diff --git a/js/tool.js b/js/tool.js new file mode 100644 index 0000000..3b9b803 --- /dev/null +++ b/js/tool.js @@ -0,0 +1,16 @@ +function Tool (span) { + this.lex = new Lex (span) + this.span = span +} +Tool.prototype.use = function(){} +Tool.prototype.focus = function(){ + focused && focused.blur() + current_tool && current_tool.blur() + current_tool = this + this.span.classList.add('focused') + this.use() +} +Tool.prototype.blur = function(){ + current_tool = null + this.span.classList.remove('focused') +} |
