1
mirror of git://git.acid.vegas/asciiblaster.git synced 2024-11-23 08:26:45 +00:00

Initial commit

This commit is contained in:
Dionysus 2023-06-23 23:23:33 -04:00
commit 5c632405f8
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
53 changed files with 13903 additions and 0 deletions

BIN
.screens/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# ASCII Blaster
> web based irc art drawing tool
![](.screens/preview.png)
## Want to give it a try? Click [HERE]() to start drawing some IRC art!
### Information
This serves as a mirror of [https://asdf.us/asciiblaster/](https://asdf.us/asciiblaster/) for anyone who would like to contribute to project or fork the repository.
The original creator of this does not have a Github account as far as I know.
###### Mirrors
[acid.vegas](https://git.acid.vegas/asciiblaster) • [GitHub](https://github.com/ircart/asciiblaster) • [GitLab](https://gitlab.com/ircart/asciiblaster) • [SourceHut](https://git.sr.ht/~acidvegas/asciiblaster) • [SuperNETs](https://git.supernets.org/ircart/asciiblaster)

139
css/ak.css Normal file
View File

@ -0,0 +1,139 @@
.fa{color:#fff}.fb{color:#000}.fc{color:#00007F}.fd{color:#009300}.fe{color:red}.ff{color:#7f0000}.fg{color:#9C009C}.fh{color:#FC7F00}.fi{color:#FF0}.fj{color:#00FC00}.fk{color:#009393}.fl{color:#0FF}.fm{color:#0000FC}.fn{color:#F0F}.fo{color:#7F7F7F}.fp{color:#D2D2D2}
.ba{background-color:#fff}.bb{background-color:#000}.bc{background-color:#00007F}.bd{background-color:#009300}.be{background-color:red}.bf{background-color:#7f0000}.bg{background-color:#9C009C}.bh{background-color:#FC7F00}.bi{background-color:#FF0}.bj{background-color:#00FC00}.bk{background-color:#009393}.bl{background-color:#0FF}.bm{background-color:#0000FC}.bn{background-color:#F0F}.bo{background-color:#7F7F7F}.bp{background-color:#D2D2D2}
.winxp .fa{color:rgb(255,255,255)}
.winxp .fb{color:rgb(0,0,0)}
.winxp .fc{color:rgb(0,0,128)}
.winxp .fd{color:rgb(0,128,0)}
.winxp .fe{color:rgb(255,0,0)}
.winxp .ff{color:rgb(128,0,0)}
.winxp .fg{color:rgb(128,0,128)}
.winxp .fh{color:rgb(255,128,0)}
.winxp .fi{color:rgb(255,255,0)}
.winxp .fj{color:rgb(0,255,0)}
.winxp .fk{color:rgb(0,128,128)}
.winxp .fl{color:rgb(0,255,255)}
.winxp .fm{color:rgb(0,0,255)}
.winxp .fn{color:rgb(255,0,255)}
.winxp .fo{color:rgb(128,128,128)}
.winxp .fp{color:rgb(192,192,192)}
.winxp .ba{background-color:rgb(255,255,255)}
.winxp .bb{background-color:rgb(0,0,0)}
.winxp .bc{background-color:rgb(0,0,128)}
.winxp .bd{background-color:rgb(0,128,0)}
.winxp .be{background-color:rgb(255,0,0)}
.winxp .bf{background-color:rgb(128,0,0)}
.winxp .bg{background-color:rgb(128,0,128)}
.winxp .bh{background-color:rgb(255,128,0)}
.winxp .bi{background-color:rgb(255,255,0)}
.winxp .bj{background-color:rgb(0,255,0)}
.winxp .bk{background-color:rgb(0,128,128)}
.winxp .bl{background-color:rgb(0,255,255)}
.winxp .bm{background-color:rgb(0,0,255)}
.winxp .bn{background-color:rgb(255,0,255)}
.winxp .bo{background-color:rgb(128,128,128)}
.winxp .bp{background-color:rgb(192,192,192)}
.vga .fa{color:rgb(255,255,255)}
.vga .fb{color:rgb(0,0,0)}
.vga .fc{color:rgb(0,0,170)}
.vga .fd{color:rgb(0,170,0)}
.vga .fe{color:rgb(255,85,85)}
.vga .ff{color:rgb(170,0,0)}
.vga .fg{color:rgb(170,0,170)}
.vga .fh{color:rgb(170,85,0)}
.vga .fi{color:rgb(255,255,85)}
.vga .fj{color:rgb(85,255,85)}
.vga .fk{color:rgb(0,170,170)}
.vga .fl{color:rgb(85,255,255)}
.vga .fm{color:rgb(85,85,255)}
.vga .fn{color:rgb(255,85,255)}
.vga .fo{color:rgb(85,85,85)}
.vga .fp{color:rgb(170,170,170)}
.vga .ba{background-color:rgb(255,255,255)}
.vga .bb{background-color:rgb(0,0,0)}
.vga .bc{background-color:rgb(0,0,170)}
.vga .bd{background-color:rgb(0,170,0)}
.vga .be{background-color:rgb(255,85,85)}
.vga .bf{background-color:rgb(170,0,0)}
.vga .bg{background-color:rgb(170,0,170)}
.vga .bh{background-color:rgb(170,85,0)}
.vga .bi{background-color:rgb(255,255,85)}
.vga .bj{background-color:rgb(85,255,85)}
.vga .bk{background-color:rgb(0,170,170)}
.vga .bl{background-color:rgb(85,255,255)}
.vga .bm{background-color:rgb(85,85,255)}
.vga .bn{background-color:rgb(255,85,255)}
.vga .bo{background-color:rgb(85,85,85)}
.vga .bp{background-color:rgb(170,170,170)}
.c64 .fa{color:rgb(255,255,255)}
.c64 .fb{color:rgb(0,0,0)}
.c64 .fc{color:rgb(69,32,170)}
.c64 .fd{color:rgb(101,170,69)}
.c64 .fe{color:rgb(138,101,32)}
.c64 .ff{color:rgb(138,69,32)}
.c64 .fg{color:rgb(138,69,170)}
.c64 .fh{color:rgb(101,69,0)}
.c64 .fi{color:rgb(207,207,101)}
.c64 .fj{color:rgb(170,239,138)}
.c64 .fk{color:rgb(138,138,138)}
.c64 .fl{color:rgb(101,170,207)}
.c64 .fm{color:rgb(138,101,223)}
.c64 .fn{color:rgb(207,138,101)}
.c64 .fo{color:rgb(69,69,69)}
.c64 .fp{color:rgb(170,170,170)}
.c64 .ba{background-color:rgb(255,255,255)}
.c64 .bb{background-color:rgb(0,0,0)}
.c64 .bc{background-color:rgb(69,32,170)}
.c64 .bd{background-color:rgb(101,170,69)}
.c64 .be{background-color:rgb(138,101,32)}
.c64 .bf{background-color:rgb(138,69,32)}
.c64 .bg{background-color:rgb(138,69,170)}
.c64 .bh{background-color:rgb(101,69,0)}
.c64 .bi{background-color:rgb(207,207,101)}
.c64 .bj{background-color:rgb(170,239,138)}
.c64 .bk{background-color:rgb(138,138,138)}
.c64 .bl{background-color:rgb(101,170,207)}
.c64 .bm{background-color:rgb(138,101,223)}
.c64 .bn{background-color:rgb(207,138,101)}
.c64 .bo{background-color:rgb(69,69,69)}
.c64 .bp{background-color:rgb(170,170,170)}
.appleii .fa{color:rgb(255,255,255)}
.appleii .fb{color:rgb(0,0,0)}
.appleii .fc{color:rgb(64,53,121)}
.appleii .fd{color:rgb(64,75,7)}
.appleii .fe{color:rgb(191,180,248)}
.appleii .ff{color:rgb(109,41,64)}
.appleii .fg{color:rgb(218,60,241)}
.appleii .fh{color:rgb(218,104,15)}
.appleii .fi{color:rgb(191,202,134)}
.appleii .fj{color:rgb(38,195,16)}
.appleii .fk{color:rgb(19,87,64)}
.appleii .fl{color:rgb(146,214,191)}
.appleii .fm{color:rgb(37,151,240)}
.appleii .fn{color:rgb(236,168,191)}
.appleii .fo{color:rgb(128,128,128)}
.appleii .fp{color:rgb(128,128,128)}
.appleii .ba{background-color:rgb(255,255,255)}
.appleii .bb{background-color:rgb(0,0,0)}
.appleii .bc{background-color:rgb(64,53,121)}
.appleii .bd{background-color:rgb(64,75,7)}
.appleii .be{background-color:rgb(191,180,248)}
.appleii .bf{background-color:rgb(109,41,64)}
.appleii .bg{background-color:rgb(218,60,241)}
.appleii .bh{background-color:rgb(218,104,15)}
.appleii .bi{background-color:rgb(191,202,134)}
.appleii .bj{background-color:rgb(38,195,16)}
.appleii .bk{background-color:rgb(19,87,64)}
.appleii .bl{background-color:rgb(146,214,191)}
.appleii .bm{background-color:rgb(37,151,240)}
.appleii .bn{background-color:rgb(236,168,191)}
.appleii .bo{background-color:rgb(128,128,128)}
.appleii .bp{background-color:rgb(128,128,128)}

255
css/sally.css Normal file
View File

@ -0,0 +1,255 @@
textarea,input[type=text],body {
margin:0;
font-family: 'FixedsysExcelsior301Regular';
font-size: 12pt;
font-weight: 100;
line-height: 11pt;
color: #6d6b6d;
-webkit-font-smoothing: antialiased !important;
}
body {
background-color: #000000 !important;
}
@font-face {
font-family: 'FixedsysExcelsior301Regular';
src: url('../fonts/fsex300-webfont.eot');
src: url('../fonts/fsex300-webfont.eot%3F') format('embedded-opentype'),
url('../fonts/fsex300-webfont.woff') format('woff'),
url('../fonts/fsex300-webfont.ttf') format('truetype'),
url('../fonts/fsex300-webfont.svg') format('svg');
font-weight: normal;
font-style: normal;
}
a {display: block}
a:link, a:visited {text-decoration: none; color: #6b6760}
a:hover { text-decoration: underline }
.faded { color: #404040; }
.rapper, .block {
float: left;
height:auto;
width:auto;
background-color: #000000;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.rapper {
white-space:pre-wrap;
word-wrap: break-word;
}
#gallery_rapper {
display: inline
}
#ui_rapper .block {
width: 100px;
}
.block {
padding:4px;
}
.block:nth-child(n+2) {
padding-left: 30px;
}
#textarea_mode { padding: 4px; }
.tool {
cursor: pointer;
}
.hidden {
visibility: hidden;
}
.tool.radio {
margin: 0 8px 0 0;
}
.tool.radio.focused {
color: #000;
background-color: #6d6d6d;
box-shadow: none;
}
.transparent {
background-color: transparent;
background-image: url(../img/gray-dither.gif);
background-size: 8px 8px;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.transparent {
background-size: 4px 4px;
}
}
span,a { min-width: 8px; line-height: 15px; display: inline-block; }
body.pixels {
line-height: 8px;
}
.pixels #brush_rapper span,
.pixels #brush_rapper a,
.pixels #canvas_rapper span,
.pixels #canvas_rapper a { line-height: 8px; overflow: hidden; }
.rapper { cursor: crosshair; }
body.grid span { border-right: 1px solid #444; border-bottom: 1px solid #444; }
body.grid div { border-left: 1px solid #444; }
body.grid #canvas_rapper > div:first-child,
body.grid #palette_rapper > div:first-child,
body.grid #letters_rapper > div:first-child,
body.grid #brush_rapper > div:first-child { border-top: 1px solid #444; }
body.grid .tool { border: 1px solid #444; }
.ed { color: #fff; }
.locked { border-bottom: 1px solid; color: #bbb; text-decoration: none; }
.tool.locked.focused { box-shadow: 0 0; }
.focused { box-shadow: inset 0 0px 2px #fff; border-color: #fff; }
.ba.focused { box-shadow: inset 0 0px 2px #000, inset 0 0px 2px #000; border-color: #000; }
.tool.focused, .ed.focused { color: white; text-decoration: underline; }
.focused { box-shadow: inset 1px 0 2px white, inset -1px 0 2px white, inset 0 1px 2px white, inset 0 -1px 2px white; }
.faba.focused, .fbba.focused, .fcba.focused, .fdba.focused, .feba.focused, .ffba.focused, .fgba.focused, .fhba.focused,
.fiba.focused, .fjba.focused, .fkba.focused, .flba.focused, .fmba.focused, .fnba.focused, .foba.focused, .fpba.focused
{ box-shadow: inset 1px 0 2px #888, inset -1px 0 2px #888, inset 0 1px 2px #888, inset 0 -1px 2px #888; }
body.loading { opacity: 0; }
body { transition: 0.1s linear; }
#import_textarea { font-size: 9pt; }
textarea { font-size:12pt; width: 37vw; height: 300px; background: #333; color: #0f0; border: 0; font-family: 'FixedsysExcelsior301Regular'; outline: 0; border: 1px solid #333; background:#010;}
#shader_rapper { display: none; }
#import_rapper { display: none; }
#canvas_rapper {
white-space: pre;
box-shadow: 0 0 2px rgba(255,255,255,0.3);
margin: 3px;
}
#ui_rapper { clear:both; float: left; width: 100vw; }
#workspace_rapper { width: 100%; }
.loading .vertical #ui_rapper { clear: none }
.vertical #ui_rapper { width: 320px; float: left; clear: none; }
.vertical .rapper, .vertical .block { float: left; }
.vertical #canvas_rapper,
.vertical #canvas_rapper div,
.vertical #tools_rapper,
.vertical #palette_rapper,
.vertical #brush_container { display: inline-block; float: left; }
.vertical #workspace_rapper { width: auto; position: relative; float: left; }
.vertical #palette_rapper { margin-right: 10px; }
.vertical #tools_block { min-width: 100%; }
#secret_rapper { float: left; clear: right; }
#secret_rapper span { float: left; }
.vertical #secret_rapper { margin-right: 10px; }
.vertical #secret_rapper span { float: left; clear: both; }
.nopaint #brush_rapper { min-height: 70px; min-width: 50px; }
#nopaint_rapper.hidden {
display: none;
}
.rotated #canvas_rapper {
transform: translateX(-50%) translateY(-50%) translateZ(0) rotate(-90deg);
transform-origin: 50% 50%;
position: absolute;
left: 50%;
}
#tools_block > * {
cursor: crosshair;
}
#brush_rapper, #letters_rapper {
cursor: crosshair;
}
.dropper #canvas_rapper {
cursor: url(../img/dropper.gif) 0 15, auto;
}
.bucket #canvas_rapper {
cursor: url(../img/bucket.png) 3 15, auto;
}
#brush_rapper {
border: 1px solid;
display: inline-block;
margin-bottom: 13px;
float: left;
}
#letters_rapper {
display: inline-block;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.close { position: absolute; top: 20px; right: 20px; z-index: 2; padding: 10px; background: black; cursor: pointer; }
#webcam_rapper { display: none; position: absolute; top: 0px; left: 0px; width:100%;height:100%; box-sizing:border-box; border: 40px solid rgba(0,0,0,0.5); background-color: rgba(0,0,0,0.5); }
#webcam_iframe { position: absolute; top: 0px; left: 0px; width:100%;height:100%; background-color: rgba(0,0,0,0.5); border: 0; }
#experimental_palette_toggle.focused { box-shadow: none; }
#cursor_input { position: fixed; top: 0; right: 0; width:30px; opacity: 0; font-size: 16px; }
.selector_el {
border: 1px dashed #fff !important;
padding-top: 1px;
position:absolute;
margin-top: -1px;
top:-999px;left:-999px;
pointer-events: none;
}
.selector_el.dragging {
color: #0f0;
}
.selector_el.creating div {
display: none;
}
.custom {
float: left;
margin-right: 5px;
margin-bottom: 5px;
}
#username_input {
background: transparent;
padding: 0;
outline: 0;
border: 1px solid transparent;
width: 76px;
}
#username_input:focus {
border: 1px solid #0f0;
color: #0f0;
}
#upload_input {
background: transparent;
padding: 0;
outline: 0;
border: 1px solid #0f0;
color: #0f0;
width: 100%;
max-width: 360px;
}
#upload_button.uploading {
background: transparent;
border: 0;
font-size: 16px;
font-family: 'FixedsysExcelsior301Regular';
-webkit-animation: rainbow 1.0s infinite;
animation: rainbow 2.0s infinite;
padding: 0; margin: 0;
}
@keyframes rainbow {
0% { color: hsl(0,100%,50%) }
33% { color: hsl(90,100%,50%) }
50% { color: #fff }
66% { color: hsl(320,100%,50%) }
100% { color: hsl(360,100%,50%) }
}
.panke #send_to_irc_el {
color: white;
text-decoration: underline;
}
.panke #shader_el,
.panke #load_el,
.panke #gallery_el,
.panke #import_textarea,
.panke #doc_el,
.panke #gallery_el,
.panke #save_button,
.panke #upload_button,
.panke #export_button,
.panke #username_input,
.panke #upload_input,
.panke #grid_el,
.panke #save_el,
.panke #vertical_checkbox,
.panke #add_custom_el,
.panke #format_el { display: none !important; }

38
doc/index.html Normal file
View File

@ -0,0 +1,38 @@
<!-- http://jollo.org/licensing/public/LNT-1.txt -->
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/sally.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="../css/ak.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="http://jollo.org/assets/jibber/css/nitelite.css" type="text/css" charset="utf-8" />
<style>
body { font-family: 'FixedsysExcelsior301Regular'; }
a:nth-of-type(2n+1), a { color: #0f0; }
a:nth-of-type(2n+2), a { color: #ff0; }
</style>
<br><br>
<center>
<table border=35 cellpadding=10>
<tr><td style="background: rgba(0,0,100,0.5)">
<h1>asdf.us/ascii documentation</h1>
<span style="white-space: pre; color: white;">
These are some handy documents which address some of the more obscure
features of the asdf.us color code tool:
* <a href="tips.txt">tips.txt</a> - Tips on using the keyboard
* <a href="irssi.txt">irssi.txt</a> - Instructions on using IRSSI to make color codes.
* <a href="nopaint.txt">nopaint.txt</a> - A guide to "No Paint"
Documents on Shaders
* <a href="shadetut.txt">shadetut.txt</a> - A brief tutorial on ASCII shaders.
* <a href="shaders/brush.txt">shaders/brush.txt</a> - Shaders designed to work on the brush
* <a href="shaders/canvas.txt">shaders/canvas.txt</a> - Shaders designed to work on the canvas
* <a href="shaders/util.txt">shaders/util.txt</a> - Miscellaneous utilities / snippets
For more information on IRC, Color Codes, and much more, visit the
<a href="http://jollo.org/LNT/doc/">documentation sitemap</a>, part of the <a href="http://jollo.org/">Jollo IRC Network</a>.
<a href="https://asdf.us/ascii/">asdf.us/ascii</a>

153
doc/irssi.txt Normal file
View File

@ -0,0 +1,153 @@
__________________________________________________________________________
____ ____ _____ ____ ___ ___ ____
/_____ _____/ / ___ \ / ____/ / ____/ /_____ _____/
/ / / / \ \ / / / / / /
/ / / /____/ / \ \__ \ \__ / /
/ / / ___ __/ \__ \ \__ \ / /
/ / / / \ \ \ \ \ \ / /
_____/ /_____ / / \ \ ____/ / ____/ /____/ /_____
___/ /__/ /_______\ \__/ /___/ // /____
__________________________________________________________________________
OPTIMIZE YOUR TERMINAL FOR COLOR CODES ON OSX
=============================================
You can use terminal, some nerds seem to prefer iterm2, but it's up to you
... http://iterm2.com/
To see color codes correctly, make sure your term type is xterm-256color --
If you use iterm: https://s3.amazonaws.com/luckyplop/a1b0f0e3d6eae746c82194876f2ccd8b200bc3bb.png
If you use terminal: https://s3.amazonaws.com/luckyplop/6a2270b58ea1cfac587607215e1b829f41d47355.png
Restart iterm after changing this setting.
The default iterm colors are kind of ugly for color codes, so you may want to change them
to something like this..
https://s3.amazonaws.com/luckyplop/c5f3a1f2b8e2f8a745fa2638c21af7d26117b91b.png
You can download this iTerm color preset here:
http://asdf.us/ascii/doc/bamboo.itermcolors
INSTALLING IRSSI ON OSX
=======================
For me the easiest thing is to install homebrew >> http://brew.sh/
Follow les instructions and then..
brew install irssi
Then you run irssi from a terminal by typing the magic word..
irssi
SETTING UP IRSSI FOR COLOR CODES
================================
Use these commands for proper unicode support --
/set term_charset utf-8
/set recode_autodetect_utf8 ON
/set recode_fallback ISO-8859-15
/set recode ON
Use these commands to dump color codes quickly and efficiently --
/set cmd_queue_speed 0msec
/set cmds_max_at_once 1
/set flood_max_msgs 0
/set flood_timecheck 0
To make your log go back very far --
/set scrollback_lines 20000
/set scrollback_time 10day
Remember to type /save after doing a /set to save your changes!
/save
Your irssi configuration will be stored in your home directory in..
.irssi/config
NORMAL IRSSI OPERATION
======================
If you do not want to do the autojoin thing these are the commands you'd normally use to connect:
/server -ssl irc.asdf.us 7777
/join #ascii
SETTING UP IRSSI TO AUTOJOIN #ASCII
===================================
First run irssi, then paste in these commands.
Please change YOUR_NICK_HERE to your preferred username!
/network add -nick YOUR_NICK_HERE -user YOUR_NICK_HERE -realname "YOUR NAME HERE" asdf
/server add -network asdf -auto -ssl irc.asdf.us 7777
/channel add -auto #ascii asdf
/save
/quit
Now run irssi again.. it should autoconnect to the channels and stuff.
If you want it to move you into #ascii by default, you can do ctrl-N and then
/layout save
/save
IRC TIPS
========
/join #ascii -- join a channel :)
/part #ascii -- leave a channel ;(
/quit blabla -- quit irc (with the quit message 'blabla')
/list -- list channels
/nick booboo -- change your nick to booboo
/who #ascii -- show complete list of people on #ascii
/names -- show quick list of names
/msg nick blabla -- send someone a private message ;)
IRSSI TIPS
==========
Ctrl-N -- move to next window
Ctrl-P -- move to previous window
/window close -- close a window
Fn-up arrow -- page up (if you don't have a pageup key)
Fn-down arrow -- page down (if you don't have a pagedown key)
IRSSI SCRIPTING
===============
This is its own can of worms. May we suggest:
http://scripts.irssi.org/scripts/noticelogic.pl
http://scripts.irssi.org/scripts/nickcolor.pl
To make it run on startup, copy it into ~/.irssi/scripts/autorun/ (and restart irssi)
Another fun IRC thing is running an XDCC server for sharing files..
-- for more info see http://asdf.us/xdcc/
AND REMEMBER...
===============
Have fun and be safe online!

58
doc/nopaint.txt Normal file
View File

@ -0,0 +1,58 @@
"" 88
9,88m, ,8888, 9,88m, ,888, mm 9,88m, 8888
88 88 86 98 88 88 ,mm88 88 88 88 88
88 88 '8888' 88888' "nn89 88 88 88 "8m
88 a brief tutorial :)
Last month or so I encountered Jeffrey Scudder's tool 'No Paint' -
https://www.nopaint.org/
- an automatic drawing tool with a minimal interface: you control it using
just two buttons. The No Paint tool provided much entertainment on #sally,
so during some downtime I added similar functionality to the asdf.us/ascii
tool. Under the brush, you should see two buttons - to kick it off, click
'paint' and it will begin drawing.
If you don't like what it's doing, click 'no' -
- this will remove the current line and start drawing a new line.
If you like what it's doing, click 'paint' -
- the line will be applied to the canvas, and it will start drawing anew.
While it's going, you can also click 'pause' and it will stop, so you can
save it or draw on it yourself.
Keyboard shortcuts -
left arrow - 'no'
right arrow - 'paint'
down arrow - 'pause'
Right-click toggles -
If you RIGHT-CLICK on "Paint" it will switch tools automatically.
If you RIGHT-CLICK on "No" it will engage TURBO MODE.
Some tools currently implemented -
- solid brush
- erase brush
- color-changing brush
- hue brush
- letter brush
- clone tool
- smear tool
- fill tool
- stars brush
- canvas slide
- canvas scale
- canvas rotate
- canvas colorcycle

126
doc/shaders/brush.txt Normal file
View File

@ -0,0 +1,126 @@
BRUSH SHADERS
=============
Unless noted, these shaders were written to work on the brush itself.
Make sure "brush" is selected and "animate" is checked.
>> distressed texture brush
Sample use of the "choice" function to get a random color.
var char = choice(" abcdef ")
lex.bg = +choice("0124")
lex.fg = +choice("01234")
lex.char = char
lex.opacity = char == " " ? 0 : 1
>> foggy terrain brush
var char = choice(" abcdef ")
lex.bg = choice([14,15])
lex.fg = choice("367")
lex.char = char
lex.opacity = char == " " ? 0 : 1
>> mirror brush (left-right)
NOTE: Animate this on the canvas, then draw:
if (x > w/2) {
lex.assign( canvas.aa[y][w-x] )
}
>> mirror brush (up-down)
NOTE: Animate this on the canvas, then draw:
if (x > h/2) {
lex.assign( canvas.aa[h-y][x] )
}
>> rainbow stardust brush
Uncheck BG and animate this to brush:
lex.fg = hue(t)
lex.char = choice(" ,'.,.','****** ")
>> noise brushes, works on a black background:
lex.bg = max(5, yellow(randint(t)))
lex.opacity = lex.bg == colors.black ? 0 : 1
>> simple rainbow:
if (lex.bg != 1) lex.bg = randint(t)
lex.opacity = lex.bg == colors.black ? 0 : 1
>> self-erasing:
if (lex.bg != 1) lex.bg = yellow(randint(t))
lex.opacity = lex.bg == colors.black ? 0 : 1
>> cycling rainbow brush
if (lex.bg != 1) lex.bg = hue( all_color_hue_order.indexOf( color_names[ lex.bg ] ) + 1 )
lex.opacity = lex.bg == colors.black ? 0 : 1
>> "stars" brush.. set your brush to paint just the character "#"
if (lex.char == "#") {
lex.fg = hue(randint(15))
lex.char = random() > 0.1 ? " " : "+@*.,\"+'*-"[randint(10)]
}
>> use fg char to mask mask what you're drawing on the bg
if (lex.char != "/") { lex.bg = 1 }
>> sharded glitch brush
Example: http://asdf.us/z/kksnvs.png
Use on a brush:
lex.bg = t/y/x
lex.opacity = lex.bg % 1 ? 0 : 1
>> incremental brush
Set your brush to be the ^ character, square, about 10x10
Draw "char" only
Then animate this shader on the canvas:
if (lex.char=="^") {
lex.bg += 1
lex.char = " "
}
lex.bg += 1

237
doc/shaders/canvas.txt Normal file
View File

@ -0,0 +1,237 @@
CANVAS SHADERS
==============
These shaders were written to work on areas of canvas.
Make sure "canvas" is selected and "animate" is checked.
>> original shader..
lex.bg = hue((x+y*y+t/10)/20)
lex.fg = (x+y)%16
lex.char = (y%2) ? ":" : "%"
>> energy ball ascii shader
d = dist(x/2+w/4, y, w/2, h/2)
an = angle(x/2+w/4, y, w/2,h/2)+t/4200
r=10.2
if (d < r) lex.bg = randint(r)
ll=abs(an|0)+""
lex.char=ll[ll.length-1]
if (d > r) {
lex.bg = randint(d)
lex.fg = randint(d)
lex.char = ll[ll.length-2]
}
>> drifting fire
t += sin(x/1000)*100000
pos = y/h*6 + sin(x*3) - cos(y*t/10000-10)
pos = clamp(pos, 0, 6)
lex.bg = hue(pos)
>> the "bJoel56" shader
yy=y
x-=w/2
y-=h/2
lex.bg = blue(yy/h+random())
lex.fg = green(yy/h*4 + sin(x/100+random()/2)) // hue(t/1000)|0;
var abcd=".'~:;!>+=icjtJYSGSXDQKHNWM";
function chara (aa,n) { return aa[clamp(n*aa.length, 0, aa.length)|0] }
lex.char = chara(abcd, y/h*(5/3 + tan(x/100)+random()/1))
>> frog shader v2
t/=-100
d = sinp( (dist(x/2+w/4, y, w/2, h/2) + t)/2 ) * 10
lex.char=',./>"ASE$#'[(floor(d))]
lex.fg = [1,3,9][floor(d*3)%3]
lex.bg=1
>> frog shader v3
// set period to like 0.2 for a normal circle
period = y/10
t/=-100
d = sinp( (dist(x/2+w/4, y, w/2, h/2) + t) * period )
dd = d * 10.5
d3 = dd < 8 ? 0 : 1
lex.char=' .,"+/>OXEN'[(floor(dd))]
lex.fg = [3,9][floor(d3)]
lex.bg=1
>> spaceships
many cool shaders are possible with this technique.. changing the char
gradient (lex.char=...) etc. i love how the dots move on v4.
this is a variation that looks like a bunch of ships flying across the screen.
has a really cool 3d look to it cuz the rows move at different speeds.
period = sin(y)
t/=-100
d = sinp( (dist(x/2+w/4, y, w/2, h/2) + t) * period )
dd = d * 10.5
d3 = dd < 8 ? 0 : 1
lex.char=' .,"+/>\^+'[(floor(dd))]
lex.fg = [3,9][floor(d3)]
lex.bg=1
>> concentric circles with a wavy "sunburst" pattern going around them
x -= w/2
y -= h/2
x /= h
y /= h/2 + 2
r = dist(x,y,0,0)
ang = angle(x,y,0,0)
if (r < 0.6) {
if (abs(mod(sin((r*t)/100000000000) + ang*18,TWO_PI)) < 2)
lex.bg = 12
else
lex.bg = 5
}
else if (r < 0.65)
lex.bg = 4
else if (abs(mod(sin((r*t)/100000000000) + ang*18,TWO_PI)) < 2)
lex.bg = 7
else
lex.bg = 8
>> slash-based interference patterns
if (x > h*2) x=h-x
y-=h/2
t/=2000
if (sin(x-y*t) > 0) {
lex.bg=1
lex.fg=4
lex.char= Math.floor(x-y*10001+t)%2 ? '\/' : '\\'
}
else {
lex.bg=1
lex.fg=9
lex.char= Math.floor(3*x+y+t)%2 ? '\\' : ' '
}
>> sparkling stars
if (lex.char != " ") {
lex.fg =floor( Math.random()*10 )
var az="Xx+*"
lex.char=az[floor(az.indexOf(lex.char)+ t/10000000 +Math.random())%az.length]
}
>> coogi x/y doodle
xx=x
t/=1000
x/=w/2
y/=h/2
y-=1
x-=1
x*=x-sin(y/t)
y*=1
lex.bg = 1 // gray( sin(x/(y/3-1)+t) + sin(y/4+t) )
lex.fg = hue( sin((y/5)+t) - cos(x*t) *5 )
lex.char = " _.,:;\"~| "[Math.round(xx*(y+1+(x+t/102)/4)*13)%13]
>> glitch shader - produces odd combinations of fg/bg
lex.char=String.fromCharCode(lex.char.charCodeAt(0)+1)
lex.bg+=7
lex.fg+=5
>> dots / lines shader
xx = ((t/10*x)*y/10)%8
lex.bg = colors.black
lex.fg = green(x*3+y*5)
lex.char = ((xx%1) !== 0) ? " " : " .,;=+!@"[xx]
>> munching squares horizon
t/=100
y+=10
x-=w/2
x/=y/10
lex.bg=hue((x^y)+t)
>> grayscale vertical interlacing
First, make a canvas that's totally white.
Run this shader:
if (lex.bg == 0) {
lex.bg = ((x)%2) ? 15 : 14
}
Then set your brush to a white square.
Run this shader w/ animate:
if (lex.bg == 0) {
lex.bg = ((x)%2) ? 0 : 1
}
>> nice purple/orange texture
lex.bg=colors.purple
lex.fg=colors.orange
x/=3
x=floor(x+y/2.1) // <- this is cool number to change
if (x+10*sin(x)+10*cos(y/(t%100)) < y/3) {
lex.char="abcdefghijklmnopqrstuvwxyz"[x%26]
} else {
lex.char="abcdefghijklmnopqrstuvwxyz".toUpperCase()[x%26]
}

38
doc/shaders/util.txt Normal file
View File

@ -0,0 +1,38 @@
SHADER UTILITIES
================
These are little snippets which may be useful in writing your own shaders.
>> basic way to slow the frame rate of a shader.
window.zz=window.zz||0
if(!(x+y)) zz++
if (lex.bg != 1 && !(zz % 4)) {
...
}
>> handy for brushes - use color to mask brush shape
lex.opacity = lex.bg == colors.black ? 0 : 1
Tip: Set to "animate brush", then use option+shift (alt+shift) to
copy color from the canvas. Brush will have the "shape" of the
copied color only. Can be a cool effect when used with fg/bg only.
>> copy color from canvas at x/y
lex.assign( canvas.get(x,y) )
>> animate canvas up and to the left..
lex.assign( canvas.get(x+1,y+1) )

156
doc/shadetut.txt Normal file
View File

@ -0,0 +1,156 @@
ASCII SHADER TUTORIAL
=====================
In the asdf.us/ascii shaders, you write a little math function that executes on every
pixel on the selected area. The shaders can affect either the brush, the selected region,
or the whole canvas.
Shaders can also be animated, so they update live. With a shader applied to the brush,
the brush changes continuously as you draw.
THE LEX OBJECT
==============
Essentially you are writing a Javascript function that modifies this "lex" object, which
has four properties
1) lex.bg = this is the background color
2) lex.fg = this is the foreground color (text color)
3) lex.char = this is the letter that you see in the space
4) lex.opacity = this is whether the pixel actually draws or not
- so like a circular brush is opacity 1 in the middle and opacity 0 on the corners
THE COLOR CODE NUMBERS
======================
With lex.bg and lex.fg, the goal is to have a number between 0 and 15, corresponding to
the color code values from mIRC.
If you shift-click on the color palette, you can cycle it around to the one which shows
the actual order of the mIRC colors.
The mIRC colors are the ones that go white, black, dark blue, green, red, dark red ...
and these correspond to the numbers 0, 1, 2, 3, 4 ...
COLOR CYCLING
=============
Additionally there are some color functions that might help -
These functions make it easier to cycle through colors in a way that makes sense logically
(since the mIRC colors are in a weird order)
- hue(...) = this creates a cycle of colors in terms of their hue or color name,
so you get a rainbow that goes from dark red through yellow, green, blue,
purple, and back
- gray(...) = cycles through grayscale
- red(...) yellow(...) green(...) blue(...) purple(...) = use smaller palettes
- inv_hue(...) fire(...) dark_gray(...) = these are oddities i made for fun
VARIABLES
=========
Variables you have at your disposal are similar to the asdf.us/shader tool -
- x, y = the coordinates of the pixel
- mouse.x, mouse.y = the coordinate of the mouse as it hovers over the canvas
- t = the current time, in milliseconds
TIP: The time will increase very quickly - it's good to add t /= 1000 at the top of
your shader so it goes slowly (and won't cause a seizure).
FUNCTIONS
=========
Remember, this is Javascript. You have the basic operators:
+ - / *
And the bitwise operators:
& | ^ ~
You can do if statements with the standard comparison operators:
< > == <= >=
You also have access to all the functions on the Math object:
floor, ceil, round
abs, sign, mod(n,m), xor
pow, exp, sqrt
cos, sin, tan
acos, asin, atan, atan2
random() rand(n) randint(n) randrange(a,b)
E, PI, PHI
And some utility functions which might help:
clamp(n,min,max)
mix(n,a,b) (lerp)
step(a,b)
smoothstep(min,max,n)
avg(m,n,a)
cosp, sinp (mapped to [0,1])
pixel(x,y) == 4*(y*w+h)
dist(x,y,a,b)
angle(x,y,a,b)
choice(array)
deg(radians), rad(degrees)
BEYOND BASIC COLORS
===================
Other weird effects are possible if you combine these color functions.
For instance, if you do hue(x+y) you'll get a rainbow. But remember, this is just
outputting a number between 0 and 15. So you can do hue(x+y) + 1 and get a different
cycle which does not really have anything to do with the rainbow, but looks cool.
HOW DRAWING WORKS IN THE ASCII TOOL
===================================
When you click and drag to draw a line, your mouse produces a series of points which
describe the line you tried to draw. But these points do not necessarily make a
continuous line - more like a series of dots, which it then draw lines between to make
a "line" or "brush stroke".
A line between two points is made by stamping the brush at regular intervals between the
points which, with these brushes, ends up filling the space in between so it looks like
you drew a continuous line.
This is why when you draw a line with a big brush, it smears the outer edges.. The stamps
happen right next to each other, so you wind up seeing mostly brush edges.
You can visualize this effect with the following shader:
lex.bg = mouse.x + mouse.y
Drawing strokes quickly, or slowly.
Make sure to make it animate to brush.
Results could look like this:
http://i.asdf.us/im/f9/1458658781640-ascii-bamboo.png
SAMPLE SHADERS
==============
You can see a list of example shaders here:
http://asdf.us/ascii/doc/shaderz.txt
If you make a cool shader and want to see it on the list, please get in touch!
You can find me on irc.jollo.org:9999 (ssl) in #sally, making color codes with my friends.
Thanks and have fun!
~ Bamboo, 22 Marzo 2016

16
doc/tips.txt Normal file
View File

@ -0,0 +1,16 @@
asdf.us/ascii tips
==================
These keyboard commands work in brush mode (square, circle, cross):
[ brush smaller
] brush bigger
ctrl~click on brush erase cell
ctrl~click on canvas draw with bg color
shift~click on canvas draw line from last position
alt~click on canvas fill brush with sampled color
alt~shift~click on canvas copy canvas to brush
rightclick on palette set bg color (when drawing with a letter)
h/t timb for guide

BIN
fonts/fsex300-webfont.eot Normal file

Binary file not shown.

BIN
fonts/fsex300-webfont.eot? Normal file

Binary file not shown.

6006
fonts/fsex300-webfont.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
fonts/fsex300-webfont.ttf Normal file

Binary file not shown.

BIN
fonts/fsex300-webfont.woff Normal file

Binary file not shown.

BIN
img/bucket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
img/dropper.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
img/gray-dither.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

166
index.html Normal file
View File

@ -0,0 +1,166 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>asciiblaster</title>
<meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=yes" />
<link rel="stylesheet" href="css/sally.css" type="text/css" charset="utf-8" />
<link rel="stylesheet" href="css/ak.css" type="text/css" charset="utf-8" />
</head>
<body class="loading panke">
<div id="goodies_rapper">
</div>
<div id="workspace_rapper">
<div id="canvas_rapper" class="rapper"></div>
</div>
<div id="ui_rapper">
<div class="block" id="tools_block">
<div id="palette_rapper"></div>
<div id="secret_rapper">
<span id="experimental_palette_toggle">.</span>
<!-- <span id="nopaint_toggle">N</span> -->
</div>
<div id="letters_rapper"></div>
<div id="custom_rapper"></div>
</div>
<div id="brush_container" class="block">
<div id="brush_rapper">
</div>
<br>
<span id="fg_checkbox" class="tool">x fg</span><br>
<span id="bg_checkbox" class="tool">x bg</span><br>
<span id="char_checkbox" class="tool">x char</span><br>
<br>
<span id="add_custom_el" class="tool">+ add</span>
<span id="mirror_x_checkbox" class="tool">_ mirror x</span><br>
<span id="mirror_y_checkbox" class="tool">_ mirror y</span><br>
<br>
<span id="undo_el" class="tool hidden">undo</span><br>
<span id="redo_el" class="tool hidden">redo</span><br>
</div>
<div id="tools_rapper" class="block">
<span id="square_el" class="tool">square</span><br>
<span id="circle_el" class="tool">circle</span><br>
<span id="cross_el" class="tool">cross</span><br>
<span id="text_el" class="tool">text</span><br>
<span id="fill_el" class="tool">fill</span><br>
<span id="select_el" class="tool">select</span><br>
<br>
<span id="rotate_el" class="tool">rotate</span><br>
<span id="scale_el" class="tool">scale</span><br>
<span id="translate_el" class="tool">translate</span><br>
<span id="slice_el" class="tool">slice</span><br>
<span id="grid_el" class="tool">_ grid</span>
<!-- <span id="rotate_checkbox" class="tool">_ rotate</span><br> -->
<span id="vertical_checkbox" class="tool">x vertical</span>
<!-- <span id="pixels_checkbox" class="tool">_ pixels</span><br> -->
</div>
<div id="textarea_mode" style="float: left">
<div>
<span id="clear_el" class="tool">new</span>
<span id="save_el" class="tool">save</span>
<span id="load_el" class="tool">load</span>
<br>
<span id="shader_el" class="tool">shader</span>
<span id="webcam_el" class="tool">webcam</span>
<a id="doc_el" href="doc/index.html" target="_blank">doc</a>
<a id="gallery_el" href="https://asdf.us/im/gallery/?tag=ascii&amp;limit=80" target="_blank">gallery</a>
<br>
<span id="advanced_checkbox" class="tool">_ advanced</span>
<br>
<span id="send_to_irc_el" class="tool">&gt; send to IRC</span>
<br>
<div id="nopaint_rapper">
<br>
<span id="nopaint_no_el" class="tool">no</span><br>
<span id="nopaint_paint_el" class="tool">paint</span><br>
<span id="nopaint_pause_el" class="tool hidden">pause</span><br>
</div>
<br>
brush: <span id="brush_w_el" class="ed">5</span> x <span id="brush_h_el" class="ed">5</span><br>
canvas: <span id="canvas_w_el" class="ed">100</span> x <span id="canvas_h_el" class="ed">30</span><br>
</div>
<div id="import_rapper">
<span id="format_el">ascii *irssi mirc ansi</span>
<span id="import_buttons">
<button id="import_button">import</button>
</span>
<div id="gallery_rapper">
<input id="username_input" type="text" placeholder="username">
<input id="upload_input" type="text" placeholder="uploaded url">
<button id="export_button">export</button>
<button id="save_button">save</button>
<button id="upload_button">upload</button>
</div><br>
<div id="cutoff_warning_el">colorcode is too wide for irc and is cutoff</div>
<textarea id="import_textarea"></textarea>
</div>
<div id="shader_rapper">
<span id="animate_checkbox" class="tool">_ animate</span>
to <span id="shader_target_el">*canvas brush selection</span>
<span id="shader_fps_el" class="hidden faded"></span><br>
<textarea id="shader_textarea"></textarea>
</div>
</div>
</div>
<div id="webcam_rapper" class="transparent">
<span class="close" id="webcam_close">x</span>
<iframe id="webcam_iframe"></iframe>
</div>
<input type="text" id="cursor_input">
</body>
<script type="text/javascript-shader" id="demo_shader">
// lex.bg = hue((x+y*y+t/10)/20)
// lex.fg = colors.white
// lex.char = " "
// lex.opacity = 1
</script>
<script src="js/vendor/colorcode.js"></script>
<script src="js/vendor/text-encoder-lite.js"></script>
<script src="js/vendor/dataUriToBlob.js"></script>
<script src="js/vendor/FileSaver.js"></script>
<script src="js/vendor/oktween.js"></script>
<script src="js/util.js"></script>
<script src="js/png.js"></script>
<script src="js/unicode.js"></script>
<script src="js/color.js"></script>
<script src="js/dither.js"></script>
<script src="js/undo.js"></script>
<script src="js/clipboard.js"></script>
<script src="js/upload.js"></script>
<script src="js/user.js"></script>
<script src="js/lex.js"></script>
<script src="js/matrix.js"></script>
<script src="js/blit.js"></script>
<script src="js/tool.js"></script>
<script src="js/shader.js"></script>
<script src="js/draw.js"></script>
<script src="js/ui/brush.js"></script>
<script src="js/ui/canvas.js"></script>
<script src="js/ui/custom.js"></script>
<script src="js/ui/goodies.js"></script>
<script src="js/ui/keys.js"></script>
<script src="js/ui/controls.js"></script>
<script src="js/ui/palette.js"></script>
<script src="js/ui/letters.js"></script>
<script src="js/ui/selection.js"></script>
<script src="js/ui/transform.js"></script>
<script src="js/ui/nopaint.js"></script>
<script src="js/app.js"></script>
</html>

100
js/app.js Normal file
View File

@ -0,0 +1,100 @@
var dragging = false
var drawing = false
var erasing = false
var selecting = false
var filling = false
var changed = false
var transforming = false
var mirror_x = false
var mirror_y = false
var focused
var canvas, tools, palette, controls, brush, mode
var current_tool, current_filetool, current_canvas
var mouse = { x: 0, y: 0 }
function init () {
build()
bind()
clipboard.load_from_location()
}
function build () {
shader.init()
// shader.run(canvas)
shader.animate()
canvas.append(canvas_rapper)
brush.append(brush_rapper)
palette.append(palette_rapper)
letters.append(letters_rapper)
letters.repaint("Basic Latin")
controls.circle.focus()
// controls.shader.focus()
brush.bg = colors.red
brush.generate()
brush.build()
// controls.grid.use()
canvas.resize_rapper()
}
function bind () {
canvas.bind()
palette.bind()
letters.bind()
brush.bind()
controls.bind()
keys.bind()
clipboard.bind()
window.addEventListener('mouseup', function(e){
dragging = erasing = false
// if (current_filetool.name != 'shader' && current_filetool.name != 'load' && current_filetool.name != 'save' && is_desktop) {
// cursor_input.focus()
// }
var ae = document.activeElement
if (ae !== shader_textarea && ae !== import_textarea && ae !== username_input && ae !== upload_input) {
if (is_desktop) cursor_input.focus()
}
if (selecting) {
selection.up(e)
}
else if (transforming) {
transform.up(e)
}
})
window.addEventListener("touchend", function(){
if (current_tool.name === "text") {
if (is_desktop) cursor_input.focus()
}
dragging = false
})
window.addEventListener('mousedown', function(e){
// if (current_filetool.name != 'shader' && is_desktop) { cursor_input.focus() }
})
document.addEventListener('DOMContentLoaded', function(){
if (is_desktop) { cursor_input.focus() }
document.body.classList.remove('loading')
})
window.onbeforeunload = function() {
// if (changed && !in_iframe()) return "You have edited this drawing."
}
function in_iframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
}
init()

105
js/blit.js Normal file
View File

@ -0,0 +1,105 @@
var blit = (function(){
var blit = {}
blit.and = blit.atop = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.getCell(u+x, v+y)
if (cell && lex.opacity > 0) {
cell.assign(lex)
}
})
}
blit.or = blit.under = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.getCell(u+x, v+y)
if (cell && cell.opacity == 0) {
cell.assign(lex)
}
})
}
// copy the region of A beginning at x,y into B
blit.copy_from = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.getCell(u+x, v+y)
if (cell) {
lex.assign(cell)
}
})
}
blit.copy_toroidal_from = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.get(u+x, v+y)
if (cell) {
lex.assign(cell)
}
})
}
blit.copy_to = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.getCell(u+x, v+y)
if (cell) {
cell.assign(lex)
}
})
}
blit.invert = function(A, B, x, y){
x = x || 0 ; y = y || 0
B.forEach(function(lex, u, v){
var cell = A.getCell(u+x, v+y)
if (cell && lex.opacity > 0) {
cell.fg = get_inverse(cell.fg)
cell.bg = get_inverse(cell.bg)
}
})
}
var distance_rect = function(x, y, ratio){
return Math.sqrt((Math.pow(y * ratio, 2)) + Math.pow(x, 2))
}
var distance_square = function(x, y, ratio){
return Math.sqrt((Math.pow(y * ratio, 2)) + Math.pow(x * ratio, 2))
}
blit.circle = function(A, lex){
var hw = brush.w/2, hh = brush.h/2
var ratio, distance
if (brush.w === brush.h){
distance = distance_square
ratio = hw / hh * (brush.w === 3 || brush.w === 5 ? 1.2 : 1.05)
} else {
distance = distance_rect
ratio = hw / hh
}
A.forEach(function(lex,x,y) {
if (distance(x - hw + 0.5, y - hh + 0.5, ratio) > hw){
lex.clear()
}
})
}
blit.cross = function(A, lex){
A.forEach(function(lex,x,y) {
if ((x+y)%2) {
lex.clear()
}
})
}
blit.inverted_cross = function(A, lex){
// 1x1 brush should still draw something
if (A.w == 1 && A.h == 1) {
return
}
A.forEach(function(lex,x,y) {
if (!((x+y)%2)) {
lex.clear()
}
})
}
blit.square = function(A, lex){
// i.e. no transparency
}
return blit
})()

330
js/clipboard.js Normal file
View File

@ -0,0 +1,330 @@
var clipboard = (function () {
var exports = {
format: "irssi",
importing: false,
visible: false,
canvas: document.createElement("canvas"),
canvas_r: document.createElement("canvas"),
bind: function () {
// import_ascii.addEventListener("change", exports.setFormat("ascii"))
// import_irssi.addEventListener("change", exports.setFormat("irssi"))
// import_mirc.addEventListener("change", exports.setFormat("mirc"))
import_button.addEventListener("click", exports.import_colorcode)
export_button.addEventListener("click", exports.export_data)
save_button.addEventListener("click", exports.save_png)
upload_button.addEventListener("click", exports.upload_png)
import_textarea.addEventListener("focus", exports.focus)
import_textarea.addEventListener("blur", exports.blur)
import_textarea.addEventListener('paste', exports.paste)
// import_irssi.setAttribute("checked", true)
},
setFormat: function (name) {
return function () {
clipboard.format = name
if (! clipboard.importing) { clipboard.export_data() }
}
},
show: function () { import_rapper.style.display = "block"; clipboard.visible = true; changed = false },
hide: function () { import_rapper.style.display = "none"; clipboard.visible = false },
focus: function () {
if (! clipboard.importing) {
import_textarea.focus()
import_textarea.select()
}
},
blur: function () {
},
import_mode: function () {
focus()
clipboard.importing = true
gallery_rapper.style.display = 'none'
format_el.style.display = 'none'
cutoff_warning_el.style.display = 'none'
import_buttons.style.display = "inline"
import_textarea.value = ""
},
export_mode: function () {
focus()
clipboard.importing = false
import_buttons.style.display = "none"
format_el.style.display = 'inline'
cutoff_warning_el.style.display = 'none'
gallery_rapper.style.display = 'inline'
clipboard.export_data()
},
paste: function (e) {
e.preventDefault()
// images will come through as files
var types = toArray(e.clipboardData.types)
import_textarea.value = ""
types.forEach(function(type, i){
console.log(type)
// this can be text/plain or text/html..
if (type.match('text/plain')) {
import_textarea.value = e.clipboardData.getData(type)
}
else {
console.error("unknown type!", item.type)
}
})
},
import_colorcode: function (data, no_undo) {
if (data && data.preventDefault) {
data = import_textarea.value
}
else {
data = data || import_textarea.value
}
var irssi_style_regex = /^\s*\/exec -out printf ("%b" )?"/;
// turn irssi style into mirc style
if (data.match(irssi_style_regex)){
data = data.replace(/\\x03/gm, '\x03')
.replace(/(\\x..)+/gm, unicode.unescapeFromEscapedBytes)
.replace(/\\x5C/g, '\\')
.replace(/\\n/gm, '\n')
.replace(/\\`/gm, '`')
.replace(/\\"/gm, '"')
.replace(/\\\$/gm, '$')
.replace(irssi_style_regex, '')
.replace(/"\s*$/, '')
}
// not a colorcode
if (!data.match(/\x03/))
return exports.import_text();
var json = colorcode.to_json(data, {fg:0, bg:1})
if (!no_undo) undo.new()
if (!no_undo) undo.save_rect(0,0, canvas.w, canvas.h)
if (json.w !== canvas.w || json.h !== canvas.h){
if (!no_undo) undo.save_size(canvas.w, canvas.h)
canvas.resize(json.w, json.h, true)
}
canvas.clear()
for (var y = 0, line; line = json.lines[y]; y++){
var row = canvas.aa[y]
for (var x = 0, char; char = line[x]; x++){
var lex = row[x]
lex.char = String.fromCharCode(char.value)
lex.fg = char.fg
lex.bg = char.bg
lex.opacity = 1
lex.build()
}
}
current_filetool && current_filetool.blur()
},
import_text: function () {
var data = import_textarea.value
var lines = data.split("\n")
var width = lines.reduce(function(a,b){ console.log(a,b); return Math.max(a, b.length) }, 0)
var height = lines.length
if (width > canvas.max) {
return alert("input too wide")
}
if (height > canvas.max) {
return alert("input too tall")
}
undo.new()
undo.save_rect(0,0, canvas.w, canvas.h)
canvas.clear()
lines.forEach(function(line, y){
var row = canvas.aa[y]
if (! row) return
for (var x = 0; x < line.length; x++) {
var lex = row[x]
if (! lex) return
lex.char = line[x]
lex.fg = brush.bg
lex.opacity = 1
lex.build()
}
})
// TODO: some notion of a "selected" region which cuts/clones the underlying region
// var pasted_region = new Matrix (width, height, function(x,y){
// var lex = new Lex (x,y)
// lex.char = lines[y][x] || " "
// lex.build()
// return lex
// })
},
export_data: function () {
var output
// switch (clipboard.format) {
switch (controls.save_format.value) {
case 'ascii':
output = canvas.ascii()
break
case 'mirc':
output = canvas.mirc({cutoff: 400})
break
case 'irssi':
output = canvas.irssi({cutoff: 400})
break
case 'ansi':
output = canvas.ansi()
break
}
if (output.cutoff){
cutoff_warning_el.style.display = 'block'
} else {
cutoff_warning_el.style.display = 'none'
}
import_textarea.value = output
clipboard.focus()
return output
},
rotate_canvas: function(){
var cr = clipboard.canvas_r, c = clipboard.canvas
cr.width = c.height
cr.height = c.width
var ctx = cr.getContext('2d')
ctx.resetTransform()
ctx.translate(0, cr.height)
ctx.rotate(-Math.PI / 2)
ctx.drawImage(c, 0, 0)
return cr
},
export_canvas: function (done_fn) {
var opts = {
palette: 'mirc',
font: canvas.pixels ? 'fixedsys_8x8' : 'fixedsys_8x15',
fg: 0,
bg: 1,
canvas: clipboard.canvas
}
opts.done = function(){
var c = canvas.rotated ? clipboard.rotate_canvas() : clipboard.canvas
if (done_fn) done_fn(c)
}
var start = Date.now();
colorcode.to_canvas(canvas.mirc(), opts)
var total = Date.now() - start;
console.log("took " + total)
},
filename: function () {
return [ +new Date, "ascii", user.username ].join("-")
},
save_png: function () {
var save_fn = function(canvas_out){
var filename = clipboard.filename() + ".png"
var blob = PNG.canvas_to_blob_with_colorcode(canvas_out, canvas.mirc())
saveAs(blob, filename);
}
clipboard.export_canvas(save_fn)
},
upload_png: function () {
var upload_fn = function(canvas_out){
var blob = PNG.canvas_to_blob_with_colorcode(canvas_out, canvas.mirc())
var filename = clipboard.filename()
var tag = 'ascii'
upload(blob, filename, tag, canvas.mirc())
}
clipboard.export_canvas(upload_fn)
}
}
// http...?a=1&b=2&b=3 -> {a: '1', b: ['2', '3']}
function parse_url_search_params(url){
var params = {}
url = url.split('?')
if (url.length < 2) return params
var search = url[1].split('&')
for (var i = 0, pair; pair = search[i]; i++){
pair = pair.split('=')
if (pair.length < 2) continue
var key = pair[0]
var val = pair[1]
if (key in params){
if (typeof params[key] === 'string'){
params[key] = [params[key], val]
}
else params[key].push(val)
}
else params[key] = val
}
return params
}
function get_filetype(txt){
txt = txt.split('.')
return txt[txt.length - 1].toLowerCase()
}
function fetch_url(url, f, type){
type = type || 'arraybuffer'
url = "/cgi-bin/proxy?" + url
//url = "http://198.199.72.134/cors/" + url
var xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = type
xhr.addEventListener('load', function(){ f(xhr.response) })
xhr.send()
}
function load_text(txt){
clipboard.import_colorcode(txt, true)
}
function load_png(buf){
var chunks = PNG.decode(buf)
if (!chunks) return
var itxt_chunks = []
for (var i=0, c; c=chunks[i]; i++){
if (c.type !== 'iTXt') continue
var itxt = PNG.decode_itxt_chunk(c)
if (!itxt.keyword || itxt.keyword !== 'colorcode') continue
clipboard.import_colorcode(itxt.data, true)
}
}
function sally_url_convert(url){
var png_regex = /^https?:\/\/jollo\.org\/den\/sallies\/([0-9]+)\/([^.]+)\.png$/
var matches = url.match(png_regex)
if (!matches) return url
return 'http://jollo.org/den/sallies/' + matches[1] + '/raw-' + matches[2] + '?.txt'
// txt suffix to force asdf proxy
}
exports.load_from_location = function(){
var params = parse_url_search_params(window.location + '')
if (!params.url) return
var url = params.url
url = sally_url_convert(url)
var type = get_filetype(url)
switch (type){
case 'txt':
fetch_url(url, load_text, 'text')
break
case 'png':
fetch_url(url, load_png)
break
}
}
return exports
})()

106
js/color.js Normal file
View File

@ -0,0 +1,106 @@
var fillColor = 1 // black
var color_names = ("white black dark-blue green red dark-red purple orange " +
"yellow lime teal cyan blue magenta dark-gray light-gray").split(" ");
var all_color_hue_order = "dark-red red orange yellow lime green teal cyan blue dark-blue purple magenta black dark-gray light-gray white".split(" ");
var all_color_inv_order = "cyan teal blue dark-blue purple magenta dark-red red orange yellow lime green white light-gray dark-gray black".split(" ");
var color_hue_order = "dark-red red orange yellow lime cyan teal blue dark-blue purple magenta".split(" ");
var color_inv_order = "cyan teal blue dark-blue purple magenta dark-red red orange yellow lime green".split(" ");
var gray_names = ("black dark-gray light-gray white").split(" ")
var fire_names = ("black dark-red red orange yellow white cyan").split(" ")
var red_names = ("black dark-red red").split(" ")
var yellow_names = ("black orange yellow white").split(" ")
var green_names = ("teal green lime").split(" ")
var blue_names = ("black dark-blue blue").split(" ")
var purple_names = ("dark-blue purple magenta red").split(" ")
var dark_gray_names = ("black dark-blue teal dark-gray light-gray white").split(" ")
var color_alphabet = "abcdefghijklmnop";
var colors = {}
color_names.forEach(function(name, i){
colors[name.replace("-", "")] = i
colors[name] = i
})
colors.brown = 5
function get_inverse (n) { return colors[all_color_inv_order.indexOf(color_names[n])] }
function mirc_color (n) { return mod(n, 16)|0 }
function mirc_color_reverse (n) { return mod(-(n+1), 16)|0 }
function all_hue (n) { return colors[all_color_hue_order[mod(n, 16)|0]] }
function all_inv_hue (n) { return colors[all_color_inv_order[mod(n, 16)|0]] }
function hue (n) { return colors[color_hue_order[mod(n, 11)|0]] }
function rand_hue () { return colors[color_hue_order[randint(11)]] }
function rand_gray () { return colors[gray_names[randint(4)]] }
function inv_hue (n) { return colors[color_inv_order[mod(n, 11)|0]] }
function gray (n) { return colors[gray_names[mod(n, 4)|0]] }
function fire (n) { return colors[fire_names[mod(n, 7)|0]] }
function red (n) { return colors[red_names[mod(n, 3)|0]] }
function yellow (n) { return colors[yellow_names[mod(n, 4)|0]] }
function green (n) { return colors[green_names[mod(n, 3)|0]] }
function blue (n) { return colors[blue_names[mod(n, 3)|0]] }
function purple (n) { return colors[purple_names[mod(n, 4)|0]] }
function dark_gray (n) { return colors[dark_gray_names[mod(n, 4)|0]] }
var css_lookup = {
'rgb(255, 255, 255)': 'A',
'rgb(0, 0, 0)': 'B',
'rgb(0, 0, 127)': 'C',
'rgb(0, 147, 0)': 'D',
'red': 'E',
'rgb(127, 0, 0)': 'F',
'rgb(156, 0, 156)': 'G',
'rgb(252, 127, 0)': 'H',
'rgb(255, 255, 0)': 'I',
'rgb(0, 252, 0)': 'J',
'rgb(0, 147, 147)': 'K',
'rgb(0, 255, 255)': 'L',
'rgb(0, 0, 252)': 'M',
'rgb(255, 0, 255)': 'N',
'rgb(127, 127, 127)': 'O',
'rgb(210, 210, 210)': 'P',
};
var css_reverse_lookup = {}
Object.keys(css_lookup).forEach(function(color){
css_reverse_lookup[ css_lookup[color].charCodeAt(0) - 65 ] = color
})
var ansi_fg = [
97, // white
30, // black
34, // dark blue
32, // green
91, // light red
31, // dark red
35, // purple
33, // "dark yellow" (orange?)
93, // "light yellow"
92, // light green
36, // cyan (teal?)
96, // light cyan
94, // light blue
95, // light magenta
90, // dark gray
37, // light gray
]
var ansi_bg = [
107, // white
40, // black
44, // dark blue
42, // green
101, // light red
41, // dark red
45, // purple
43, // yellow (orange)
103, // light yellow
102, // light green
46, // cyan (teal?)
106, // light cyan
104, // light blue
105, // light magenta
100, // dark gray
47, // light gray
]

10
js/dither.js Normal file
View File

@ -0,0 +1,10 @@
var dither = {
aa: '▓▒░ ',
a: '▓',
b: '▒',
c: '░',
d: ' ',
p: function(n){
return dither.aa[Math.floor(Math.abs(n) % 4)]
}
}

221
js/draw.js Normal file
View File

@ -0,0 +1,221 @@
var draw = (function(){
var last_point = [0,0]
function down (e, lex, point) {
var w = canvas.w, h = canvas.h
erasing = (e.which == "3" || e.ctrlKey)
changed = true
if (e.shiftKey) {
line (lex, last_point, point, erasing)
if (mirror_x) {
line(lex, [w-last_point[0], last_point[1]], [w-point[0], point[1]], erasing)
}
if (mirror_y) {
line(lex, [last_point[0], h-last_point[1]], [point[0], h-point[1]], erasing)
}
if (mirror_x && mirror_y) {
line(lex, [w-last_point[0], h-last_point[1]], [w-point[0], h-point[1]], erasing)
}
}
else {
stamp (canvas, brush, point[0], point[1], erasing)
if (mirror_x) {
stamp (canvas, brush, w-point[0], point[1], erasing)
}
if (mirror_y) {
stamp (canvas, brush, point[0], h-point[1], erasing)
}
if (mirror_x && mirror_y) {
stamp (canvas, brush, w-point[0], h-point[1], erasing)
}
}
last_point[0] = point[0]
last_point[1] = point[1]
}
function set_last_point (e, point) {
last_point[0] = point[0]
last_point[1] = point[1]
}
function move (e, lex, point) {
var w = canvas.w, h = canvas.h
line(lex, last_point, point, erasing)
if (mirror_x) {
line(lex, [w-last_point[0], last_point[1]], [w-point[0], point[1]], erasing)
}
if (mirror_y) {
line(lex, [last_point[0], h-last_point[1]], [point[0], h-point[1]], erasing)
}
if (mirror_x && mirror_y) {
line(lex, [w-last_point[0], h-last_point[1]], [w-point[0], h-point[1]], erasing)
}
last_point[0] = point[0]
last_point[1] = point[1]
}
function move_toroidal (e, lex, point) {
var w = canvas.w, h = canvas.h
var src_x_quantile = quantile( last_point[0], w )
var src_y_quantile = quantile( last_point[1], h )
var dst_x_quantile = quantile( point[0], w )
var dst_y_quantile = quantile( point[1], h )
var src_x_mod = mod( last_point[0], w )
var src_y_mod = mod( last_point[1], h )
var dst_x_mod = mod( point[0], w )
var dst_y_mod = mod( point[1], h )
// if we've moved across the edge of the board, draw two lines
if (src_x_quantile != dst_x_quantile || src_y_quantile != dst_y_quantile) {
var xa, ya
if (src_x_quantile < dst_x_quantile) {
xa = [
[src_x_mod, dst_x_mod + w],
[src_x_mod-w, dst_x_mod],
]
}
else if (src_x_quantile == dst_x_quantile) {
xa = [
[src_x_mod, dst_x_mod],
[src_x_mod, dst_x_mod],
]
}
else {
xa = [
[src_x_mod, dst_x_mod-w],
[src_x_mod+w, dst_x_mod],
]
}
if (src_y_quantile < dst_y_quantile) {
ya = [
[src_y_mod, dst_y_mod + h],
[src_y_mod-h, dst_y_mod],
]
}
else if (src_y_quantile == dst_y_quantile) {
ya = [
[src_y_mod, dst_y_mod],
[src_y_mod, dst_y_mod],
]
}
else {
ya = [
[src_y_mod, dst_y_mod-h],
[src_y_mod+h, dst_y_mod],
]
}
line(lex, [ xa[0][0], ya[0][0] ], [ xa[0][1], ya[0][1] ], erasing)
line(lex, [ xa[1][0], ya[1][0] ], [ xa[1][1], ya[1][1] ], erasing)
}
else {
var x_a = mod( last_point[0], w )
var y_a = mod( last_point[1], h )
var x_b = mod( point[0], w )
var y_b = mod( point[1], h )
var last_point_mod = [x_b, y_b], point_mod = [x_a, y_a]
line(lex, last_point_mod, point_mod, erasing)
// if (mirror_x) {
// line(lex, [w-last_point_mod[0], last_point_mod[1]], [w-point_mod[0], point_mod[1]], erasing)
// }
// if (mirror_y) {
// line(lex, [last_point_mod[0], h-last_point_mod[1]], [point_mod[0], h-point_mod[1]], erasing)
// }
}
last_point[0] = point[0]
last_point[1] = point[1]
// y = point.y
}
function point (lex, x, y, erasing) {
stamp (canvas, brush, x, y, erasing)
}
function line (lex, a, b, erasing) {
var len = dist(a[0], a[1], b[0], b[1])
var bw = 1
var x, y, i;
for (var i = 0; i <= len; i += bw) {
x = lerp(i / len, a[0], b[0])
y = lerp(i / len, a[1], b[1])
stamp (canvas, brush, x, y, erasing)
}
}
function stamp (canvas, brush, x, y, erasing) {
var hh = brush.w/2|0
brush.forEach(function(lex, s, t){
s = round( s + x-hh )
t = round( t + y-hh )
if (s >= 0 && s < canvas.w && t >= 0 && t < canvas.h) {
if (lex.opacity === 0 && lex.char === ' ') return;
var aa = canvas.aa[t][s]
undo.save_lex(s, t, aa)
if (erasing) {
aa.erase(lex)
}
else {
aa.stamp(lex, brush)
}
}
})
}
function fill (lex, x, y) {
var q = [ [x,y] ]
var aa = canvas.aa
var target = aa[y][x].clone()
var n, w = 0, e = 0, j = 0
var kk = 0
// gets into a weird infinite loop if we don't break here.. :\
if (target.eq(lex)) { return }
LOOP: while (q.length) {
n = q.shift()
if (aa[n[1]][n[0]].ne(target)) {
continue LOOP
}
w = e = n[0]
j = n[1]
WEST: while (w > 0) {
if (aa[j][w-1].eq(target)) {
w = w-1
}
else {
break WEST
}
}
EAST: while (e < canvas.w-1) {
if (aa[j][e+1].eq(target)) {
e = e+1
}
else {
break EAST
}
}
for (var i = w; i <= e; i++) {
undo.save_lex(i, j, aa[j][i])
aa[j][i].assign(lex)
if (j > 0 && aa[j-1][i].eq(target)) {
q.push([ i, j-1 ])
}
if (j < canvas.h-1 && aa[j+1][i].eq(target)) {
q.push([ i, j+1 ])
}
}
}
}
var draw = {}
draw.down = down
draw.set_last_point = set_last_point
draw.move = move
draw.move_toroidal = move_toroidal
draw.stamp = stamp
draw.line = line
draw.point = point
draw.fill = fill
return draw
})()

138
js/lex.js Normal file
View File

@ -0,0 +1,138 @@
function Lex (x,y) {
if (typeof x == "number") {
this.y = y
this.x = x
this.span = document.createElement("span")
}
else {
this.span = x
}
this.fg = colors.white
this.bg = colors.black
this.char = " "
this.opacity = 1
this.focused = false
}
Lex.prototype.build = function(){
if (isNaN(this.bg) || this.bg == Infinity || this.bg == -Infinity) this.bg = colors.black
if (isNaN(this.fg) || this.fg == Infinity || this.fg == -Infinity) this.fg = colors.black
this.span.className = this.css()
this.span.innerHTML = this.html()
}
Lex.prototype.css = function(){
return (
this.focused ?
"focused " : ""
) + (
this.opacity === 0 ?
"transparent f" + color_alphabet[modi(this.fg,16)] :
"f" + color_alphabet[modi(this.fg,16)] + " b" + color_alphabet[modi(this.bg,16)]
)
}
Lex.prototype.html = function(){
return this.char == " " ? "&nbsp;" : this.char || "&nbsp;"
}
Lex.prototype.read = function(){
this.char = this.span.innerHTML
return this.char
}
Lex.prototype.ascii = function(){
return this.char || " "
}
Lex.prototype.sanitize = function(){
switch (this.char) {
// case "%": return "%"
case undefined:
case "": return " "
default: return this.char
}
}
var fgOnly = false
Lex.prototype.mirc = function(){
var char = this.char || " "
if (fgOnly) {
return "\x03" + (this.fg&15) + char
}
if ((this.bg&15) < 10 && ! isNaN(parseInt(char))) {
return "\x03" + (this.fg&15) + ",0" + (this.bg&15) + char
}
else {
return "\x03" + (this.fg&15) + "," + (this.bg&15) + char
}
}
Lex.prototype.ansi = function(){
var fg = ansi_fg[ this.fg&15 ]
var bg = ansi_bg[ this.bg&15 ]
var c = this.sanitize()
if (c == "\\") c = "\\\\"
if (c == '"') c = '\\"'
return "\\e[" + fg + ";" + bg + "m" + c
}
Lex.prototype.assign = function (lex){
this.fg = lex.fg
this.bg = lex.bg
this.char = lex.char
this.opacity = lex.opacity
this.build()
}
Lex.prototype.stamp = function (lex, brush){
if (brush.draw_fg) this.fg = lex.fg
if (brush.draw_bg && lex.opacity > 0) this.bg = lex.bg
if (brush.draw_char) this.char = lex.char
this.opacity = 1
this.build()
}
Lex.prototype.clone = function () {
var lex = new Lex (0,0)
lex.assign(this)
return lex
}
Lex.prototype.erase = function (){
this.fg = fillColor
this.bg = fillColor
this.char = " "
this.opacity = 1
this.build()
}
Lex.prototype.eq = function(lex){
return lex && this.fg == lex.fg && this.bg == lex.bg && this.char == lex.char
}
Lex.prototype.eqColor = function(lex){
return lex && this.fg == lex.fg && this.bg == lex.bg
}
Lex.prototype.ne = function(lex){
return ! this.eq(lex)
}
Lex.prototype.clear = function(){
this.bg = colors.black
this.fg = 0
this.char = " "
this.opacity = 0
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')
this.focused = true
focused = this
}
Lex.prototype.blur = function(){
focused = null
this.span && this.span.classList.remove('focused')
this.focused = false
this.onBlur && this.onBlur()
}
Lex.prototype.demolish = function(){
if (this.span.parentNode) { this.span.parentNode.removeChild(this.span) }
this.span = null
}
Lex.prototype.key = function(char, keyCode) {
if (! char) { return }
this.char = char
this.fg = brush.fg
this.build()
return true
}

321
js/matrix.js Normal file
View File

@ -0,0 +1,321 @@
function Matrix (w,h,f){
this.x = 0
this.y = 0
this.w = w
this.h = h
this.f = f
this.focus_x = 0
this.focus_y = 0
this.initialize()
}
Matrix.prototype.initialize = function(f){
var w = this.w || 1, h = this.h || 1, f = f || this.f
var aa = new Array (h)
for (var y = 0; y < h; y++) {
aa[y] = new Array (w)
for (var x = 0; x < w; x++) {
aa[y][x] = f(x,y)
}
}
this.aa = aa
}
Matrix.prototype.rebuild = function (){
this.demolish()
this.initialize()
this.append()
this.bind()
this.generate && this.generate()
this.focus_clamp()
check_if_lost_focus()
}
Matrix.prototype.clone = function () {
var base = this
var clone = new Matrix(this.w, this.h, function(x,y){
return base.getCell(x,y).clone()
})
clone.f = this.f
return clone
}
Matrix.prototype.assign = function (mat) {
var base = this
this.demolish()
this.w = mat.w
this.h = mat.h
// this.f = function(){}
this.initialize(function(x,y){
var el = mat.getCell(x,y).clone()
el.build()
return el
})
this.append()
this.bind()
check_if_lost_focus()
return this
}
Matrix.prototype.bind = function () {}
Matrix.prototype.demolish = function (){
this.forEach(function(lex){
lex.demolish()
})
while (this.rapper && 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.focus_clamp = function(){
this.focus_x = clamp(this.focus_x, 0, this.w - 1)
this.focus_y = clamp(this.focus_y, 0, this.h - 1)
}
Matrix.prototype.focus_add = function(x, y){
this.focus(this.focus_x + x, this.focus_y + y)
}
Matrix.prototype.focus = function(x, y){
if (x === undefined) x = this.focus_x
if (y === undefined) y = this.focus_y
x = mod(x, this.w)
y = mod(y, this.h)
this.focus_x = x
this.focus_y = y
//focused_input = this
this.aa[y][x].focus()
}
Matrix.prototype.focusLex = function(y,x){
if (x < 0) {
y -= 1
}
if (x > this.aa[0].length) {
y += 1
}
this.aa[mod(y,this.h)][mod(x,this.w)].focus()
}
Matrix.prototype.clear = function(){
this.forEach(function(lex,x,y){ lex.clear() })
}
Matrix.prototype.erase = function(){
this.forEach(function(lex,x,y){ lex.erase() })
}
Matrix.prototype.fill = function(lex){
this.fg = lex.fg
this.bg = lex.bg
this.char = lex.char
this.opacity = lex.opacity
this.forEach(function(el,x,y){
el.assign(lex)
el.build()
})
}
Matrix.prototype.build = function(){
this.forEach(function(lex,x,y){
lex.build()
})
}
Matrix.prototype.append = function(rapper){
rapper = this.rapper = rapper || this.rapper
if (! this.rapper) return
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.region = function(w,h,x,y) {
w = w || 1
h = h || 1
x = x || 0
y = y || 0
var parent = this
var mat = new Matrix(w, h, function(x,y){
return parent.aa[y][x]
})
mat.f = this.f
return mat
}
Matrix.prototype.setCell = function(lex,x,y){
this.aa[y] && this.aa[y][x] && this.aa[y][x].assign(lex)
}
Matrix.prototype.getCell = function(x,y){
if (this.aa[y] && this.aa[y][x]) return this.aa[y][x]
else return null
}
Matrix.prototype.get = function(x,y){
y = floor(mod(y || 0, this.h))
x = floor(mod(x || 0, this.w))
if (this.aa[y] && this.aa[y][x]) return this.aa[y][x]
else return null
}
Matrix.prototype.resize = function(w,h){
w = w || canvas.w
h = h || canvas.h
var div, row, lex
var f = this.f, old_h = this.aa.length, old_w = this.aa[0].length
var rapper = this.rapper
w = max(w, 1)
h = max(h, 1)
if (h < old_h) {
for (var y = old_h; y > h; y--) {
row = this.aa.pop()
div = row[0].span.parentNode
row.forEach(function(lex, x){
lex.demolish()
})
div.parentNode.removeChild(div)
}
}
else if (h > old_h) {
for (var y = old_h; y < h; y++) {
div = document.createElement("div")
rapper.appendChild( div )
this.aa[y] = new Array (w)
for (var x = 0; x < w; x++) {
lex = this.aa[y][x] = f(x,y)
div.appendChild(lex.span)
}
}
}
if (w < old_w) {
this.aa.forEach(function(row, y){
while (row.length > w) {
lex = row.pop()
lex.demolish()
}
})
}
else if (w > old_w) {
this.aa.forEach(function(row, y){
div = row[0].span.parentNode
for (var x = row.length; x < w; x++) {
lex = row[x] = f(x,y)
div.appendChild(lex.span)
}
})
}
this.w = w
this.h = h
this.bind && this.bind()
this.focus_clamp()
if (this.rapper && this.rapper.parentNode != document.body) {
this.resize_rapper()
}
}
Matrix.prototype.resize_rapper = function(){
var cell = canvas.aa[0][0].span
var cw = cell.offsetWidth
var ch = cell.offsetHeight
// if (canvas.grid) { ch++ }
var width = cw * this.aa[0].length
var height = ch * this.aa.length
if (canvas.grid) { width++; height++ }
if (this.rotated) {
this.rapper.parentNode.classList.add("rotated")
this.rapper.parentNode.style.height = (width) + "px"
this.rapper.parentNode.style.width = (height) + "px"
this.rapper.style.top = (width/2) + "px"
// this.rapper.style.left = ((canvas_rapper.offsetHeight+20)/2) + "px"
}
else {
this.rapper.parentNode.classList.remove("rotated")
this.rapper.parentNode.style.height = ""
this.rapper.style.width =
this.rapper.parentNode.style.width = (width) + "px"
this.rapper.style.top = ""
// canvas_rapper.style.left = "auto"
}
}
Matrix.prototype.ascii = function () {
var lines = this.aa.map(function(row, y){
var last, line = ""
row.forEach(function(lex, x) {
line += lex.ascii()
})
return line // .replace(/\s+$/,"")
})
var txt = lines.join("\n")
return txt
}
Matrix.prototype.ansi = function (opts) {
var lines = this.aa.map(function(row, y){
var last, line = ""
row.forEach(function(lex, x) {
if (lex.eqColor(last)) {
line += lex.sanitize()
}
else {
line += lex.ansi()
last = lex
}
})
return line
})
var txt = lines.filter(function(line){ return line.length > 0 }).join('\\e[0m\\n') + "\\e[0m"
return 'echo -e "' + txt + '"'
}
Matrix.prototype.mirc = function (opts) {
var cutoff = false
var lines = this.aa.map(function(row, y){
var last, line = ""
row.forEach(function(lex, x) {
if (lex.eqColor(last)) {
line += lex.sanitize()
}
else {
line += lex.mirc()
last = lex
}
})
if (opts && opts.cutoff && line.length > opts.cutoff) {
cutoff = true
return line.substr(0, opts.cutoff)
}
return line
})
var txt = lines.filter(function(line){ return line.length > 0 }).join('\n')
if (cutoff) {
txt = new String(txt)
txt.cutoff = true
}
return txt
}
Matrix.prototype.irssi = function(opts){
var mirc = this.mirc(opts)
var txt = mirc
// .replace(/\%/g, '%%')
.replace(/\\/g, '\\x5C')
.replace(/\"/g, '\\\"')
// .replace(/\'/g, '\\\'')
.replace(/\`/g, '\\\`')
.replace(/\$/g, '\\$')
// .replace(/\n\s+/g, '\n')
// .replace(/\s+$/g, '\n')
// .replace(/^\n+/, '')
.replace(/\n/g, '\\n')
.replace(/\x02/g, '\\x02')
.replace(/\x03/g, '\\x03')
txt = unicode.escapeToEscapedBytes(txt)
txt = '/exec -out printf "%b" "' + txt + '"\n'
if (mirc.cutoff){
txt = new String(txt)
txt.cutoff = true
}
return txt
}

226
js/png.js Normal file
View File

@ -0,0 +1,226 @@
var PNG = (function(){
var crc32 = function(u8){
var table = new Uint32Array([
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
])
var crc = 0 ^ (-1)
for(var i = 0; i < u8.length; i++){
crc = (crc >>> 8) ^ table[(crc ^ u8[i]) & 0xFF]
}
//return (crc ^ (-1)) // signed
return (crc ^ (-1)) >>> 0
}
var signature = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
var te, td
// decodes chunks in png
// see http://www.w3.org/TR/PNG/#5Chunk-layout
// returns something like
// [{length: Number,
// type: String[4],
// crc: Number,
// data: Uint8Array[] // optional
// }, ...]
var decode = function(buf, err){
var u8a = new Uint8Array(buf)
var dv = new DataView(buf)
td = td || new TextDecoder('utf-8')
err = err || function(msg){ throw new Error(msg) }
var out = []
var pos = 0
if (u8a.length < signature.length) return err("not a valid png")
for (var i=0; i<signature.length; i++){
if (signature[i] !== u8a[i]) return err("not a valid png")
pos += 1
}
var done = false
while (!done){
var chunk = {}
if (pos + 4 > u8a.length) return err("unexpected end of file")
chunk.length = dv.getInt32(pos, false)
pos += 4
if (pos + 4 > u8a.length) return err("unexpected end of file")
chunk.type = td.decode(new DataView(buf, pos, 4))
pos += 4
if (chunk.length){
if (pos + chunk.length > u8a.length) return err('unexpected end of file')
chunk.data = new Uint8Array(buf, pos, chunk.length)
pos += chunk.length
}
if (pos + 4 > u8a.length) return err("unexpected end of file")
//chunk.crc = new Uint8Array(buf, pos, 4)
chunk.crc = dv.getUint32(pos, false)
pos += 4
out.push(chunk)
//done = true
//console.log(pos.length, u8a.length)
if (pos === u8a.length) done = true
}
return out
}
var encode = function(chunks){
te = te || new TextEncoder('utf-8')
var size = 8 // inital png signature
for (var i=0, c; c=chunks[i]; i++){
size += 4 // length
size += 4 // type
size += c.length // data
size += 4 // crc32
}
var buf = new ArrayBuffer(size)
var u8 = new Uint8Array(buf)
var dv = new DataView(buf)
var pos = 0
u8.set(signature, 0)
pos += 8
for (var i=0, c; c=chunks[i]; i++){
dv.setInt32(pos, c.length, false) // length
pos += 4
var chunk_type_u8 = te.encode(c.type) // type
u8.set(chunk_type_u8, pos)
pos += 4
if (c.length){
u8.set(c.data, pos) // data
pos += c.length
}
//u8.set(c.crc, pos) // crc32
dv.setUint32(pos, c.crc, false) // crc32
pos += 4
}
return u8
}
var make_itxt_chunk = function(keyword, txt){
te = te || new TextEncoder('utf-8')
var keyword_u8 = te.encode(keyword)
var txt_u8 = te.encode(txt)
var header_u8 = new Uint8Array(keyword_u8.length + 5)
header_u8.set(keyword_u8, 0)
// header has keyword, then a null byte and some additional fields
// see http://www.w3.org/TR/PNG/#11iTXt
var chunk = {type: 'iTXt'}
chunk.length = header_u8.length + txt_u8.length
var u8 = new Uint8Array(4 + chunk.length)
// put type and data on the same u8 array so we can calculate crc
u8.set(te.encode(chunk.type), 0)
u8.set(header_u8, 4)
u8.set(txt_u8, header_u8.length + 4)
chunk.crc = crc32(u8)
chunk.data = new Uint8Array(u8.buffer, 4)
return chunk
}
var read_cstring = function(u8, pos){
var str = ""
while (pos < u8.length){
if (u8[pos] === 0) return str
str += String.fromCharCode(u8[pos])
pos++
}
return str
}
var decode_itxt_chunk = function(chunk){
td = td || new TextDecoder('utf-8')
var data = {}
var pos = 0
data.keyword = read_cstring(chunk.data, 0)
pos += data.keyword.length + 1
data.compression = chunk.data[pos]
pos += 1
data.compression_method = chunk.data[pos]
pos += 1
data.language = read_cstring(chunk.data, pos)
pos += data.language.length + 1
data.translated_keyword = read_cstring(chunk.data, pos)
pos += data.translated_keyword.length + 1
var data_u8 = chunk.data.subarray(pos)
data.data = td.decode(data_u8)
return data
}
var canvas_to_blob_with_colorcode = function(canvas, cc){
var u8 = dataUriToUint8Array(canvas.toDataURL())
var chunks = decode(u8.buffer)
var itxt_chunk = make_itxt_chunk('colorcode', cc)
// assume we wanna insert the chunk very last, just in front of the end
chunks.splice(chunks.length - 1, 0, itxt_chunk)
var blob = new Blob([encode(chunks)], {type: 'image/png'})
return blob
}
var exports = {}
exports.crc32 = crc32
exports.decode = decode
exports.encode = encode
exports.make_itxt_chunk = make_itxt_chunk
exports.decode_itxt_chunk = decode_itxt_chunk
exports.canvas_to_blob_with_colorcode = canvas_to_blob_with_colorcode
return exports
})()

58
js/shader.js Normal file
View File

@ -0,0 +1,58 @@
var shader = (function(){
var fn_str, fn, lex
var exports = {}
var animating = false
exports.init = function(){
lex = new Lex (0, 0)
exports.build(demo_shader.innerHTML)
}
exports.build = function (fn_str){
try {
new_fn = new Function('lex', 'x', 'y', 'w', 'h', 't', fn_str)
new_fn(lex, 0, 0, 1, 1, 0)
}
catch (e) {
throw 'Shader execution error'
}
exports.fn = fn = new_fn
return fn
}
exports.run = function(canvas){
var t = +new Date
shader.canvas = shader.canvas || canvas
var w = shader.canvas.w, h = shader.canvas.h
shader.canvas.forEach(function(lex, x, y){
fn(lex, x, y, w, h, t)
lex.build()
})
}
exports.toggle = function(state){
animating = typeof state == "boolean" ? state : ! animating
shader_fps_el.classList.toggle('hidden')
return animating
}
exports.pause = function(){
animating = false
shader_fps_el.classList.add('hidden')
shader.fps_time = 0
}
exports.play = function(){
animating = true
shader_fps_el.classList.remove('hidden')
}
exports.animate = function (t){
requestAnimationFrame(exports.animate)
if (! animating) { return }
if (shader.fps_time){
var ms = Date.now() - shader.fps_time
fps = 1000 / ms
shader_fps_el.innerHTML = (fps | 0) + ' fps'
}
shader.fps_time = Date.now()
exports.run(canvas)
}
return exports
})()

170
js/tool.js Normal file
View File

@ -0,0 +1,170 @@
var Tool = Model({
init: function (el) {
this.el = el
this.lex = new Lex (el)
this.name = el.innerHTML
},
bind: function(){
var tool = this
tool.el.addEventListener('mousedown', function(e){
tool.focus()
})
tool.el.addEventListener('contextmenu', function(e){
tool.context(e)
})
if (tool.memorable) {
// console.log(tool.name, localStorage.getItem("ascii.tools." + tool.name) )
tool.use( localStorage.getItem("ascii.tools." + tool.name) == "true" )
}
},
use: function(){},
context: function(e){},
done: function(){},
focus: function(){
// focused && focused.blur()
current_tool && current_tool.blur()
current_tool = this
this.el.classList.add('focused')
this.use()
if (this.name != 'shader' && is_desktop) { cursor_input.focus() }
},
blur: function(){
current_tool = null
this.el.classList.remove('focused')
this.done()
}
})
var FileTool = Tool.extend({
focus: function(){
if (current_filetool === this) {
this.blur()
return
}
current_filetool && current_filetool.blur()
current_filetool = this
this.el.classList.add('focused')
this.use()
if (this.name != 'shader' && is_desktop) { cursor_input.focus() }
},
blur: function(){
current_filetool = null
this.el.classList.remove('focused')
this.done()
}
})
var RadioItem = Tool.extend({
init: function(group, el){
this.group = group
this.el = el
},
focus: function(){
this.el.classList.add('focused')
},
blur: function(){
this.el.classList.remove('focused')
},
bind: function(){
var control = this
this.el.addEventListener('mousedown', function(){
control.group.use(control)
})
}
})
var RadioGroup = Tool.extend({
init: function(el){
this.el = el
this.controls = {}
var names = el.innerHTML.split(' ')
el.innerHTML = ''
var group = this
names.forEach(function(value){
var el = document.createElement('span')
el.classList.add('radio','tool')
var control = new RadioItem(group, el)
if (value.substr(0,1) === '*') {
control.value = value = value.substr(1)
group.use(control)
}
control.value = el.innerHTML = value
group.controls[value] = control
group.el.appendChild(el)
})
},
use: function(control){
if (typeof control === 'string') {
control = this.controls[control]
}
this.selected_control && this.selected_control.blur()
this.value = control.value
this.selected_control = control
control.focus()
control.use()
if (this.memorable){
localStorage.setItem("ascii.tools." + this.name, this.value)
}
},
bind: function(){
var tool = this
for (var n in this.controls){
this.controls[n].bind()
}
if (tool.memorable) {
var value = localStorage.getItem("ascii.tools." + tool.name)
if (value) tool.use(value)
}
}
})
var Checkbox = Tool.extend({
init: function (el){
this.__init(el)
var name = this.name.replace(/^[x_] /,"")
var state = localStorage.getItem("ascii.tools." + name) == "true" || this.name[0] == "x"
this.name = name
this.update(state)
},
update: function(state){
if (state) this.el.innerHTML = "x " + this.name
else this.el.innerHTML = "_ " + this.name
if (this.memorable) { localStorage.setItem("ascii.tools." + this.name, !! state) }
}
})
var BlurredCheckbox = Checkbox.extend({
focus: function(){
this.use()
},
blur: function(){
this.el.classList.remove('focused')
this.done()
}
})
var BlurredTool = Tool.extend({
focus: function(){
this.use()
},
blur: function(){
this.el.classList.remove('focused')
this.done()
}
})
var HiddenCheckbox = BlurredCheckbox.extend({
on: "o",
off: ".",
init: function (el){
this.el = el
this.lex = new Lex (el)
this.name = this.el.id
var state = localStorage.getItem("ascii.tools." + name) == "true" || this.el.innerHTML[0] == this.on
this.update(state)
},
update: function(state){
this.el.innerHTML = state ? this.on : this.off
if (this.memorable) { localStorage.setItem("ascii.tools." + this.name, !! state) }
}
})

108
js/ui/brush.js Normal file
View File

@ -0,0 +1,108 @@
var brush = (function(){
var brush = new Matrix (5, 5, function(x,y){
var lex = new Lex (x,y)
lex.build()
return lex
})
brush.modified = false
brush.mask = blit.circle
brush.generate = function(){
brush.fill(brush)
brush.mask(brush)
}
brush.bind = function(){
var last_point = [0,0]
var dragging = false
var erasing = false
brush.forEach(function(lex, x, y){
if (lex.bound) return
lex.bound = true
var point = [x,y]
lex.span.addEventListener('contextmenu', function(e){
e.preventDefault()
})
lex.span.addEventListener('mousedown', function(e){
e.preventDefault()
current_canvas = brush
brush.modified = true
dragging = true
erasing = (e.which == "3" || e.ctrlKey)
if (erasing) {
lex.clear()
}
else {
fillColor = brush.bg
lex.assign(brush)
}
brush.focus(x, y)
})
lex.span.addEventListener('mousemove', function(e){
e.preventDefault()
if (! dragging) {
return
}
erasing = (e.which == "3" || e.ctrlKey)
if (erasing) {
lex.clear()
}
else {
lex.assign(brush)
}
brush.focus(x, y)
})
})
window.addEventListener('mouseup', function(e){
dragging = erasing = false
})
}
brush.resize = function(w, h){
w = this.w = clamp(w, this.min, this.max)
h = this.h = clamp(h, this.min, this.max)
brush.rebuild()
controls.brush_w.char = "" + w
controls.brush_w.build()
controls.brush_h.char = "" + h
controls.brush_h.build()
}
brush.size_add = function(w, h){
brush.resize(brush.w + w, brush.h + h)
}
brush.expand = function(i){
brush.size_add(i, i)
}
brush.contract = function(i){
brush.size_add(-i, -i)
}
brush.load = function(lex){
brush.char = lex.char
brush.fg = lex.fg
brush.bg = lex.bg
brush.opacity = 1
}
brush.min = 1
brush.max = 100
brush.char = " "
brush.fg = 0
brush.bg = 1
brush.opacity = 1
brush.draw_fg = true
brush.draw_bg = true
brush.draw_char = true
return brush
})()

144
js/ui/canvas.js Normal file
View File

@ -0,0 +1,144 @@
var canvas = current_canvas = (function(){
var cols = 100
var rows = 30
var canvas = new Matrix (cols, rows, function(x,y){
var lex = new Lex (x,y)
lex.build()
return lex
})
canvas.bind = function(){
canvas.forEach(function(lex, x, y){
if (lex.bound) return
lex.bound = true
var point = [x,y]
lex.span.addEventListener('contextmenu', function(e){
e.preventDefault()
})
lex.span.addEventListener('mousedown', function(e){
if (is_mobile) return
e.preventDefault()
dragging = true
current_canvas = canvas
if (e.altKey) {
if (e.shiftKey) {
blit.copy_from(canvas, brush, floor(x-brush.w/2), floor(y-brush.h/2))
brush.mask(brush)
draw.set_last_point(e, point)
}
else {
brush.load(lex)
brush.generate()
dragging = false
}
return
}
else if (drawing) {
undo.new()
draw.down(e, lex, point)
}
else if (selecting) {
selection.down(e, lex, point)
}
else if (transforming) {
transform.down(e, lex, point)
}
else if (filling) {
undo.new()
draw.fill(brush, x, y)
}
canvas.focus(x, y)
})
lex.span.addEventListener("mousemove", function(e){
mouse.x = x
mouse.y = y
if (is_mobile) return
if (! dragging) return
if (drawing) {
draw.move(e, lex, point)
}
else if (selecting) {
selection.move(e, lex, point)
}
else if (transforming) {
transform.move(e, lex, point)
}
canvas.focus(x, y)
})
})
if (is_mobile) {
canvas.rapper.addEventListener('touchstart', function(e){
e.preventDefault()
var x, y, point, lex
x = (e.touches[0].pageX - canvas.rapper.offsetTop) / canvas.aa[0][0].span.offsetWidth
y = (e.touches[0].pageY - canvas.rapper.offsetTop) / canvas.aa[0][0].span.offsetHeight
x = ~~clamp(x, 0, canvas.aa[0].length-1)
y = ~~clamp(y, 0, canvas.aa.length-1)
point = [x,y]
lex = canvas.aa[y][x]
dragging = true
if (drawing) {
undo.new()
draw.down(e, lex, point)
}
else if (filling) {
undo.new()
draw.fill(brush, x, y)
}
canvas.focus(x, y)
})
canvas.rapper.addEventListener("touchmove", function(e){
e.preventDefault()
var x, y, point, lex
x = (e.touches[0].pageX - canvas.rapper.offsetTop) / canvas.aa[0][0].span.offsetWidth
y = (e.touches[0].pageY - canvas.rapper.offsetTop) / canvas.aa[0][0].span.offsetHeight
x = ~~clamp(x, 0, canvas.aa[0].length-1)
y = ~~clamp(y, 0, canvas.aa.length-1)
point = [x,y]
lex = canvas.aa[y][x]
if (! dragging) return
shader_el.innerHTML = point.join(",")
if (drawing) {
draw.move(e, lex, point)
}
canvas.focus(x, y)
})
}
}
canvas.min = 1
canvas.max = 999
// canvas.resize(1, 1, true) // wont create undo state
canvas.resize = function(w, h, no_undo){
var old_w = this.w, old_h = this.h
w = this.w = clamp(w, this.min, this.max)
h = this.h = clamp(h, this.min, this.max)
if (old_w === w && old_h === h) return;
if (!no_undo){
undo.new()
undo.save_resize(w, h, old_w, old_h)
}
canvas.__proto__.resize.call(canvas, w, h)
controls.canvas_w.char = "" + w
controls.canvas_w.build()
controls.canvas_h.char = "" + h
controls.canvas_h.build()
}
canvas.size_add = function(w, h){
canvas.resize(canvas.w + w, canvas.h + h)
}
return canvas
})()

374
js/ui/controls.js vendored Normal file
View File

@ -0,0 +1,374 @@
var controls = (function(){
var controls = {}
controls.cross = new Tool (cross_el)
controls.cross.use = function(){
if (brush.mask == blit.cross) {
controls.cross.el.innerHTML = "ssoɹɔ"
brush.mask = blit.inverted_cross
}
else {
controls.cross.el.innerHTML = "cross"
brush.mask = blit.cross
}
brush.generate()
drawing = true
brush.modified = false
}
controls.cross.done = function(){
controls.cross.el.innerHTML = "cross"
drawing = false
}
controls.circle = new Tool (circle_el)
controls.circle.use = function(){
brush.mask = blit.circle
brush.generate()
drawing = true
brush.modified = false
}
controls.circle.done = function(){
drawing = false
}
controls.square = new Tool (square_el)
controls.square.use = function(){
brush.mask = blit.square
brush.generate()
brush.modified = false
drawing = true
}
controls.square.done = function(){
drawing = false
}
controls.text = new Tool (text_el)
controls.text.use = function(){
current_filetool && current_filetool.blur()
}
controls.select = new Tool (select_el)
controls.select.use = function(){
selection.show()
}
controls.select.done = function(){
selection.hide()
}
controls.rotate = new Tool (rotate_el)
controls.rotate.use = function(){
transform.set_mode('rotate')
}
controls.rotate.done = function(){
transform.done()
}
controls.scale = new Tool (scale_el)
controls.scale.use = function(){
transform.set_mode('scale')
}
controls.scale.done = function(){
transform.done()
}
controls.slice = new Tool (slice_el)
controls.slice.use = function(){
transform.set_mode('slice')
}
controls.slice.done = function(){
transform.done()
}
controls.translate = new Tool (translate_el)
controls.translate.use = function(){
transform.set_mode('translate')
}
controls.translate.done = function(){
transform.done()
}
controls.fill = new Tool (fill_el)
controls.fill.use = function(){
filling = true
document.body.classList.add("bucket")
}
controls.fill.done = function(){
filling = false
document.body.classList.remove("bucket")
}
controls.undo = new BlurredTool (undo_el)
controls.undo.use = function(){
undo.undo()
}
controls.redo = new BlurredTool (redo_el)
controls.redo.use = function(){
undo.redo()
}
controls.clear = new BlurredTool (clear_el)
controls.clear.use = function(){
undo.new()
undo.save_rect(0, 0, canvas.w, canvas.h)
canvas.erase()
current_filetool && current_filetool.blur()
}
controls.webcam = new FileTool (webcam_el)
controls.webcam.load = function(){
this.loaded = true
webcam_close.addEventListener("click", function(){ controls.webcam.blur() })
window.addEventListener("message", function(e){
if (e.origin !== window.location.origin) return
controls.webcam.blur()
controls.circle.focus()
import_textarea.value = e.data
clipboard.import_colorcode()
})
}
controls.webcam.use = function(){
if (! this.loaded) {
this.load()
}
webcam_iframe.src = "webcam.html"
webcam_rapper.style.display = "block"
}
controls.webcam.done = function(){
webcam_iframe.src = ""
webcam_rapper.style.display = "none"
}
controls.grid = new BlurredCheckbox (grid_el)
controls.grid.memorable = true
controls.grid.use = function(state){
state = typeof state == "boolean" ? state : ! document.body.classList.contains("grid")
document.body.classList[ state ? "add" : "remove" ]('grid')
letters.grid = palette.grid = canvas.grid = state
canvas.resize_rapper()
palette.resize_rapper()
letters.resize_rapper()
if (! selection.hidden) selection.reposition()
this.update( state )
}
ClipboardTool = FileTool.extend({
blur: function(){
this.__blur()
clipboard.hide()
}
})
controls.save = new ClipboardTool (save_el)
controls.save.use = function(){
changed && clipboard.upload_png()
clipboard.show()
clipboard.export_mode()
}
controls.send_to_irc = new ClipboardTool (send_to_irc_el)
controls.send_to_irc.use = function(){
changed && clipboard.upload_png()
clipboard.show()
clipboard.export_mode()
alert('your ascii art is now on display on the IRC channel inside the panke.gallery!')
}
controls.load = new ClipboardTool (load_el)
controls.load.use = function(){
// console.log("use")
clipboard.show()
clipboard.import_mode()
}
controls.save_format = new RadioGroup(format_el)
controls.save_format.name = 'save_format'
controls.save_format.memorable = true
var cs = controls.save_format.controls
cs.mirc.use = cs.irssi.use = cs.ascii.use = function(){
clipboard.export_data()
}
//
var ShaderTool = FileTool.extend({
active: false,
use: function(state){
this.active = typeof state == "boolean" ? state : ! this.active
if (this.active) {
shader_rapper.style.display = "block"
shader_textarea.focus()
} else {
shader_rapper.style.display = "none"
}
},
done: function(){
this.use(false)
}
})
controls.shader = new ShaderTool (shader_el)
shader_textarea.value = shader_textarea.value || demo_shader.innerHTML
shader_textarea.addEventListener("input", function(){
var fn = shader.build(shader_textarea.value)
fn && shader.run(canvas)
})
controls.animate = new BlurredCheckbox (animate_checkbox)
controls.animate.use = function(state){
var state = shader.toggle()
this.update(state)
// controls.shader.focus()
controls.shader.use(true)
}
controls.shader_target = new RadioGroup(shader_target_el)
var cs = controls.shader_target.controls
cs.canvas.use = function(){ shader.canvas = canvas }
cs.brush.use = function(){ shader.canvas = brush }
cs.selection.use = function(){ shader.canvas = selection.canvas }
controls.experimental_palette = new HiddenCheckbox (experimental_palette_toggle)
controls.experimental_palette.memorable = true
controls.experimental_palette.use = function(state){
var state = palette.experimental(state)
this.update(state)
}
controls.advanced = new BlurredCheckbox (advanced_checkbox)
controls.advanced.memorable = true
controls.advanced.use = function(state){
console.log(state)
state = typeof state == "boolean" ? state : ! document.body.classList.contains('panke')
if (state)
document.body.classList.add('panke')
else
document.body.classList.remove('panke')
this.update(state)
}
/*
controls.nopaint = new HiddenCheckbox (nopaint_toggle)
controls.nopaint.memorable = true
controls.nopaint.on = "N"
controls.nopaint.use = function(state){
var state = nopaint.toggle(state)
this.update(state)
}
*/
//
controls.fg = new BlurredCheckbox (fg_checkbox)
controls.fg.use = function(state){
brush.draw_fg = state || ! brush.draw_fg
this.update(brush.draw_fg)
}
controls.bg = new BlurredCheckbox (bg_checkbox)
controls.bg.use = function(state){
brush.draw_bg = state || ! brush.draw_bg
this.update(brush.draw_bg)
}
controls.char = new BlurredCheckbox (char_checkbox)
controls.char.use = function(state){
brush.draw_char = state || ! brush.draw_char
this.update(brush.draw_char)
}
//
// controls.turn = new BlurredCheckbox (turn_checkbox)
// controls.turn.memorable = true
// controls.turn.use = function(state){
// canvas.rotated = typeof state == "boolean" ? state : ! canvas.rotated
// canvas.resize_rapper()
// this.update(canvas.rotated)
// }
// controls.pixels = new BlurredCheckbox (pixels_checkbox)
// controls.pixels.memorable = true
// controls.pixels.use = function(state){
// canvas.pixels = typeof state == "boolean" ? state : ! canvas.pixels
// document.body.classList.toggle("pixels", canvas.pixels)
// this.update(canvas.pixels)
// }
controls.mirror_x = new BlurredCheckbox (mirror_x_checkbox)
controls.mirror_x.use = function(state){
window.mirror_x = typeof state == "boolean" ? state : ! window.mirror_x
this.update(window.mirror_x)
}
controls.mirror_y = new BlurredCheckbox (mirror_y_checkbox)
controls.mirror_y.use = function(state){
window.mirror_y = typeof state == "boolean" ? state : ! window.mirror_y
this.update(window.mirror_y)
}
//
controls.vertical = new BlurredCheckbox (vertical_checkbox)
controls.vertical.memorable = true
controls.vertical.use = function(state){
canvas.vertical = typeof state == "boolean" ? state : ! canvas.vertical
controls.vertical.refresh()
}
controls.vertical.refresh = function(){
if (canvas.vertical) {
document.body.classList.add("vertical")
}
else {
document.body.classList.remove("vertical")
}
palette.repaint()
letters.repaint()
this.update(canvas.vertical)
}
//
controls.brush_w = new Lex (brush_w_el)
controls.brush_h = new Lex (brush_h_el)
controls.canvas_w = new Lex (canvas_w_el)
controls.canvas_h = new Lex (canvas_h_el)
// bind
controls.bind = function(){
for (var n in controls){
var control = controls[n]
if (typeof control === 'object' && 'bind' in control){
control.bind()
}
}
[
controls.brush_w,
controls.brush_h,
controls.canvas_w,
controls.canvas_h
].forEach(function(lex){
lex.span.addEventListener('mousedown', function(e){
lex.focus()
if (is_mobile) cursor_input.focus()
})
});
controls.brush_w.key = keys.single_numeral_key(controls.brush_w, function(w){ brush.resize(w, brush.h) })
controls.brush_w.raw_key = keys.arrow_key(function(w){ brush.size_add(w, 0) })
controls.brush_h.key = keys.single_numeral_key(controls.brush_h, function(h){ brush.resize(brush.w, h) })
controls.brush_h.raw_key = keys.arrow_key(function(h){ brush.size_add(0, h) })
controls.canvas_w.key = keys.multi_numeral_key(controls.canvas_w, 3)
controls.canvas_w.onBlur = keys.multi_numeral_blur(controls.canvas_w, function(w){ canvas.resize(w, canvas.h) })
controls.canvas_w.raw_key = keys.arrow_key(function(w){ canvas.size_add(w, 0) })
controls.canvas_h.key = keys.multi_numeral_key(controls.canvas_h, 3)
controls.canvas_h.onBlur = keys.multi_numeral_blur(controls.canvas_h, function(h){ canvas.resize(canvas.w, h) })
controls.canvas_h.raw_key = keys.arrow_key(function(h){ canvas.size_add(0, h) })
add_custom_el.addEventListener("click", function(){
custom.clone()
})
}
return controls
})()

24
js/ui/custom.js Normal file
View File

@ -0,0 +1,24 @@
var custom = (function(){
var exports = {}
exports.clone = function (){
var new_brush = brush.clone()
var rapper = document.createElement("div")
rapper.className = "custom"
new_brush.append(rapper)
custom_rapper.appendChild(rapper)
// store in localstorage?
rapper.addEventListener("click", function(){
// load this brush
exports.load(new_brush)
})
}
exports.load = function(new_brush){
brush.assign( new_brush )
}
return exports
})()

132
js/ui/goodies.js Normal file
View File

@ -0,0 +1,132 @@
var goodies = (function(){
var goodies = {}
goodies.build = () => {
Object.keys(goodies.list).map(() => {
goodies_rapper.appendChild(tool_canvas.el)
})
}
goodies.list = {}
goodies.list.choppy = {
mode: 'brush',
fn: `var char = choice(" abcdef ")
lex.bg = +choice("0124")
lex.fg = +choice("01234")
lex.char = char
lex.opacity = char == " " ? 0 : 1`,
}
goodies.list.foggy = {
mode: 'brush',
fn: `var char = choice(" abcdef ")
lex.bg = choice([14,15])
lex.fg = choice("367")
lex.char = char
lex.opacity = char == " " ? 0 : 1`,
}
// goodies.list.name = {
// fn: ``,
// }
// goodies.list.name = {
// fn: ``,
// }
// goodies.list.name = {
// fn: ``,
// }
// goodies.list.name = {
// fn: ``,
// }
// >> mirror brush (up-down)
// NOTE: Animate this on the canvas, then draw:
// if (x > h/2) {
// lex.assign( canvas.aa[h-y][x] )
// }
// >> rainbow stardust brush
// Uncheck BG and animate this to brush:
// lex.fg = hue(t)
// lex.char = choice(" ,'.,.','****** ")
// >> noise brushes, works on a black background:
// lex.bg = max(5, yellow(randint(t)))
// lex.opacity = lex.bg == colors.black ? 0 : 1
// >> simple rainbow:
// if (lex.bg != 1) lex.bg = randint(t)
// lex.opacity = lex.bg == colors.black ? 0 : 1
// >> self-erasing:
// if (lex.bg != 1) lex.bg = yellow(randint(t))
// lex.opacity = lex.bg == colors.black ? 0 : 1
// >> cycling rainbow brush
// if (lex.bg != 1) lex.bg = hue( all_color_hue_order.indexOf( color_names[ lex.bg ] ) + 1 )
// lex.opacity = lex.bg == colors.black ? 0 : 1
// >> "stars" brush.. set your brush to paint just the character "#"
// if (lex.char == "#") {
// lex.fg = hue(randint(15))
// lex.char = random() > 0.1 ? " " : "+@*.,\"+'*-"[randint(10)]
// }
// >> use fg char to mask mask what you're drawing on the bg
// if (lex.char != "/") { lex.bg = 1 }
// >> sharded glitch brush
// Example: http://asdf.us/z/kksnvs.png
// Use on a brush:
// lex.bg = t/y/x
// lex.opacity = lex.bg % 1 ? 0 : 1
// >> incremental brush
// Set your brush to be the ^ character, square, about 10x10
// Draw "char" only
// Then animate this shader on the canvas:
// if (lex.char=="^") {
// lex.bg += 1
// lex.char = " "
// }
// lex.bg += 1
return goodies
})

240
js/ui/keys.js Normal file
View File

@ -0,0 +1,240 @@
var keys = (function(){
var keys = {}
keys.bind = function(){
cursor_input.addEventListener('keydown', function(e){
// console.log("keycode:", e.keyCode)
if (e.altKey) {
document.body.classList.add("dropper")
}
switch (e.keyCode) {
case 27: // esc
if (!selection.hidden && current_canvas === canvas){
selection.hide()
selection.show()
} else if (focused){
focused.blur()
}
return
}
if (window.focused && focused.raw_key) {
focused.raw_key(e)
return
}
switch (e.keyCode) {
case 219: // [
if (current_tool.name != "text") {
e.preventDefault()
brush.contract(1)
brush.modified = false
check_if_lost_focus()
}
break
case 221: // ]
if (current_tool.name != "text") {
e.preventDefault()
brush.expand(1)
brush.modified = false
}
break
case 8: // backspace
e.preventDefault()
if (current_canvas === canvas)
undo.new()
current_canvas.focus_add(-1, 0)
if (current_canvas === canvas)
undo.save_focused_lex()
focused.char = " "
focused.build()
return
case 13: // return
e.preventDefault()
current_canvas.focusLex(focused.y, focused.x+1)
return
case 38: // up
e.preventDefault()
current_canvas.focus_add(0, -1)
break
case 40: // down
e.preventDefault()
current_canvas.focus_add(0, 1)
break
case 37: // left
e.preventDefault()
current_canvas.focus_add(-1, 0)
break
case 39: // right
e.preventDefault()
current_canvas.focus_add(1, 0)
break
// use typical windows and os x shortcuts
// undo: ctrl-z or cmd-z
// redo: ctrl-y or shift-cmd-z
case 89: // y
if (!e.ctrlKey && !e.metaKey) break;
e.preventDefault();
undo.redo();
break
case 90: // z
if (!e.ctrlKey && !e.metaKey) break;
e.preventDefault();
if (e.shiftKey)
undo.redo();
else
undo.undo();
break
// default:
// if (focused) { focused.key(undefined, e.keyCode) }
}
})
cursor_input.addEventListener('input', function(e){
/*
if (! e.metaKey && ! e.ctrlKey && ! e.altKey) {
e.preventDefault()
}
*/
if (current_tool.name == "shader") {
cursor_input.value = ""
return
}
var char = cursor_input.value
cursor_input.value = ""
// console.log("input:", char)
if (current_tool.name != "text" && ! brush.modified) {
brush.char = char
if (char == " ") {
brush.bg = brush.fg
}
else if (brush.bg != fillColor) {
brush.fg = brush.bg
brush.bg = fillColor
}
brush.rebuild()
}
if (focused && char) {
var y = focused.y, x = focused.x
if (current_canvas === canvas){
undo.new()
undo.save_focused_lex()
}
var moving = focused.key(char, e.keyCode)
if ( ! moving || ! ('y' in focused && 'x' in focused) ) { return }
current_canvas.focus_add(1, 0)
}
})
cursor_input.addEventListener("keyup", function(e){
if (! e.altKey) {
document.body.classList.remove("dropper")
}
})
}
keys.int_key = function (f) {
return function (key, keyCode) {
var n = parseInt(key)
! isNaN(n) && f(n)
}
}
keys.arrow_key = function (fn) {
return function (e){
switch (e.keyCode) {
case 38: // up
e.preventDefault()
fn(1)
break
case 40: // down
e.preventDefault()
fn(-1)
break
}
}
}
keys.left_right_key = function (fn) {
return function (e){
switch (e.keyCode) {
case 39: // right
e.preventDefault()
fn(1)
break
case 38: // up
case 40: // down
e.preventDefault()
fn(0)
break
case 37: // left
e.preventDefault()
fn(-1)
break
}
}
}
keys.single_numeral_key = function (lex, fn) {
return keys.int_key(function(n, keyCode){
if (n == 0) n = 10
lex.blur()
fn(n)
})
}
keys.multi_numeral_key = function (lex, digits){
return keys.int_key(function(n, keyCode){
lex.read()
if (lex.char.length < digits) {
n = parseInt(lex.char) * 10 + n
}
lex.char = ""+n
lex.build()
})
}
keys.multi_numeral_blur = function (lex, fn){
return function(){
var n = parseInt(lex.char)
if (isNaN(n)) return
fn(n)
}
}
// function cancelZoom() {
// var d = document,
// viewport,
// content,
// maxScale = ',maximum-scale=',
// maxScaleRegex = /,*maximum\-scale\=\d*\.*\d*/;
// // this should be a focusable DOM Element
// if (!this.addEventListener || !d.querySelector) {
// return;
// }
// viewport = d.querySelector('meta[name="viewport"]');
// content = viewport.content;
// function changeViewport(event) {
// // http://nerd.vasilis.nl/prevent-ios-from-zooming-onfocus/
// viewport.content = content + (event.type == 'blur' ? (content.match(maxScaleRegex, '') ? '' : maxScale + 10) : maxScale + 1);
// }
// // We could use DOMFocusIn here, but it's deprecated.
// this.addEventListener('focus', changeViewport, true);
// this.addEventListener('blur', changeViewport, false);
// }
// cancelZoom.bind(cursor_input)();
return keys
})()
function check_if_lost_focus() {
if (! window.focused || ! window.focused.span)
window.focused = canvas.aa[0][0]
}

89
js/ui/letters.js Normal file
View File

@ -0,0 +1,89 @@
var letters = (function(){
var last_charset = ""
var charset_index = 0
var charsets = [
'Basic Latin',
'Latin-1 Supplement',
'Box Drawing',
'Block Elements',
]
var letters = new Matrix (1, 1, function(x,y){
var lex = new Lex (x,y)
return lex
})
letters.charset = ""
letters.repaint = function(charset){
letters.charset = charset = charset || last_charset
last_charset = charset
var chars = unicode.block(charset, 32)
if (chars[0] != " ") chars.unshift(" ")
if (canvas.vertical) {
letters.resize( Math.ceil( chars.length / 16 ), 16 )
}
else {
letters.resize( 32, Math.ceil( chars.length / 32 ) )
}
var i = 0
letters.forEach(function(lex,x,y){
if (canvas.vertical) { x=x^y;y=x^y;x=x^y }
var char = chars[i++]
if (palette.chars.indexOf(brush.char) > 1) {
lex.bg = brush.fg
lex.fg = brush.bg
}
else {
lex.bg = colors.black
lex.fg = brush.fg == fillColor ? colors.black : brush.fg
}
lex.char = char
lex.opacity = 1
lex.build()
})
}
letters.bind = function(){
letters.forEach(function(lex,x,y){
if (lex.bound) return
lex.bound = true
lex.span.addEventListener('mousedown', function(e){
e.preventDefault()
if (e.shiftKey) {
charset_index = (charset_index+1) % charsets.length
letters.repaint(charsets[charset_index])
return
}
else if (e.ctrlKey || e.which == 3) {
brush.char = lex.char
brush.bg = brush.fg
brush.fg = fillColor
}
else {
brush.char = lex.char
if (lex.char == " ") {
brush.bg = brush.fg
}
else if (brush.bg != fillColor) {
brush.fg = brush.bg
brush.bg = fillColor
}
}
if (! brush.modified) {
brush.generate()
}
palette.repaint()
})
lex.span.addEventListener('contextmenu', function(e){
e.preventDefault()
})
})
}
return letters
})()

896
js/ui/nopaint.js Normal file
View File

@ -0,0 +1,896 @@
var nopaint = (function(){
controls.no = new Tool (nopaint_no_el)
controls.no.use = function(state){
undo.undo()
controls.paint.focus()
}
controls.no.context = function(e){
e.preventDefault()
nopaint.turbo()
}
controls.paint = new Tool (nopaint_paint_el)
controls.paint.use = function(state){
nopaint.paint()
nopaint_pause_el.classList.toggle("hidden", false)
focused = controls.paint.lex
}
controls.paint.context = function(e){
e.preventDefault()
nopaint.autoplay()
}
controls.nopaint_pause = new Tool (nopaint_pause_el)
controls.nopaint_pause.use = function(state){
// nopaint.pause()
nopaint.autoplay(false)
nopaint_pause_el.classList.toggle("hidden", true)
focused = canvas.aa[0][0]
}
// use own stepwise clock to drive tweens
oktween.raf = function(){}
var nopaint = {}
nopaint.debug = true
nopaint.delay = nopaint.normal_delay = 100
nopaint.turbo_delay = 0
nopaint.tool = null
nopaint.tools = {}
nopaint.keys = []
nopaint.weights = []
nopaint.step = 0
nopaint.time = 0
nopaint.timeout = false
nopaint.toggle = function(state){
var state = typeof state == "boolean" ? state : nopaint_rapper.classList.contains("hidden")
nopaint_rapper.classList.toggle("hidden", ! state)
nopaint_pause_el.classList.toggle("hidden", true)
document.body.classList.toggle("nopaint", state)
return state
}
nopaint.no = function(){
undo.undo()
nopaint.paint()
}
nopaint.raw_key = controls.paint.lex.raw_key = keys.left_right_key(function(n){
if (! nopaint.timeout) return
if (n < 0) nopaint.no()
else if (n > 0) nopaint.paint()
else nopaint.pause()
})
nopaint.pause = nopaint.blur = function(){
clearTimeout(nopaint.timeout)
nopaint.timeout = 0
nopaint.step = 0
}
nopaint.paint = function(){
var state = undo.new()
delete state.focus
nopaint.pause()
nopaint.switch_tool()
nopaint.go()
}
nopaint.go = function(){
nopaint.timeout = setTimeout(nopaint.go, nopaint.delay)
oktween.update(nopaint.time)
nopaint.tool.paint( nopaint.step )
nopaint.time += 1
nopaint.step += 1
}
nopaint.switch_tool = function(){
last_tool = nopaint.tool
last_tool && last_tool.finish()
nopaint.tool = nopaint.get_random_tool( last_tool )
nopaint.tool.start( last_tool )
nopaint.debug && console.log("> %s", nopaint.tool.type)
}
nopaint.add_tool = function(fn){
nopaint.tools[fn.type] = fn
}
nopaint.disable_all_tools = function(){
Object.keys(nopaint.tools).forEach(function(key){
nopaint.tools[key].disabled = true
})
}
nopaint.enable_tools = function(keys){
keys.forEach(function(key){
if (nopaint.tools[key]) nopaint.tools[key].disabled = false
})
}
nopaint.get_random_tool = function( last_tool ){
var n = rand( nopaint.sum )
for (var i = 0, _len = nopaint.weights.length; i < _len; i++) {
if (n < nopaint.weights[i] && (! last_tool || nopaint.keys[i] !== last_tool.key)) {
return nopaint.tools[ nopaint.keys[i] ]
}
}
return nopaint.tools[ choice(nopaint.keys) ]
}
nopaint.regenerate_weights = function(){
nopaint.sum = 0
nopaint.weights = []
nopaint.keys = Object.keys( nopaint.tools ).sort(function(a,b){
return nopaint.tools[b].opt.weight-nopaint.tools[a].opt.weight
}).filter(function(key){
return ! nopaint.tools[key].disabled
})
nopaint.keys.forEach(function(key){
nopaint.sum += nopaint.tools[key].opt.weight
nopaint.weights.push( nopaint.sum )
})
}
nopaint.is_turbo = false
nopaint.turbo = function(state){
nopaint.is_turbo = typeof state == "boolean" ? state : ! nopaint.is_turbo
nopaint.delay = nopaint.is_turbo ? nopaint.turbo_delay : nopaint.normal_delay
if (nopaint.is_turbo) {
nopaint_no_el.classList.add("locked")
}
else {
nopaint_no_el.classList.remove("locked")
}
}
nopaint.is_autoplay = false
nopaint.autoplay = function(state){
nopaint.is_autoplay = typeof state == "boolean" ? state : ! nopaint.is_autoplay
if (nopaint.is_autoplay) {
nopaint_paint_el.classList.add("locked")
if (! nopaint.player) {
nopaint.player = new RandomPlayer ()
}
if (! nopaint.timeout) nopaint.paint()
nopaint.player.play()
}
else {
nopaint_paint_el.classList.remove("locked")
nopaint.pause()
nopaint.player && nopaint.player.pause()
}
}
var NopaintPlayer = Model({
type: "player",
upload_png: false,
upload_interval: 100,
step: 0,
timeout: null,
delay: function(){
return nopaint.is_turbo ? randrange(150, 300) : randrange(400, 800)
},
reset: function(){
this.no_count = 0
this.paint_count = 0
},
pause: function(){
clearTimeout(this.timeout)
this.timeout = 0
},
play: function(){
clearTimeout(this.timeout)
var delay = this.delay()
this.timeout = setTimeout(this.play.bind(this), delay)
this.check_fitness()
this.step += 1
},
check_fitness: function(){
switch (this.fitness()) {
case "no":
nopaint.no_count += 1
nopaint.since_last_no = 0
nopaint.since_last_paint += 1
nopaint.no()
break
case "paint":
nopaint.paint_count += 1
nopaint.since_last_no += 1
nopaint.since_last_paint = 0
nopaint.paint()
break
case "screenshot":
if (this.save_as_png) break
console.log("uploading...")
setTimeout(clipboard.upload_png, 0)
// fall thru
default:
nopaint.since_last_no += 1
nopaint.since_last_paint += 1
break
}
},
fitness: function(){},
})
var RandomPlayer = NopaintPlayer.extend({
type: "random_player",
upload_png: false,
fitness: function(){
var no_prob = random()
var paint_prob = 1 - no_prob
if (paint_prob < 0.3) {
return "paint"
}
else if (no_prob < 0.5) {
return "no"
}
else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
return "screenshot"
}
}
})
var StylePlayer = NopaintPlayer.extend({
type: "style_player",
upload_png: false,
fitness: function(){
var no_prob = random()
var paint_prob = 1 - no_prob
var np, pp
var steps = this.since_last_paint
if (nopaint.tool.is_brush) {
if (nopaint.tool.is_clone) {
if (steps < randrange(3,8)) return
np = 0.3
pp = 0.4
}
else if (nopaint.tool.is_erase) {
if (steps < randrange(2,6)) return
np = 0.3
pp = 0.4
}
else {
if (steps < randrange(2,4)) return
np = 0.1
pp = 0.3
}
}
if (nopaint.tool.is_shader) {
switch (nopaint.tool.name) {
case "rotate":
case "scale":
if (steps < randrange(2,4)) return
np = 0.1
pp = 0.2
break
default:
np = 0.2
pp = 0.2
}
}
if (nopaint.tool.is_fill) {
np = 0.4
pp = 0.1
}
if (steps > 10) {
np *= 0.7
pp *= 1.5
if (nopaint.is_turbo) {
np *= 1.2
pp *= 1.2
}
}
if (paint_prob < np) {
return "paint"
}
else if (no_prob < np) {
return "no"
}
else if ( this.paint_count > 100 && (this.step % 100) == 99 ) {
return "screenshot"
}
}
})
/* Base models for brushes */
var NopaintTool = Model({
type: "none",
init: function(opt){
this.opt = opt || {}
},
start: function(){},
paint: function(t){},
update: function(t){},
finish: function(){},
})
var NopaintBrush = NopaintTool.extend({
type: "brush",
is_brush: true,
init: function(opt){
this.opt = opt || {}
this.opt.max_radius = this.opt.max_radius || 10
this.p = {x: randint(canvas.w), y: randint(canvas.h)}
this.fg = 0
this.bg = 1
this.char = " "
this.tweens = []
},
start: function(last_brush){
this.set_brush_mask()
this.toggle_channels()
this.reset( last_brush )
this.regenerate()
draw.down({}, null, [this.p.x, this.p.y])
},
paint: function(t){
this.update(t)
draw.move_toroidal({}, null, [this.p.x, this.p.y])
},
finish: function(){
this.tweens.forEach(function(t){ t.cancel() })
this.tweens = []
},
reorient: function(last_brush){
var a = {}, b
if (last_brush) {
this.p.x = a.x = randint(canvas.w)
this.p.y = a.y = randint(canvas.h)
}
else {
a.x = this.p.x
a.y = this.p.y
}
b = this.get_next_point()
var tween = oktween.add({
obj: this.p,
from: a,
to: b,
duration: b.duration,
easing: b.easing,
update: b.update,
finished: function(){
this.iterate()
this.regenerate()
}.bind(this)
})
this.tweens.push(tween)
},
get_next_point: function(){
var radius = randrange(2, this.opt.max_radius)
var b = {}
b.duration = randrange(1, 7)
b.easing = choice(easings)
b.x = this.p.x + randrange(-radius, radius)
b.y = this.p.y + randrange(-radius, radius)
return b
},
set_brush_mask: function(){
var r = Math.random()
if (r < 0.2) {
brush.mask = blit.square
}
else if (r < 0.6) {
brush.mask = blit.circle
}
else if (r < 0.9) {
brush.mask = blit.cross
}
else{
brush.mask = blit.inverted_cross
}
},
toggle_channels: function(){
if (Math.random() < 0.001) { controls.bg.use(false) }
else if (! brush.draw_bg && Math.random() < 0.25) { controls.bg.use(true) }
if (Math.random() < 0.1) { controls.fg.use(false) }
else if (! brush.draw_fg && Math.random() < 0.5) { controls.fg.use(true) }
if (Math.random() < 0.02) { controls.char.use(false) }
else if (! brush.draw_char && Math.random() < 0.2) { controls.char.use(true) }
},
iterate: function( last_brush ){
this.reorient( last_brush )
},
regenerate: function(){
brush.load( this )
brush.generate()
}
})
var easings = "linear circ_out circ_in circ_in_out quad_in quad_out quad_in_out".split(" ")
/* Standard brushes */
var SolidBrush = NopaintBrush.extend({
type: "solid",
recolor: function(){
this.fg = this.bg = randint(16)
this.char = " "
},
resize: function(m,n){
m = m || 3
n = n || 0
var bw = xrandrange(5, 0, m) + n
brush.resize( round(bw * randrange(0.9, 1.8)) || 1, round(bw) || 1 )
},
reset: function( last_brush ){
this.opt.max_radius = randrange(5,20)
this.resize()
this.reorient( last_brush )
this.recolor( last_brush )
this.regenerate()
},
iterate: function( last_brush ){
this.resize()
this.reorient( last_brush )
},
})
var EraseBrush = SolidBrush.extend({
type: "erase",
reset: function( last_brush ){
this.opt.max_radius = randrange(8, 20)
this.reorient( last_brush )
this.bg = random() < 0.2 ? colors.white : colors.black
this.char = " "
brush.load( this )
this.resize(3,2)
},
})
var ShadowBrush = NopaintBrush.extend({
type: "shadow",
pairs: [
[ colors.yellow, colors.orange ],
[ colors.orange, colors.darkred ],
[ colors.red, colors.darkred ],
[ colors.lime, colors.green ],
[ colors.cyan, colors.teal ],
[ colors.cyan, colors.blue ],
[ colors.blue, colors.darkblue ],
[ colors.magenta, colors.purple ],
[ colors.lightgray, colors.darkgray ],
[ colors.darkgray, colors.black ],
[ colors.white, colors.lightgray ],
[ colors.white, colors.black ],
],
shapes: [
[[0],[1]],
[[0,0],[1,1]],
[[1,0,0],[1,1,1]],
[[0,0,1],[1,1,1]],
[[0,0,0],[1,1,1]],
[[0,0,0,0],[1,1,1,1]],
[[1,0,0,0],[null,1,1,1]],
[[0,0,0,1],[1,1,1,null]],
[[0,0],[1,0],[1,1]],
[[0,0],[0,1],[1,1]],
],
reset: function( last_brush ){
var pair = choice(this.pairs)
var shape = choice(this.shapes)
this.reorient( last_brush )
brush.char = " "
brush.resize(shape[0].length, shape.length)
brush.generate()
brush.rebuild()
brush.forEach(function(lex,x,y){
if (shape[y][x] == null) {
lex.opacity = 0
}
else {
lex.fg = lex.bg = pair[ shape[y][x] ]
lex.opacity = 1
}
lex.build()
})
},
regenerate: function(){},
})
var RandomBrush = SolidBrush.extend({
type: "random",
iterate: function( last_brush ){
this.reorient( last_brush )
this.recolor( last_brush )
},
})
var HueBrush = SolidBrush.extend({
type: "hue",
recolor: function(){
this.fg = this.bg = rand_hue()
this.char = " "
},
})
var GrayBrush = SolidBrush.extend({
type: "gray",
recolor: function(){
this.fg = this.bg = rand_gray()
this.char = " "
},
})
var LetterBrush = SolidBrush.extend({
type: "letter",
recolor: function(){
this.fg = rand_hue()
this.bg = rand_hue()
this.char = choice( unicode.block(letters.charset, 32) )
},
})
var RandomLetterBrush = LetterBrush.extend({
type: "random-letter",
iterate: function(){
if (Math.random() < 0.01) {
this.fg += 1
}
if (Math.random() < 0.05) {
var n = this.fg
this.fg = this.bg
this.bg = n
}
if (Math.random() < 0.7) {
this.char = choice( unicode.block(letters.charset, 32) )
}
this.regenerate()
this.__iterate()
},
update: function(){
if (Math.random() < 0.3) {
this.char = choice( unicode.block(letters.charset, 32) )
}
this.regenerate()
},
})
var CloneBrush = SolidBrush.extend({
type: "clone",
is_clone: true,
reset: function( last_brush ){
this.opt.max_radius = randrange(5, 20)
this.reorient( last_brush )
this.resize(4,2)
this.clone_random_region()
},
clone_random_region: function(x, y){
var x = randrange(0, canvas.w - brush.w)
var y = randrange(0, canvas.h - brush.h)
this.clone_region(x, y)
},
clone_region: function(x, y){
blit.copy_toroidal_from(canvas, brush, round(x-brush.w/2), round(y-brush.h/2))
brush.mask(brush)
},
iterate: function( last_brush ){
this.reorient( last_brush )
},
regenerate: function(){},
})
var SmearBrush = CloneBrush.extend({
type: "smear",
update: function(){
var r = random()
var jitter_x = randnullsign() * xrand(2, 2)
var jitter_y = randnullsign() * xrand(2, 2)
this.clone_region( this.p.x + jitter_x, this.p.y + jitter_y )
},
iterate: function( last_brush ){
this.resize(4, 2)
this.update()
this.reorient( last_brush )
}
})
var StarsTool = NopaintBrush.extend({
type: "stars",
chars: "....,,'''*",
start: function(last_brush){
this.reorient( last_brush )
},
paint: function(t){
if (Math.random() < 0.5) {
var lex = canvas.get(this.p.x, this.p.y)
undo.save_lex(lex.x, lex.y, lex)
lex.fg = rand_hue()
// lex.bg = colors.black
lex.char = choice(this.chars)
lex.build()
}
},
})
/* Fill tool */
var FillTool = NopaintTool.extend({
type: "fill",
rate: 25,
is_fill: true,
start: function(){
this.fill()
},
paint: function(t){
if ((t % this.rate) == this.rate-1) {
this.fill()
}
},
recolor: function(){
this.fg = this.bg = randint(16)
this.char = " "
this.opacity = 1
},
fill: function(){
var x = randint(canvas.w)
var y = randint(canvas.h)
this.recolor()
draw.fill(this, x, y)
}
})
var FillLetterTool = FillTool.extend({
type: "fill-letter",
rate: 25,
recolor: function(){
this.fg = randint(16)
this.bg = randint(16)
this.char = choice( unicode.block(letters.charset, 32) )
this.opacity = 1
},
})
/* Shader Tools */
var ShaderTool = NopaintTool.extend({
type: "shader",
speed: 3,
is_shader: true,
is_recursive: false,
start: function(){
undo.save_rect(0, 0, canvas.w, canvas.h)
this.canvas = canvas.clone()
},
paint: function(t){
if ((t % this.speed) == 0) {
var w = canvas.w
var h = canvas.h
var lex
if (this.is_recursive) {
this.canvas.assign(canvas)
}
this.before_shade()
for (var x = 0; x < w; x++) {
for (var y = 0; y < h; y++) {
lex = canvas.get(x, y)
if (! this.shade( this.canvas, canvas, lex, x, y, w, h )) {
lex.build()
}
}
}
}
},
before_shade: function(){},
shade: function(src, dest, lex, x, y, w, h){},
finish: function(){
this.canvas.demolish()
}
})
var ColorizeTool = ShaderTool.extend({
type: "colorize",
fns: [mirc_color_reverse,hue,inv_hue,gray,fire,red,yellow,green,blue,purple,dark_gray],
speed: 5,
start: function(){
this.__start()
this.i = randint(this.fns.length)
},
before_shade: function(){
this.i = (this.i + 1) % this.fns.length
this.fn = this.fns[this.i]
},
shade: function(src, dest, lex, x, y, w, h){
lex.bg = this.fn( lex.bg )
return false
},
})
var TranslateTool = ShaderTool.extend({
type: "translate",
dx: 0,
dy: 0,
speed: 3,
start: function(){
this.__start()
this.dx = randint(3)-1
this.dy = randint(3)-1
this.x = this.y = 0
if (! this.dx && ! this.dy) {
this.dx = 1
this.dy = 0
}
},
before_shade: function(){
this.x += this.dx
this.y += this.dy
},
shade: function(src, dest, lex, x, y, w, h){
var copy = src.get(x+this.x, y+this.y)
lex.assign(copy)
return true
},
})
var SliceTool = ShaderTool.extend({
type: "slice",
dx: 0,
dy: 0,
speed: 1,
is_recursive: true,
start: function(){
this.__start()
this.is_y = Math.random() > 0.3
this.limit = this.is_y ? canvas.h : canvas.w
this.position = randint(this.limit)
this.direction = 1
},
before_shade: function(){
if (Math.random() < 0.6) {
this.position = mod(this.position + 1, this.limit)
}
if (Math.random() > 0.8) {
this.direction = randsign()
}
},
shade: function(src, dest, lex, x, y, w, h){
if (this.is_y) {
if (y >= this.position) {
var copy = src.get(x + this.direction, y)
lex.assign(copy)
}
}
else if (x >= this.position) {
var copy = src.get(x, y + this.direction)
lex.assign(copy)
}
return true
},
})
var ScaleTool = ShaderTool.extend({
type: "scale",
scale: 1,
dscale: 0,
speed: 3,
start: function(){
this.__start()
var sign = randsign()
this.x_scale = 1
this.y_scale = 1
this.dx_scale = randsign() * randrange(0.0005, 0.01)
var r = Math.random()
if (r < 0.333) {
this.dy_scale = this.dx_scale * randrange(0.85, 1.25)
}
else if (r < 0.666) {
this.dy_scale = this.dx_scale
}
else {
this.dy_scale = randsign() * randrange(0.0005, 0.01)
}
},
before_shade: function(){
this.x_scale += this.dx_scale
this.y_scale += this.dy_scale
},
shade: function(src, dest, lex, x, y, w, h){
x = (x/w) * 2 - 1
y = (y/h) * 2 - 1
x *= this.x_scale
y *= this.y_scale
x = (x + 1) / 2 * w
y = (y + 1) / 2 * h
var copy = src.get(x, y)
lex.assign(copy)
return true
},
})
var RotateTool = ShaderTool.extend({
type: "rotate",
theta: 0,
d_theta: 0,
start: function(){
this.__start()
var sign = randsign()
this.theta = 0
this.d_theta = randsign() * randrange(0.001, 0.05)
},
before_shade: function(){
this.theta += this.d_theta
},
shade: function(src, dest, lex, x, y, w, h){
x = (x/w) * 2 - 1
y = (y/h) * 2 - 1
var ca = cos(this.theta)
var sa = sin(this.theta)
var a = x * ca - y * sa
var b = x * sa + y * ca
x = (a + 1) / 2 * w
y = (b + 1) / 2 * h
var copy = src.get(x, y)
lex.assign(copy)
return true
},
})
var CycleTool = ShaderTool.extend({
type: "cycle",
n: 0,
speed: 5,
is_recursive: true,
start: function(){
this.__start()
this.n = randsign()
if (random() < 0.2) this.n *= randint(15)
},
shade: function(src, dest, lex, x, y){
lex.bg += this.n
return false
},
})
nopaint.add_tool( new SolidBrush({ weight: 5 }) )
nopaint.add_tool( new ShadowBrush({ weight: 10 }) )
nopaint.add_tool( new EraseBrush({ weight: 5 }) )
nopaint.add_tool( new RandomBrush({ weight: 4 }) )
nopaint.add_tool( new HueBrush({ weight: 5 }) )
nopaint.add_tool( new GrayBrush({ weight: 5 }) )
nopaint.add_tool( new LetterBrush({ weight: 2 }) )
nopaint.add_tool( new RandomLetterBrush({ weight: 12 }) )
nopaint.add_tool( new CloneBrush({ weight: 8 }) )
nopaint.add_tool( new SmearBrush({ weight: 10 }) )
nopaint.add_tool( new FillTool({ weight: 3 }) )
nopaint.add_tool( new FillLetterTool({ weight: 6 }) )
nopaint.add_tool( new StarsTool({ weight: 2 }) )
nopaint.add_tool( new TranslateTool({ weight: 4 }) )
nopaint.add_tool( new CycleTool({ weight: 1 }) )
nopaint.add_tool( new ScaleTool({ weight: 3 }) )
nopaint.add_tool( new RotateTool({ weight: 3 }) )
nopaint.add_tool( new SliceTool({ weight: 4 }) )
nopaint.add_tool( new ColorizeTool({ weight: 1 }) )
nopaint.regenerate_weights()
nopaint.toggle(true)
nopaint.player = new StylePlayer ()
return nopaint
})()

106
js/ui/palette.js Normal file
View File

@ -0,0 +1,106 @@
var palette = (function(){
var palette = new Matrix (32, 2, function(x,y){
var lex = new Lex (x,y)
return lex
})
var palette_index = localStorage.getItem("ascii.palette") || 1
var palette_list = [all_hue, all_inv_hue, mirc_color, mirc_color_reverse]
var palette_fn = palette_list[palette_index]
palette.chars = " " + dither.a + dither.b + dither.c
palette.repaint = function(){
var xw = use_experimental_palette ? 5 : 2
if (canvas.vertical) {
palette.resize( xw, 16 )
}
else {
palette.resize( 32, xw )
}
palette.forEach(function(lex,x,y){
if (canvas.vertical) { x=x^y;y=x^y;x=x^y;x*=2 }
if (y < 2) {
lex.bg = palette_fn(x>>1)
lex.fg = palette_fn(x>>1)
}
else {
lex.bg = fillColor
lex.fg = palette_fn(x>>1)
}
lex.char = palette.chars[y]
lex.opacity = 1
lex.build()
if (lex.char == "_") lex.char = " "
})
}
palette.repaint()
var use_experimental_palette = false
palette.experimental = function(state){
use_experimental_palette = typeof state == "boolean" ? state : ! use_experimental_palette
use_experimental_palette ? palette.resize(32, 5) : palette.resize(32, 2)
palette.repaint()
return use_experimental_palette
}
palette.bind = function(){
palette.forEach(function(lex, x, y){
if (lex.bound) return
lex.bound = true
lex.span.addEventListener('mousedown', function(e){
e.preventDefault()
if (e.shiftKey) {
palette_index = (palette_index+1) % palette_list.length
localStorage.setItem("ascii.palette", palette_index)
palette_fn = palette_list[palette_index]
palette.repaint()
return
}
if (e.ctrlKey || e.which == 3) return
if (brush.char == " " && lex.char == " ") {
brush.fg = lex.fg
brush.bg = lex.bg
brush.char = lex.char
}
else if (lex.char != " ") {
brush.fg = lex.bg
brush.bg = lex.fg
brush.char = lex.char
}
else {
brush.fg = lex.bg
brush.bg = fillColor
// brush.char = lex.char
}
brush.opacity = lex.opacity
if (! brush.modified) {
brush.generate()
}
if (filling || e.ctrlKey) {
fillColor = lex.bg
}
letters.repaint()
})
lex.span.addEventListener('contextmenu', function(e){
e.preventDefault()
fillColor = y ? lex.fg : lex.bg
palette.repaint()
brush.fg = lex.fg
brush.char = lex.char
brush.opacity = lex.opacity
brush.generate()
brush_rapper.style.borderColor = css_reverse_lookup[fillColor]
return
})
})
}
brush_rapper.style.borderColor = css_reverse_lookup[fillColor]
return palette
})()

159
js/ui/selection.js Normal file
View File

@ -0,0 +1,159 @@
var selection = (function(){
var creating = false, moving = false, copying = false
var selection_canvas = new Matrix (1, 1, function(x,y){
var lex = new Lex (x,y)
lex.build()
return lex
})
var selector_el = document.createElement("div")
selector_el.className = "selector_el"
selection_canvas.append(selector_el)
document.body.appendChild(selector_el)
// in selection mode..
// - we start by clicking the canvas. this positions the selection, and copies
// the character
// - then we drag down and to the right. this resizes the selection and pushes new
// rows and columns. each of these copies the character underneath.
// - on mouseup, the selection is locked. then..
// - drag the selection to move it -- this "cuts" it and leaves a blank space on the canvas.
// - shift-drag the selection to copy it
var a = [0, 0]
var b = [0, 0]
var c = [0, 0]
var d = [0, 0]
function reset () {
a[0] = a[1] = b[0] = b[1] = 0
}
function left (a,b) { return min(a[0],b[0]) }
function top (a,b) { return min(a[1],b[1]) }
function right (a,b) { return max(a[0],b[0]) }
function bottom (a,b) { return max(a[1],b[1]) }
function width (a,b) { return abs(a[0]-b[0])+1 }
function height (a,b) { return abs(a[1]-b[1])+1 }
function mag_x (a,b) { return a[0]-b[0] }
function mag_y (a,b) { return a[1]-b[1] }
function orient (a,b) {
var l = left(a,b), m = top(a,b), n = right(a,b), o = bottom(a,b)
a[0] = l ; a[1] = m ; b[0] = n ; b[1] = o
}
function contains (a,b,point) {
var contains_x = a[0] <= point[0] && point[0] <= b[0]
var contains_y = a[1] <= point[1] && point[1] <= b[1]
return (contains_x && contains_y)
}
function reposition (aa, bb) {
aa = aa || a
bb = bb || b
var cell = canvas.aa[top(aa, bb)][left(aa, bb)].span
var cell_left = cell.offsetLeft
var cell_top = cell.offsetTop
var cell_width = cell.offsetWidth
var cell_height = cell.offsetHeight
var w = width(aa, bb)
var h = height(aa, bb)
selector_el.style.top = (cell_top-1) + "px"
selector_el.style.left = (cell_left-1) + "px"
selector_el.style.width = (cell_width*w+1) + "px"
selector_el.style.height = (cell_height*h+1) + "px"
}
function down (e, lex, point){
if ( ! contains(a,b,point) ) {
copying = false
moving = false
creating = true
a[0] = point[0]
a[1] = point[1]
b[0] = point[0]
b[1] = point[1]
reposition(a,b)
selection.hidden = false
selector_el.classList.add("creating")
} else {
copying = false
moving = true
creating = false
c[0] = point[0]
c[1] = point[1]
d[0] = point[0]
d[1] = point[1]
}
show()
selector_el.classList.remove("dragging")
}
function move (e, lex, point){
if (creating) {
b[0] = point[0]
b[1] = point[1]
reposition(a,b)
}
else if (moving) {
d[0] = point[0]
d[1] = point[1]
var dx = - clamp( mag_x(c,d), b[0] - canvas.w + 1, a[0] )
var dy = - clamp( mag_y(c,d), b[1] - canvas.h + 1, a[1] )
reposition( [ a[0] + dx, a[1] + dy ], [ b[0] + dx, b[1] + dy ])
}
else if (copying) {
}
}
function up (e) {
if (creating) {
orient(a,b)
selection_canvas.resize(width(a,b), height(a,b))
reposition(a,b)
blit.copy_from( canvas, selection_canvas, a[0], a[1] )
selection_canvas.build()
selector_el.classList.remove("creating")
}
if (moving) {
var dx = - clamp( mag_x(c,d), b[0] - canvas.w + 1, a[0] )
var dy = - clamp( mag_y(c,d), b[1] - canvas.h + 1, a[1] )
a[0] += dx
a[1] += dy
b[0] += dx
b[1] += dy
undo.new()
undo.save_rect(a[0], a[1], b[0] - a[0] + 1, b[1] - a[1] + 1)
blit.copy_to( canvas, selection_canvas, a[0], a[1] )
}
if (copying) {
}
creating = moving = copying = false
selector_el.classList.remove("dragging")
}
function show () {
selecting = true
}
function hide () {
reset()
selector_el.style.top = "-9999px"
selector_el.style.left = "-9999px"
selector_el.style.width = "0px"
selector_el.style.height = "0px"
creating = moving = copying = false
selection.hidden = true
selecting = false
}
var selection = {}
selection.reposition = reposition
selection.down = down
selection.move = move
selection.up = up
selection.canvas = selection_canvas
selection.show = show
selection.hide = hide
selection.hidden = true
return selection
})()

176
js/ui/transform.js Normal file
View File

@ -0,0 +1,176 @@
var transform = (function(){
var p = [0,0], q = [0,0]
var mode
var copy
function down (e, lex, point){
p[0] = point[0]
p[1] = point[1]
q[0] = e.pageX
q[1] = e.pageY
undo.new()
undo.save_rect(0, 0, canvas.w, canvas.h)
copy = canvas.clone()
mode.init(e)
}
function move (e, lex, point){
var pdx = point[0] - p[0]
var pdy = point[1] - p[1]
var dx = e.pageX - q[0]
var dy = e.pageY - q[1]
var w = canvas.w
var h = canvas.h
mode.before(dx, dy, pdx, pdy, point)
for (var x = 0; x < w; x++) {
for (var y = 0; y < h; y++) {
lex = canvas.get(x, y)
if (! mode.shade( copy, canvas, lex, x, y, w, h )) {
lex.build()
}
}
}
}
function up (e){
}
var modes = {
rotate: {
init: function(e){
mode.theta = 0
},
before: function(dx, dy){
var radius = dist(0, 0, dx, dy)
if (radius < 10) return
mode.theta = angle(0, 0, dx, -dy)
},
shade: function(src, dest, lex, x, y, w, h){
x = (x/w) * 2 - 1
y = (y/h) * 2 - 1
var ca = cos(mode.theta)
var sa = sin(mode.theta)
var a = x * ca - y * sa
var b = x * sa + y * ca
x = (a + 1) / 2 * w
y = (b + 1) / 2 * h
var copy = src.get(x, y)
lex.assign(copy)
return true
},
},
scale: {
init: function(e){
mode.independent = e.shiftKey || e.altKey || e.metaKey
mode.x_scale = mode.y_scale = 0
},
before: function(dx, dy, pdx, pdy){
if (mode.independent) {
mode.x_scale = Math.pow(2, -pdx / (canvas.w / 8))
mode.y_scale = Math.pow(2, -pdy / (canvas.h / 8))
}
else {
mode.x_scale = mode.y_scale = Math.pow(2, -pdx / (canvas.w / 8))
}
},
shade: function(src, dest, lex, x, y, w, h){
x = ((x-p[0])/w) * 2 - 1
y = ((y-p[1])/h) * 2 - 1
x *= mode.x_scale
y *= mode.y_scale
x = (x + 1) / 2 * w
y = (y + 1) / 2 * h
var copy = src.get(x+p[0], y+p[1])
lex.assign(copy)
return true
},
},
translate: {
init: function(e){
mode.dx = mode.dy = 0
},
before: function(dx, dy, pdx, pdy){
mode.dx = -pdx
mode.dy = -pdy
},
shade: function(src, dest, lex, x, y, w, h){
var copy = src.get(x+mode.dx, y+mode.dy)
lex.assign(copy)
return true
},
},
slice: {
init: function(e){
mode.is_y = ! (e.altKey || e.metaKey)
mode.reverse = !! (e.shiftKey)
mode.position = 0
mode.direction = 0
mode.last_dd = -1
},
before: function(dx, dy, pdx, pdy, point){
var new_position = mode.is_y ? point[1] : point[0]
var dd = mode.is_y ? pdx : pdy
if (mode.position !== new_position) {
mode.position = new_position
mode.direction = 0
}
if (mode.last_dd !== -1) {
mode.direction = mode.last_dd - dd
}
console.log(mode.position)
mode.last_dd = dd
copy.assign(canvas)
},
shade: function(src, dest, lex, x, y, w, h){
if (mode.is_y) {
if (y >= mode.position || (mode.reverse && mode.position >= y)) {
var copy = src.get(x + mode.direction, y)
lex.assign(copy)
}
}
else if (x >= mode.position || (mode.reverse && mode.position >= x)) {
var copy = src.get(x, y + mode.direction)
lex.assign(copy)
}
return true
},
},
/*
mode: {
init: function(e){
},
before: function(dx, dy, pdx, pdy){
},
shade: function(src, dest, lex, x, y, w, h){
},
},
*/
}
function set_mode(m){
if (m in modes) {
mode = modes[m]
transforming = true
}
}
function done(){
transforming = false
copy && copy.demolish()
}
return {
down: down,
move: move,
up: up,
set_mode: set_mode,
modes: modes,
done: done,
}
})()

227
js/undo.js Normal file
View File

@ -0,0 +1,227 @@
var undo = (function(){
var max_states = 200;
// undotimetotal = 0;
var stack = {undo: [], redo: []};
var current_undo = null;
var dom = {undo: undo_el, redo: redo_el};
dom.undo.is_visible = dom.redo.is_visible = false
var LexState = function(lex){
this.fg = lex.fg;
this.bg = lex.bg;
this.char = lex.char;
this.opacity = lex.opacity;
};
var update_dom_visibility = function(type){
var el = dom[type]
if (el.is_visible){
if (stack[type].length === 0) {
el.classList.add('hidden')
el.is_visible = false
}
} else if (stack[type].length > 0){
el.classList.remove('hidden')
el.is_visible = true
}
}
var update_dom = function(){
update_dom_visibility('undo')
update_dom_visibility('redo')
}
// state is an undo or redo state that might contain these props
// { lexs: {'0,0': LexState, ...}, // for sparse lex changes (eg brush, fill)
// focus: {x:, y: },
// size: {w:, h: },
// rects: [{x:, y:, w:, h:, lexs: [LexState, ...]}, ...]
// }
var new_state = function(){
var state = {lexs:{}};
save_focus(canvas.focus_x, canvas.focus_y, state)
return state
}
var new_redo = function(){
return new_state()
}
var new_undo = function(){
current_undo = new_state()
stack.redo = []
stack.undo.push(current_undo)
if (stack.undo.length > max_states) stack.undo.shift();
update_dom()
return current_undo
}
var save_focus = function(x, y, state){
state = state || current_undo
state.focus = {x:x, y:y}
}
var save_size = function(w, h, state){
state = state || current_undo
state.size = {w:w, h:h};
}
// the reason for stringifying the x y coords is so that each
// coordinate is saved only once in an undo state.
// otherwise there would be problems with, eg, a brush stroke
// that passed over the same grid cell twice.
var save_lex = function(x, y, lex, state){
// var start = Date.now()
state = state || current_undo
var lexs = state.lexs;
var xy = x + "," + y;
if (xy in lexs) return;
lexs[xy] = new LexState(lex)
// undotimetotal += Date.now() - start
}
var save_focused_lex = function(state){
state = state || current_undo
var x = canvas.focus_x
var y = canvas.focus_y
save_lex(x, y, canvas.aa[y][x], state)
}
var save_rect = function(xpos, ypos, w, h, state){
if (w === 0 || h === 0) return;
state = state || current_undo;
state.rects = state.rects || []
var aa = canvas.aa;
var rect = {x: xpos, y: ypos, w: w, h: h, lexs: []}
var lexs = rect.lexs
var xlen = xpos + w
var ylen = ypos + h
for (var y = ypos; y < ylen; y++){
var aay = aa[y]
for (var x = xpos; x < xlen; x++){
lexs.push(new LexState(aay[x]))
}
}
state.rects.push(rect)
}
var save_resize = function(w, h, old_w, old_h, state){
state = state || current_undo
save_size(old_w, old_h, state)
if (old_w > w){
// .---XX
// | XX
// |___XX
save_rect(w, 0, old_w - w, old_h, state)
if (old_h > h){
// .----.
// | |
// XXXX_|
save_rect(0, h, w, old_h - h, state)
}
} else if (old_h > h){
// .----.
// | |
// XXXXXX
save_rect(0, h, old_w, old_h - h, state)
}
}
var restore_state = function(state){
// all redo states will have a cached undo state on them
// an undo state might have a cached redo state
// if it doesn't have one, generate one
var make_redo = ! ('redo' in state || 'undo' in state);
var aa = canvas.aa
var lex, lexs;
if (make_redo){
state.redo = new_redo()
// copy saved rects that intersect with current canvas size
// important to do this before resizing canvas
if ('rects' in state){
for (var ri=0, rect; rect=state.rects[ri]; ri++){
if (rect.x >= canvas.w ||
rect.y >= canvas.h) continue;
var w = Math.min(rect.w, canvas.w - rect.x)
var h = Math.min(rect.h, canvas.h - rect.y)
save_rect(rect.x, rect.y, w, h, state.redo)
}
}
if ('size' in state){
save_resize(state.size.w, state.size.h, canvas.w, canvas.h, state.redo)
}
}
if ('size' in state){
canvas.resize(state.size.w, state.size.h, true);
}
if ('rects' in state){
for (var ri=0, rect; rect=state.rects[ri]; ri++){
lexs = rect.lexs
for (var li=0; lex=lexs[li]; li++){
var x = (li % rect.w) + rect.x
var y = ((li / rect.w)|0) + rect.y
aa[y][x].assign(lex)
}
}
}
lexs = state.lexs
for (var key in lexs){
var xy = key.split(',');
lex = aa[xy[1]][xy[0]]
if (make_redo)
save_lex(xy[0], xy[1], lex, state.redo)
lex.assign(lexs[key])
}
if ('focus' in state){
canvas.focus_x = state.focus.x
canvas.focus_y = state.focus.y
if (current_canvas === canvas){
canvas.focus()
}
}
}
var undo = function(){
var state = stack.undo.pop();
if (!state) return;
restore_state(state)
// now take the applied undo state and store it on the redo state
// and push the redo state to the redo stack
state.redo.undo = state
stack.redo.push(state.redo)
delete state.redo
update_dom()
}
var redo = function(){
var state = stack.redo.pop();
if (!state) return;
restore_state(state)
state.undo.redo = state
stack.undo.push(state.undo)
delete state.undo
update_dom()
}
return {
stack: stack,
new: new_undo,
// new_redo: new_redo,
save_focus: save_focus,
save_size: save_size,
save_lex: save_lex,
save_focused_lex: save_focused_lex,
save_rect: save_rect,
save_resize: save_resize,
undo: undo,
redo: redo
}
})()

543
js/unicode.js Normal file
View File

@ -0,0 +1,543 @@
var unicode = (function(){
var UNICODE_BLOCK_LIST = [
0x0020, 0x007F, "Basic Latin",
0x0080, 0x00FF, "Latin-1 Supplement",
0x0100, 0x017F, "Latin Extended-A",
0x0180, 0x024F, "Latin Extended-B",
0x0250, 0x02AF, "IPA Extensions",
0x02B0, 0x02FF, "Spacing Modifier Letters",
0x0300, 0x036F, "Combining Diacritical Marks",
0x0370, 0x03FF, "Greek and Coptic",
0x0400, 0x04FF, "Cyrillic",
0x0500, 0x052F, "Cyrillic Supplement",
0x0530, 0x058F, "Armenian",
0x0590, 0x05FF, "Hebrew",
0x0600, 0x06FF, "Arabic",
0x0700, 0x074F, "Syriac",
0x0750, 0x077F, "Arabic Supplement",
0x0780, 0x07BF, "Thaana",
0x07C0, 0x07FF, "NKo",
0x0800, 0x083F, "Samaritan",
0x0840, 0x085F, "Mandaic",
0x08A0, 0x08FF, "Arabic Extended-A",
0x0900, 0x097F, "Devanagari",
0x0980, 0x09FF, "Bengali",
0x0A00, 0x0A7F, "Gurmukhi",
0x0A80, 0x0AFF, "Gujarati",
0x0B00, 0x0B7F, "Oriya",
0x0B80, 0x0BFF, "Tamil",
0x0C00, 0x0C7F, "Telugu",
0x0C80, 0x0CFF, "Kannada",
0x0D00, 0x0D7F, "Malayalam",
0x0D80, 0x0DFF, "Sinhala",
0x0E00, 0x0E7F, "Thai",
0x0E80, 0x0EFF, "Lao",
0x0F00, 0x0FFF, "Tibetan",
0x1000, 0x109F, "Myanmar",
0x10A0, 0x10FF, "Georgian",
0x1100, 0x11FF, "Hangul Jamo",
0x1200, 0x137F, "Ethiopic",
0x1380, 0x139F, "Ethiopic Supplement",
0x13A0, 0x13FF, "Cherokee",
0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics",
0x1680, 0x169F, "Ogham",
0x16A0, 0x16FF, "Runic",
0x1700, 0x171F, "Tagalog",
0x1720, 0x173F, "Hanunoo",
0x1740, 0x175F, "Buhid",
0x1760, 0x177F, "Tagbanwa",
0x1780, 0x17FF, "Khmer",
0x1800, 0x18AF, "Mongolian",
0x18B0, 0x18FF, "Unified Canadian Aboriginal Syllabics Extended",
0x1900, 0x194F, "Limbu",
0x1950, 0x197F, "Tai Le",
0x1980, 0x19DF, "New Tai Lue",
0x19E0, 0x19FF, "Khmer Symbols",
0x1A00, 0x1A1F, "Buginese",
0x1A20, 0x1AAF, "Tai Tham",
0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended",
0x1B00, 0x1B7F, "Balinese",
0x1B80, 0x1BBF, "Sundanese",
0x1BC0, 0x1BFF, "Batak",
0x1C00, 0x1C4F, "Lepcha",
0x1C50, 0x1C7F, "Ol Chiki",
0x1CC0, 0x1CCF, "Sundanese Supplement",
0x1CD0, 0x1CFF, "Vedic Extensions",
0x1D00, 0x1D7F, "Phonetic Extensions",
0x1D80, 0x1DBF, "Phonetic Extensions Supplement",
0x1DC0, 0x1DFF, "Combining Diacritical Marks Supplement",
0x1E00, 0x1EFF, "Latin Extended Additional",
0x1F00, 0x1FFF, "Greek Extended",
0x2000, 0x206F, "General Punctuation",
0x2070, 0x209F, "Superscripts and Subscripts",
0x20A0, 0x20CF, "Currency Symbols",
0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols",
0x2100, 0x214F, "Letterlike Symbols",
0x2150, 0x218F, "Number Forms",
0x2190, 0x21FF, "Arrows",
0x2200, 0x22FF, "Mathematical Operators",
0x2300, 0x23FF, "Miscellaneous Technical",
0x2400, 0x243F, "Control Pictures",
0x2440, 0x245F, "Optical Character Recognition",
0x2460, 0x24FF, "Enclosed Alphanumerics",
0x2500, 0x257F, "Box Drawing",
0x2580, 0x259F, "Block Elements",
0x25A0, 0x25FF, "Geometric Shapes",
0x2600, 0x26FF, "Miscellaneous Symbols",
0x2700, 0x27BF, "Dingbats",
0x27C0, 0x27EF, "Miscellaneous Mathematical Symbols-A",
0x27F0, 0x27FF, "Supplemental Arrows-A",
0x2800, 0x28FF, "Braille Patterns",
0x2900, 0x297F, "Supplemental Arrows-B",
0x2980, 0x29FF, "Miscellaneous Mathematical Symbols-B",
0x2A00, 0x2AFF, "Supplemental Mathematical Operators",
0x2B00, 0x2BFF, "Miscellaneous Symbols and Arrows",
0x2C00, 0x2C5F, "Glagolitic",
0x2C60, 0x2C7F, "Latin Extended-C",
0x2C80, 0x2CFF, "Coptic",
0x2D00, 0x2D2F, "Georgian Supplement",
0x2D30, 0x2D7F, "Tifinagh",
0x2D80, 0x2DDF, "Ethiopic Extended",
0x2DE0, 0x2DFF, "Cyrillic Extended-A",
0x2E00, 0x2E7F, "Supplemental Punctuation",
0x2E80, 0x2EFF, "CJK Radicals Supplement",
0x2F00, 0x2FDF, "Kangxi Radicals",
0x2FF0, 0x2FFF, "Ideographic Description Characters",
0x3000, 0x303F, "CJK Symbols and Punctuation",
0x3040, 0x309F, "Hiragana",
0x30A0, 0x30FF, "Katakana",
0x3100, 0x312F, "Bopomofo",
0x3130, 0x318F, "Hangul Compatibility Jamo",
0x3190, 0x319F, "Kanbun",
0x31A0, 0x31BF, "Bopomofo Extended",
0x31C0, 0x31EF, "CJK Strokes",
0x31F0, 0x31FF, "Katakana Phonetic Extensions",
0x3200, 0x32FF, "Enclosed CJK Letters and Months",
0x3300, 0x33FF, "CJK Compatibility",
0x3400, 0x4DBF, "CJK Unified Ideographs Extension A",
0x4DC0, 0x4DFF, "Yijing Hexagram Symbols",
0x4E00, 0x9FFF, "CJK Unified Ideographs",
0xA000, 0xA48F, "Yi Syllables",
0xA490, 0xA4CF, "Yi Radicals",
0xA4D0, 0xA4FF, "Lisu",
0xA500, 0xA63F, "Vai",
0xA640, 0xA69F, "Cyrillic Extended-B",
0xA6A0, 0xA6FF, "Bamum",
0xA700, 0xA71F, "Modifier Tone Letters",
0xA720, 0xA7FF, "Latin Extended-D",
0xA800, 0xA82F, "Syloti Nagri",
0xA830, 0xA83F, "Common Indic Number Forms",
0xA840, 0xA87F, "Phags-pa",
0xA880, 0xA8DF, "Saurashtra",
0xA8E0, 0xA8FF, "Devanagari Extended",
0xA900, 0xA92F, "Kayah Li",
0xA930, 0xA95F, "Rejang",
0xA960, 0xA97F, "Hangul Jamo Extended-A",
0xA980, 0xA9DF, "Javanese",
0xA9E0, 0xA9FF, "Myanmar Extended-B",
0xAA00, 0xAA5F, "Cham",
0xAA60, 0xAA7F, "Myanmar Extended-A",
0xAA80, 0xAADF, "Tai Viet",
0xAAE0, 0xAAFF, "Meetei Mayek Extensions",
0xAB00, 0xAB2F, "Ethiopic Extended-A",
0xAB30, 0xAB6F, "Latin Extended-E",
0xABC0, 0xABFF, "Meetei Mayek",
0xAC00, 0xD7AF, "Hangul Syllables",
0xD7B0, 0xD7FF, "Hangul Jamo Extended-B",
0xD800, 0xDB7F, "High Surrogates",
0xDB80, 0xDBFF, "High Private Use Surrogates",
0xDC00, 0xDFFF, "Low Surrogates",
0xE000, 0xF8FF, "Private Use Area",
0xF900, 0xFAFF, "CJK Compatibility Ideographs",
0xFB00, 0xFB4F, "Alphabetic Presentation Forms",
0xFB50, 0xFDFF, "Arabic Presentation Forms-A",
0xFE00, 0xFE0F, "Variation Selectors",
0xFE10, 0xFE1F, "Vertical Forms",
0xFE20, 0xFE2F, "Combining Half Marks",
0xFE30, 0xFE4F, "CJK Compatibility Forms",
0xFE50, 0xFE6F, "Small Form Variants",
0xFE70, 0xFEFF, "Arabic Presentation Forms-B",
0xFF00, 0xFFEF, "Halfwidth and Fullwidth Forms",
0xFFF0, 0xFFFF, "Specials",
0x10000, 0x1007F, "Linear B Syllabary",
0x10080, 0x100FF, "Linear B Ideograms",
0x10100, 0x1013F, "Aegean Numbers",
0x10140, 0x1018F, "Ancient Greek Numbers",
0x10190, 0x101CF, "Ancient Symbols",
0x101D0, 0x101FF, "Phaistos Disc",
0x10280, 0x1029F, "Lycian",
0x102A0, 0x102DF, "Carian",
0x102E0, 0x102FF, "Coptic Epact Numbers",
0x10300, 0x1032F, "Old Italic",
0x10330, 0x1034F, "Gothic",
0x10350, 0x1037F, "Old Permic",
0x10380, 0x1039F, "Ugaritic",
0x103A0, 0x103DF, "Old Persian",
0x10400, 0x1044F, "Deseret",
0x10450, 0x1047F, "Shavian",
0x10480, 0x104AF, "Osmanya",
0x10500, 0x1052F, "Elbasan",
0x10530, 0x1056F, "Caucasian Albanian",
0x10600, 0x1077F, "Linear A",
0x10800, 0x1083F, "Cypriot Syllabary",
0x10840, 0x1085F, "Imperial Aramaic",
0x10860, 0x1087F, "Palmyrene",
0x10880, 0x108AF, "Nabataean",
0x10900, 0x1091F, "Phoenician",
0x10920, 0x1093F, "Lydian",
0x10980, 0x1099F, "Meroitic Hieroglyphs",
0x109A0, 0x109FF, "Meroitic Cursive",
0x10A00, 0x10A5F, "Kharoshthi",
0x10A60, 0x10A7F, "Old South Arabian",
0x10A80, 0x10A9F, "Old North Arabian",
0x10AC0, 0x10AFF, "Manichaean",
0x10B00, 0x10B3F, "Avestan",
0x10B40, 0x10B5F, "Inscriptional Parthian",
0x10B60, 0x10B7F, "Inscriptional Pahlavi",
0x10B80, 0x10BAF, "Psalter Pahlavi",
0x10C00, 0x10C4F, "Old Turkic",
0x10E60, 0x10E7F, "Rumi Numeral Symbols",
0x11000, 0x1107F, "Brahmi",
0x11080, 0x110CF, "Kaithi",
0x110D0, 0x110FF, "Sora Sompeng",
0x11100, 0x1114F, "Chakma",
0x11150, 0x1117F, "Mahajani",
0x11180, 0x111DF, "Sharada",
0x111E0, 0x111FF, "Sinhala Archaic Numbers",
0x11200, 0x1124F, "Khojki",
0x112B0, 0x112FF, "Khudawadi",
0x11300, 0x1137F, "Grantha",
0x11480, 0x114DF, "Tirhuta",
0x11580, 0x115FF, "Siddham",
0x11600, 0x1165F, "Modi",
0x11680, 0x116CF, "Takri",
0x118A0, 0x118FF, "Warang Citi",
0x11AC0, 0x11AFF, "Pau Cin Hau",
0x12000, 0x123FF, "Cuneiform",
0x12400, 0x1247F, "Cuneiform Numbers and Punctuation",
0x13000, 0x1342F, "Egyptian Hieroglyphs",
0x16800, 0x16A3F, "Bamum Supplement",
0x16A40, 0x16A6F, "Mro",
0x16AD0, 0x16AFF, "Bassa Vah",
0x16B00, 0x16B8F, "Pahawh Hmong",
0x16F00, 0x16F9F, "Miao",
0x1B000, 0x1B0FF, "Kana Supplement",
0x1BC00, 0x1BC9F, "Duployan",
0x1BCA0, 0x1BCAF, "Shorthand Format Controls",
0x1D000, 0x1D0FF, "Byzantine Musical Symbols",
0x1D100, 0x1D1FF, "Musical Symbols",
0x1D200, 0x1D24F, "Ancient Greek Musical Notation",
0x1D300, 0x1D35F, "Tai Xuan Jing Symbols",
0x1D360, 0x1D37F, "Counting Rod Numerals",
0x1D400, 0x1D7FF, "Mathematical Alphanumeric Symbols",
0x1E800, 0x1E8DF, "Mende Kikakui",
0x1EE00, 0x1EEFF, "Arabic Mathematical Alphabetic Symbols",
0x1F000, 0x1F02F, "Mahjong Tiles",
0x1F030, 0x1F09F, "Domino Tiles",
0x1F0A0, 0x1F0FF, "Playing Cards",
0x1F100, 0x1F1FF, "Enclosed Alphanumeric Supplement",
0x1F200, 0x1F2FF, "Enclosed Ideographic Supplement",
0x1F300, 0x1F5FF, "Miscellaneous Symbols and Pictographs",
0x1F600, 0x1F64F, "Emoticons",
0x1F650, 0x1F67F, "Ornamental Dingbats",
0x1F680, 0x1F6FF, "Transport and Map Symbols",
0x1F700, 0x1F77F, "Alchemical Symbols",
0x1F780, 0x1F7FF, "Geometric Shapes Extended",
0x1F800, 0x1F8FF, "Supplemental Arrows-C",
0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B",
0x2A700, 0x2B73F, "CJK Unified Ideographs Extension C",
0x2B740, 0x2B81F, "CJK Unified Ideographs Extension D",
0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement",
0xE0000, 0xE007F, "Tags",
0xE0100, 0xE01EF, "Variation Selectors Supplement",
0xF0000, 0xFFFFF, "Supplementary Private Use Area-A",
0x100000, 0x10FFFF, "Supplementary Private Use Area-B",
]
var UNICODE_BLOCK_COUNT = UNICODE_BLOCK_LIST.length / 3
var UNICODE_LOOKUP = {}
for (var i = 0, len = UNICODE_BLOCK_LIST.length; i < len; i += 3) {
UNICODE_LOOKUP[ UNICODE_BLOCK_LIST[i+2] ] = [ UNICODE_BLOCK_LIST[i], UNICODE_BLOCK_LIST[i+1] ]
}
function index (j) {
return [ UNICODE_BLOCK_LIST[j*3], UNICODE_BLOCK_LIST[j*3+1], UNICODE_BLOCK_LIST[j*3+2], [] ]
}
function range(m,n){
if (m > n) return []
var a = new Array (n-m)
for (var i = 0, j = m; j <= n; i++, j++) {
a[i] = j
}
return a
}
function paginate (a, n){
var aa = [], ai, i = 0
while (i < 100) {
ai = a.slice(i * n, (i+1) * n)
if (! ai.length) break
aa.push(ai)
i++
}
return aa
}
function block (name, n){
var b = UNICODE_LOOKUP[name]
if (! b) return ""
return range.apply(null, b).map(function(n){ return String.fromCharCode(n) })
}
function entities (a) {
return a.map(function(k){ return "&#" + k.join(";&#") + ";" }).join("<br>")
}
function findGroups (chars){
var groups = [], row, list
for (var i = 0, j = -1, next = -1, len = chars.length; i < len; i++) {
if (chars[i] < next) {
list.push(chars[i])
continue
}
do {
j += 1
next = UNICODE_BLOCK_LIST[(j+1)*3]
} while (chars[i] > next)
row = index(j)
list = row[3]
groups.push( row )
}
return groups
}
// encodes unicode characters as escaped utf16 - \xFFFF
// encodes ONLY non-ascii characters
function escapeToUtf16 (txt) {
var escaped_txt = "", kode
for (var i = 0; i < txt.length; i++) {
kode = txt.charCodeAt(i)
if (kode > 0x7f) {
kode = kode.toString(16)
switch (kode.length) {
case 2:
kode = "0" + kode
case 3:
kode = "0" + kode
}
escaped_txt += "\\u" + kode
}
else {
escaped_txt += txt[i]
}
}
return escaped_txt
}
// encodes unicode characters as escaped bytes - \xFF
// encodes ONLY non-ascii characters
function escapeToEscapedBytes (txt) {
var escaped_txt = "", kode, utf8_bytes
for (var i = 0; i < txt.length; i++) {
kode = txt.charCodeAt(i)
if (kode > 0x7f) {
utf8_bytes = convertUnicodeCodePointToUtf8Bytes(kode)
escaped_txt += convertBytesToEscapedString(utf8_bytes, 16)
}
else {
escaped_txt += txt[i]
}
}
return escaped_txt
}
// encodes unicode characters as escaped bytes - \xFF
// encodes an ENTIRE string
function escapeAllToEscapedBytes(str, base) {
var unicode_codes = convertStringToUnicodeCodePoints(str);
var data_bytes = convertUnicodeCodePointsToBytes(unicode_codes);
return convertBytesToEscapedString(data_bytes, 16);
}
// [ 0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84 ] => '\xE3\x81\x82\xE3\x81\x84'
// [ 0343, 0201, 0202, 0343, 0201, 0204 ] => '\343\201\202\343\201\204'
function convertBytesToEscapedString(data_bytes, base) {
var escaped = '';
for (var i = 0; i < data_bytes.length; ++i) {
var prefix = (base == 16 ? "\\x" : "\\");
var num_digits = base == 16 ? 2 : 3;
var escaped_byte = prefix + formatNumber(data_bytes[i], base, num_digits)
escaped += escaped_byte;
}
return escaped;
}
// [ 0x3042, 0x3044 ] => [ 0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84 ]
function convertUnicodeCodePointsToBytes(unicode_codes) {
var utf8_bytes = [];
for (var i = 0; i < unicode_codes.length; ++i) {
var bytes = convertUnicodeCodePointToUtf8Bytes(unicode_codes[i]);
utf8_bytes = utf8_bytes.concat(bytes);
}
return utf8_bytes;
}
// 0x3042 => [ 0xE3, 0x81, 0x82 ]
function convertUnicodeCodePointToUtf8Bytes(unicode_code) {
var utf8_bytes = [];
if (unicode_code < 0x80) { // 1-byte
utf8_bytes.push(unicode_code);
} else if (unicode_code < (1 << 11)) { // 2-byte
utf8_bytes.push((unicode_code >>> 6) | 0xC0);
utf8_bytes.push((unicode_code & 0x3F) | 0x80);
} else if (unicode_code < (1 << 16)) { // 3-byte
utf8_bytes.push((unicode_code >>> 12) | 0xE0);
utf8_bytes.push(((unicode_code >> 6) & 0x3f) | 0x80);
utf8_bytes.push((unicode_code & 0x3F) | 0x80);
} else if (unicode_code < (1 << 21)) { // 4-byte
utf8_bytes.push((unicode_code >>> 18) | 0xF0);
utf8_bytes.push(((unicode_code >> 12) & 0x3F) | 0x80);
utf8_bytes.push(((unicode_code >> 6) & 0x3F) | 0x80);
utf8_bytes.push((unicode_code & 0x3F) | 0x80);
}
return utf8_bytes;
}
// "あい" => [ 0x3042, 0x3044 ]
function convertStringToUnicodeCodePoints(str) {
var surrogate_1st = 0;
var unicode_codes = [];
for (var i = 0; i < str.length; ++i) {
var utf16_code = str.charCodeAt(i);
if (surrogate_1st != 0) {
if (utf16_code >= 0xDC00 && utf16_code <= 0xDFFF) {
var surrogate_2nd = utf16_code;
var unicode_code = (surrogate_1st - 0xD800) * (1 << 10) + (1 << 16) +
(surrogate_2nd - 0xDC00);
unicode_codes.push(unicode_code);
} else {
// Malformed surrogate pair ignored.
}
surrogate_1st = 0;
} else if (utf16_code >= 0xD800 && utf16_code <= 0xDBFF) {
surrogate_1st = utf16_code;
} else {
unicode_codes.push(utf16_code);
}
}
return unicode_codes;
}
// 0xff => "ff"
// 0xff => "377"
function formatNumber(number, base, num_digits) {
var str = number.toString(base).toUpperCase();
for (var i = str.length; i < num_digits; ++i) {
str = "0" + str;
}
return str;
}
// convert \xFF\xFF\xFF to unicode
function unescapeFromEscapedBytes (str) {
var data_bytes = convertEscapedBytesToBytes(str);
var unicode_codes = convertUtf8BytesToUnicodeCodePoints(data_bytes);
return convertUnicodeCodePointsToString(unicode_codes);
}
// r'\xE3\x81\x82\xE3\x81\x84' => [ 0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84 ]
// r'\343\201\202\343\201\204' => [ 0343, 0201, 0202, 0343, 0201, 0204 ]
function convertEscapedBytesToBytes(str) {
var parts = str.split("\\x");
parts.shift(); // Trim the first element.
var codes = [];
var max = Math.pow(2, 8);
for (var i = 0; i < parts.length; ++i) {
var code = parseInt(parts[i], 16);
if (code >= 0 && code < max) {
codes.push(code);
} else {
// Malformed code ignored.
}
}
return codes;
}
// [ 0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84 ] => [ 0x3042, 0x3044 ]
function convertUtf8BytesToUnicodeCodePoints(utf8_bytes) {
var unicode_codes = [];
var unicode_code = 0;
var num_followed = 0;
for (var i = 0; i < utf8_bytes.length; ++i) {
var utf8_byte = utf8_bytes[i];
if (utf8_byte >= 0x100) {
// Malformed utf8 byte ignored.
} else if ((utf8_byte & 0xC0) == 0x80) {
if (num_followed > 0) {
unicode_code = (unicode_code << 6) | (utf8_byte & 0x3f);
num_followed -= 1;
} else {
// Malformed UTF-8 sequence ignored.
}
} else {
if (num_followed == 0) {
unicode_codes.push(unicode_code);
} else {
// Malformed UTF-8 sequence ignored.
}
if (utf8_byte < 0x80){ // 1-byte
unicode_code = utf8_byte;
num_followed = 0;
} else if ((utf8_byte & 0xE0) == 0xC0) { // 2-byte
unicode_code = utf8_byte & 0x1f;
num_followed = 1;
} else if ((utf8_byte & 0xF0) == 0xE0) { // 3-byte
unicode_code = utf8_byte & 0x0f;
num_followed = 2;
} else if ((utf8_byte & 0xF8) == 0xF0) { // 4-byte
unicode_code = utf8_byte & 0x07;
num_followed = 3;
} else {
// Malformed UTF-8 sequence ignored.
}
}
}
if (num_followed == 0) {
unicode_codes.push(unicode_code);
} else {
// Malformed UTF-8 sequence ignored.
}
unicode_codes.shift(); // Trim the first element.
return unicode_codes;
}
// [ 0x3042, 0x3044 ] => [ 0x3042, 0x3044 ]
// [ 0xD840, 0xDC0B ] => [ 0x2000B ] // A surrogate pair.
function convertUnicodeCodePointsToUtf16Codes(unicode_codes) {
var utf16_codes = [];
for (var i = 0; i < unicode_codes.length; ++i) {
var unicode_code = unicode_codes[i];
if (unicode_code < (1 << 16)) {
utf16_codes.push(unicode_code);
} else {
var first = ((unicode_code - (1 << 16)) / (1 << 10)) + 0xD800;
var second = (unicode_code % (1 << 10)) + 0xDC00;
utf16_codes.push(first)
utf16_codes.push(second)
}
}
return utf16_codes;
}
// [ 0x3042, 0x3044 ] => "あい"
function convertUnicodeCodePointsToString(unicode_codes) {
var utf16_codes = convertUnicodeCodePointsToUtf16Codes(unicode_codes);
return convertUtf16CodesToString(utf16_codes);
}
// [ 0x3042, 0x3044 ] => "あい"
function convertUtf16CodesToString(utf16_codes) {
var unescaped = '';
for (var i = 0; i < utf16_codes.length; ++i) {
unescaped += String.fromCharCode(utf16_codes[i]);
}
return unescaped;
}
return {
raw: UNICODE_BLOCK_LIST,
lookup: UNICODE_LOOKUP,
index: index,
range: range,
block: block,
findGroups: findGroups,
paginate: paginate,
escapeToEscapedBytes: escapeToEscapedBytes,
unescapeFromEscapedBytes: unescapeFromEscapedBytes,
}
})()

83
js/upload.js Normal file
View File

@ -0,0 +1,83 @@
var upload = (function(){
var el = document.getElementById("upload_input")
var button = document.getElementById("upload_button")
var uploading = false
function upload(blob, filename, tag, ascii){
if (uploading) return
filename = filename || get_filename()
tag = tag || "shader"
button.innerHTML = "uploading..."
button.className = "uploading"
uploading = true
uploadImage({
blob: blob,
ascii: ascii,
filename: filename,
username: user.username,
tag: tag,
success: function(data){
// data.url
// data.filesize
// data.success
console.log(data);
el.style.display = "block"
el.value = data.url
el.focus()
setCaretToPos(el, 0)
button.innerHTML = "upload"
button.className = ""
uploading = false
},
error: function(data){
console.log(data)
console.log("error uploading: " + data.error)
button.innerHTML = "upload"
button.className = ""
uploading = false
}
});
}
function uploadImage(opt){
if (! opt.blob || ! opt.filename) return;
opt.username = opt.username || "";
opt.success = opt.success || noop;
opt.error = opt.error || noop;
var form = new FormData();
form.append("username", opt.username);
form.append("filename", opt.filename);
form.append("qqfile", opt.blob);
form.append("tag", opt.tag);
if (opt.ascii) {
form.append("ascii", opt.ascii);
}
var req = new XMLHttpRequest();
req.open("POST", "/cgi-bin/im/shader/upload");
req.onload = function(event) {
if (req.status == 200) {
var res = JSON.parse(req.responseText);
if (res.success) {
opt.success(res);
}
else {
opt.error(res);
}
} else {
opt.error({ success: false, error: req.status });
}
};
req.send(form);
}
return upload
})()

67
js/user.js Normal file
View File

@ -0,0 +1,67 @@
var user = (function(){
var user = {}
var el = document.getElementById("username_input")
user.init = function(){
user.load()
user.bind()
}
user.bind = function(){
el.addEventListener("input", user.save)
}
user.load = function(){
user.username = user.getCookie()
if (! user.username) {
user.username = '00' + randint(9876876)
user.setCookie(user.username)
}
if (!user.username.match(/^00/)) {
el.value = user.username
}
}
user.prefs = new function(){}
user.prefs.get = function (key){
return localStorage.getItem("im.prefs." + key)
}
user.prefs.set = function (key,value){
return localStorage.setItem("im.prefs." + key, value)
}
user.sanitize = function(){
return el.value.replace(/[^-_ a-zA-Z0-9]/g,"")
}
user.getCookie = function () {
var username = localStorage.getItem("im.name") || "";
if (document.cookie && ! username.length) {
var cookies = document.cookie.split(";")
for (i in cookies) {
var cookie = cookies[i].split("=")
if (cookie[0].indexOf("imname") !== -1) {
if (cookie[1] !== 'false' && cookie[1] !== 'undefined' && cookie[1].length) {
return cookie[1]
}
}
}
}
return username
}
var timeout
user.save = function(){
clearTimeout(timeout)
timeout = setTimeout(function(){
var username = user.sanitize()
if (username != user.username) user.setCookie(username);
})
}
user.setCookie = function(username){
if (!user.username.match(/^00/)) {
console.log("setting to " + username)
}
document.cookie = "imname="+username+";path=/;domain=.asdf.us;max-age=1086400"
localStorage.setItem("im.name", username);
}
user.init()
return user
})()

199
js/util.js Normal file
View File

@ -0,0 +1,199 @@
if (window.$) {
$.fn.int = function(){ return parseInt($(this).val(),10) }
$.fn.float = function(){ return parseFloat($(this).val()) }
$.fn.string = function(){ return trim($(this).val()) }
$.fn.enable = function() { return $(this).attr("disabled",null) }
$.fn.disable = function() { return $(this).attr("disabled","disabled") }
}
function noop(){}
function trim(s){ return s.replace(/^\s+/,"").replace(/\s+$/,"") }
var E = Math.E
var PI = Math.PI
var PHI = (1+Math.sqrt(5))/2
var TWO_PI = PI*2
var LN10 = Math.LN10
function clamp(n,a,b){ return n<a?a:n<b?n:b }
function norm(n,a,b){ return (n-a) / (b-a) }
function lerp(n,a,b){ return (b-a)*n+a }
function mix(n,a,b){ return a*(1-n)+b*n }
function ceil(n){ return Math.ceil(n) }
function floor(n){ return Math.floor(n) }
function round(n){ return Math.round(n) }
function max(a,b){ return Math.max(a,b) }
function min(a,b){ return Math.min(a,b) }
function abs(n){ return Math.abs(n) }
function sign(n){ return Math.abs(n)/n }
function pow(n,b) { return Math.pow(n,b) }
function exp(n) { return Math.exp(n) }
function log(n){ return Math.log(n) }
function ln(n){ return Math.log(n)/LN10 }
function sqrt(n) { return Math.sqrt(n) }
function cos(n){ return Math.cos(n) }
function sin(n){ return Math.sin(n) }
function tan(n){ return Math.tan(n) }
function acos(n){ return Math.cos(n) }
function asin(n){ return Math.sin(n) }
function atan(n){ return Math.atan(n) }
function atan2(a,b){ return Math.atan2(a,b) }
function sec(n){ return 1/cos(n) }
function csc(n){ return 1/sin(n) }
function cot(n){ return 1/tan(n) }
function cosp(n){ return (1+Math.cos(n))/2 } // cos^2
function sinp(n){ return (1+Math.sin(n))/2 }
function random(){ return Math.random() }
function rand(n){ return (Math.random()*n) }
function randint(n){ return rand(n)|0 }
function randrange(a,b){ return a + rand(b-a) }
function randsign(){ return random() >= 0.5 ? -1 : 1 }
function randnullsign(){ var r = random(); return r < 0.333 ? -1 : r < 0.666 ? 0 : 1 }
function xrandom(exp){ return Math.pow(Math.random(), exp) }
function xrand(exp,n){ return (xrandom(exp)*n) }
function xrandint(exp,n){ return rand(exp,n)|0 }
function xrandrange(exp,a,b){ return a + xrand(exp,b-a) }
function choice(a){ return a[randint(a.length)] }
function deg(n){ return n*180/PI }
function rad(n){ return n*PI/180 }
function xor(a,b){ a=!!a; b=!!b; return (a||b) && !(a&&b) }
function mod(n,m){ n = n % m; return n < 0 ? (m + n) : n }
function modi(n,m){ return floor(mod(n,m)) }
function dist(x0,y0,x1,y1){ return sqrt(pow(x1-x0,2)+pow(y1-y0,2)) }
function angle(x0,y0,x1,y1){ return atan2(y1-y0,x1-x0) }
function avg(m,n,a){ return (m*(a-1)+n)/a }
function quantize(a,b){ return floor(a/b)*b }
function quantile(a,b){ return floor(a/b) }
function pixel(x,y){ return 4*(mod(y,actual_h)*actual_w+mod(x,actual_w)) }
function rgbpixel(d,x,y){
var p = pixel(~~x,~~y)
r = d[p]
g = d[p+1]
b = d[p+2]
a = d[p+3]
}
function fit(d,x,y){ rgbpixel(d,x*actual_w/w,y*actual_h/h) }
function step(a, b){
return (b >= a) + 0
// ^^ bool -> int
}
function julestep (a,b,n) {
return clamp(norm(n,a,b), 0.0, 1.0);
}
// hermite curve apparently
function smoothstep(min,max,n){
var t = clamp((n - min) / (max - min), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t)
}
function toArray(a){ return Array.prototype.slice.call(a) }
function shuffle(a){
for (var i = a.length; i > 0; i--){
var r = randint(i)
var swap = a[i-1]
a[i-1] = a[r]
a[r] = swap
}
return a
}
function reverse(a){
var reversed = []
for (var i = 0, _len = a.length-1; i <= _len; i++){
reversed[i] = a[_len-i]
}
return reversed
}
function deinterlace(a){
var odd = [], even = []
for (var i = 0, _len = a.length; i < _len; i++) {
if (i % 2) even.push(a[i])
else odd.push(a[i])
}
return [even, odd]
}
function weave(a){
var aa = deinterlace(a)
var b = []
aa[0].forEach(function(el){ b.push(el) })
reverse(aa[1]).forEach(function(el){ b.push(el) })
return b
}
function cssRule (selector, declaration) {
var x = document.styleSheets, y = x.length-1;
x[y].insertRule(selector+"{"+declaration+"}", x[y].cssRules.length);
}
// easing functions
function circular (t) { return Math.sqrt( 1 - ( --t * t ) ) }
function quadratic (t) { return t * ( 2 - t ) }
function back (t) {
var b = 4;
return ( t = t - 1 ) * t * ( ( b + 1 ) * t + b ) + 1;
}
function bounce (t) {
if (t >= 1) return 1;
if ( ( t /= 1 ) < ( 1 / 2.75 ) ) {
return 7.5625 * t * t;
} else if ( t < ( 2 / 2.75 ) ) {
return 7.5625 * ( t -= ( 1.5 / 2.75 ) ) * t + 0.75;
} else if ( t < ( 2.5 / 2.75 ) ) {
return 7.5625 * ( t -= ( 2.25 / 2.75 ) ) * t + 0.9375;
} else {
return 7.5625 * ( t -= ( 2.625 / 2.75 ) ) * t + 0.984375;
}
}
function elastic (t) {
var f = 0.22,
e = 0.4;
if ( t === 0 ) { return 0; }
if ( t == 1 ) { return 1; }
return ( e * Math.pow( 2, - 10 * t ) * Math.sin( ( t - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
}
Model=function a(b,c,d,e){function f(){var a=this,f={};a.on=function(a,b){(f[a]||
(f[a]=[])).push(b)},a.trigger=function(a,b){for(var c=f[a],d=0;c&&d<c.length;)c
[d++](b)},a.off=function(a,b){for(d=f[a]||[];b&&(c=d.indexOf(b))>-1;)d.splice(c
,1);f[a]=b?d:[]};for(c in b)d=b[c],a[c]=typeof d=="function"?function(){return(
d=this.apply(a,arguments))===e?a:d}.bind(d):d;a.init&&a.init.apply(a,arguments)
}return f.extend=function(f){d={};for(c in b)d[c]=b[c];for(c in f)d[c]=f[c],b[c
]!==e&&(d["__"+c]=b[c]);return a(d)},f},typeof module=="object"&&(module.exports
=Model); // c-{{{-<
function defaults (dest, src) {
dest = dest || {}
for (var i in src) {
dest[i] = typeof dest[i] == 'undefined' ? src[i] : dest[i]
}
return dest
}
function setSelectionRange(input, selectionStart, selectionEnd) {
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
}
else if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', selectionEnd);
range.moveStart('character', selectionStart);
range.select();
}
}
function setCaretToPos(input, pos) {
setSelectionRange(input, pos, pos);
}
// Naive useragent detection pattern
var is_iphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))
var is_ipad = (navigator.userAgent.match(/iPad/i))
var is_android = (navigator.userAgent.match(/Android/i))
var is_mobile = is_iphone || is_ipad || is_android
var is_desktop = ! is_mobile;

232
js/vendor/FileSaver.js vendored Normal file
View File

@ -0,0 +1,232 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 2013-10-21
*
* By Eli Grey, http://eligrey.com
* License: X11/MIT
* See LICENSE.md
*/
/*global self */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs
|| (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
|| (function(view) {
"use strict";
var
doc = view.document
// only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, URL = view.URL || view.webkitURL || view
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = !view.externalHost && "download" in save_link
, click = function(node) {
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
node.dispatchEvent(event);
}
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function (ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
, deletion_queue = []
, process_deletion_queue = function() {
var i = deletion_queue.length;
while (i--) {
var file = deletion_queue[i];
if (typeof file === "string") { // file is an object URL
URL.revokeObjectURL(file);
} else { // file is a File
file.remove();
}
}
deletion_queue.length = 0; // clear queue
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, FileSaver = function(blob, name) {
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, get_object_url = function() {
var object_url = get_URL().createObjectURL(blob);
deletion_queue.push(object_url);
return object_url;
}
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_object_url(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
window.open(object_url, "_blank");
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
}
, abortable = function(func) {
return function() {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = {create: true, exclusive: false}
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_object_url(blob);
// FF for Android has a nasty garbage collection mechanism
// that turns all objects that are not pure javascript into 'deadObject'
// this means `doc` and `save_link` are unusable and need to be recreated
// `view` is usable though:
doc = view.document;
save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a");
save_link.href = object_url;
save_link.download = name;
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
save_link.dispatchEvent(event);
filesaver.readyState = filesaver.DONE;
dispatch_all();
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
var save = function() {
dir.getFile(name, create_if_not_found, abortable(function(file) {
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
deletion_queue.push(file);
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
};
writer.onerror = function() {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function(event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function() {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, {create: false}, abortable(function(file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function(ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name) {
return new FileSaver(blob, name);
}
;
FS_proto.abort = function() {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
view.addEventListener("unload", process_deletion_queue, false);
return saveAs;
}(this.self || this.window || this.content));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== 'undefined') module.exports = saveAs;

551
js/vendor/colorcode.js vendored Normal file
View File

@ -0,0 +1,551 @@
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.colorcode=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var colorcode = {};
module.exports = colorcode;
colorcode.to_json = require('./src/to_json');
colorcode.from_json = require('./src/from_json');
colorcode.style = require('./src/style');
colorcode.to_canvas = require('./src/canvas');
colorcode.color = require('./src/color');
colorcode.font = require('./src/font');
},{"./src/canvas":2,"./src/color":3,"./src/font":4,"./src/from_json":8,"./src/style":9,"./src/to_json":10}],2:[function(require,module,exports){
var to_json = require('./to_json');
var fontload = require('./font').load;
var style = require('./style');
var color = require('./color');
// node-canvas
var Canvas = require('canvas');
if (typeof Image === "undefined") Image = Canvas.Image;
var make_canvas = function(){
if (typeof document === "undefined" && typeof Canvas !== "undefined")
return new Canvas();
else
return document.createElement("canvas");
}
var canvas_tmp;
var render_colorcode = function(json, canvas, font, opts){
var cw = font.char_w
, ch = font.char_h
, ctx = canvas.getContext('2d')
, canvas_tmp = canvas_tmp || make_canvas()
, ctx_tmp = canvas_tmp.getContext("2d")
var palette = color.palettes[opts.palette || style.palette];
var bg = opts.bg || style.bg;
canvas_tmp.width = cw;
canvas_tmp.height = ch;
canvas.width = json.w * cw;
canvas.height = json.h * ch;
// pre fill entire canvas with bg color
// is this a good optimization?
if (bg === color.transparent_index){
// already cleared when resized above
// canvas.clearRect(0,0, canvas.width,canvas.height);
} else {
ctx.fillStyle = palette[bg];
ctx.fillRect(0,0, canvas.width,canvas.height);
}
for (var l=0; l<json.lines.length; l++){
var line = json.lines[l];
for (var c=0; c<line.length; c++){
var char = line[c];
var x = c * cw
var y = l * ch
// draw bg for this char if not already filled
if (char.bg !== bg) {
if (char.bg === color.transparent_index) {
ctx.clearRect(x, y, cw, ch)
} else {
ctx.fillStyle = palette[char.bg]
ctx.fillRect(x, y, cw, ch);
}
}
if (font.is_char_blank(char.value)) continue;
// draw char in fg
var fg = palette[char.fg]
if (fg !== color.transparent){
ctx_tmp.globalCompositeOperation = 'source-over'
ctx_tmp.fillStyle = fg
ctx_tmp.fillRect(0,0,cw,ch)
ctx_tmp.globalCompositeOperation = 'destination-in'
font.render_char(font, char.value, ctx_tmp, 0, 0, char)
ctx.drawImage(canvas_tmp, x, y)
} else { // transparent foreground punches out bg
ctx.globalCompositeOperation = 'destination-out'
font.render_char(font, char.value, ctx, x, y, char)
ctx.globalCompositeOperation = 'source-over'
}
}
}
if (opts.done) opts.done(canvas)
}
var to_canvas = function(string_or_json, opts){
opts = opts || {};
if (typeof string_or_json === 'string')
string_or_json = to_json(string_or_json, opts);
var canvas = opts.canvas || make_canvas();
var font_name = opts.font || style.font;
fontload(font_name, function(font){
render_colorcode(string_or_json, canvas, font, opts)
});
return canvas;
}
module.exports = to_canvas;
},{"./color":3,"./font":4,"./style":9,"./to_json":10,"canvas":11}],3:[function(require,module,exports){
var style = require('./style');
var color = {};
module.exports = color;
style.palette = 'mirc';
color.transparent_index = 99;
color.transparent = 'rgba(0,0,0,0)';
var ps = color.palettes = {};
ps.mirc = [
'rgb(255,255,255)'
,'rgb(0,0,0)'
,'rgb(0,0,127)'
,'rgb(0,147,0)'
,'rgb(255,0,0)'
,'rgb(127,0,0)'
,'rgb(156,0,156)'
,'rgb(252,127,0)'
,'rgb(255,255,0)'
,'rgb(0,252,0)'
,'rgb(0,147,147)'
,'rgb(0,255,255)'
,'rgb(0,0,252)'
,'rgb(255,0,255)'
,'rgb(127,127,127)'
,'rgb(210,210,210)'
];
ps.winxp = [
'rgb(255,255,255)'
,'rgb(0,0,0)'
,'rgb(0,0,128)'
,'rgb(0,128,0)'
,'rgb(255,0,0)'
,'rgb(128,0,0)'
,'rgb(128,0,128)'
,'rgb(255,128,0)'
,'rgb(255,255,0)'
,'rgb(0,255,0)'
,'rgb(0,128,128)'
,'rgb(0,255,255)'
,'rgb(0,0,255)'
,'rgb(255,0,255)'
,'rgb(128,128,128)'
,'rgb(192,192,192)'
];
ps.vga = [
'rgb(255,255,255)'
,'rgb(0,0,0)'
,'rgb(0,0,170)'
,'rgb(0,170,0)'
,'rgb(255,85,85)'
,'rgb(170,0,0)'
,'rgb(170,0,170)'
,'rgb(170,85,0)'
,'rgb(255,255,85)'
,'rgb(85,255,85)'
,'rgb(0,170,170)'
,'rgb(85,255,255)'
,'rgb(85,85,255)'
,'rgb(255,85,255)'
,'rgb(85,85,85)'
,'rgb(170,170,170)'
];
ps.c64 = [
'rgb(255,255,255)'
,'rgb(0,0,0)'
,'rgb(69,32,170)'
,'rgb(101,170,69)'
,'rgb(138,101,32)'
,'rgb(138,69,32)'
,'rgb(138,69,170)'
,'rgb(101,69,0)'
,'rgb(207,207,101)'
,'rgb(170,239,138)'
,'rgb(138,138,138)'
,'rgb(101,170,207)'
,'rgb(138,101,223)'
,'rgb(207,138,101)'
,'rgb(69,69,69)'
,'rgb(170,170,170)'
];
ps.appleii = [
'rgb(255,255,255)'
,'rgb(0,0,0)'
,'rgb(64,53,121)'
,'rgb(64,75,7)'
,'rgb(191,180,248)'
,'rgb(109,41,64)'
,'rgb(218,60,241)'
,'rgb(218,104,15)'
,'rgb(191,202,134)'
,'rgb(38,195,16)'
,'rgb(19,87,64)'
,'rgb(146,214,191)'
,'rgb(37,151,240)'
,'rgb(236,168,191)'
,'rgb(128,128,128)'
,'rgb(128,128,128)'
];
},{"./style":9}],4:[function(require,module,exports){
var __dirname="/src";var style = require('./style');
// node-canvas
var Canvas = require('canvas');
if (typeof Image === "undefined") Image = Canvas.Image;
var font = {};
module.exports = font;
// hack for loading fonts in node... todo, fix this
font.img_path = "";
if (typeof document === "undefined") font.img_path = __dirname + "/../examples/web/"
font.list = {};
var fsexps = require('./font/fixedsys');
var cp437s = require('./font/cp437');
for (f in fsexps) font.list[fsexps[f].name] = fsexps[f];
for (f in cp437s) font.list[cp437s[f].name] = cp437s[f];
style.font = 'fixedsys_8x16';
var err_font_load = function(){
console.log("couldn't load font")
}
font.load = function(font_name, callback_fn){
if (!(font_name in font.list)) { return;} // todo error
var f = font.list[font_name]
if (f.loaded) {
callback_fn(f);
} else {
f.sheet = new Image();
f.sheet.crossOrigin = 'anonymous'
// node-canvas doesn't have addEventListener :(
f.sheet.onload = function(){
f.loaded = true
callback_fn(f);
}
f.sheet.src = font.img_path + f.sheet_url
}
}
},{"./font/cp437":5,"./font/fixedsys":6,"./style":9,"canvas":11}],5:[function(require,module,exports){
var cp437s = [[8,8],[8,12],[8,14],[8,16],[10,10],[10,16],[12,12],[16,16]]
var fonts = {};
module.exports = fonts;
// utf8 -> cp437 function by sheetjs
// edited from https://github.com/SheetJS/js-codepage/blob/master/bits/437.js
var cp437 = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d[i]] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
var render_char = function(font, char_value, ctx, ctx_x, ctx_y){
char_value = cp437.enc[String.fromCharCode(char_value)] | 0;
var sheet_x = (char_value % font.sheet_w_in_chars) * font.char_w
var sheet_y = ((char_value / font.sheet_w_in_chars) |0) * font.char_h
ctx.drawImage(font.sheet,
sheet_x|0, sheet_y|0, font.char_w, font.char_h,
ctx_x|0, ctx_y|0, font.char_w, font.char_h)
}
for (var i=0, wh; wh=cp437s[i]; i++){
var font = {};
font.is_char_blank = require('../fontutil').is_char_blank;
font.render_char = render_char;
font.name = 'cp437_' + wh[0] + 'x' + wh[1];
font.sheet_url = './img/' + font.name + '.png'
font.sheet_w_in_chars = 16;
font.char_w = wh[0]
font.char_h = wh[1]
fonts[font.name] = font;
}
// window.cp437 = cp437;
},{"../fontutil":7}],6:[function(require,module,exports){
var fsexps = [[8,16,0],[8,15,1],[8,8,5]]
var fonts = {};
module.exports = fonts;
var render_char = function(font, char_value, ctx, ctx_x, ctx_y, char){
var sheet_x = 0, sheet_y = 3;
if (char_value >= 0x20 && char_value <= 0x7e){ // ascii
sheet_x = (char_value - 0x20) * font.char_w_sheet
if (char.i){ // italic
sheet_y = 1 * font.char_h_sheet + 3
}
} else if (char_value >= 0x80 && char_value <= 0xff){ // latin-1
sheet_x = (char_value - 0x80) * font.char_w_sheet;
sheet_y = 2 * font.char_h_sheet + 3
} else if (char_value >= 0x0100 && char_value <= 0x017f){ // latin a
sheet_x = (char_value - 0x0100) * font.char_w_sheet;
sheet_y = 3 * font.char_h_sheet + 3
} else if (char_value >= 0x0180 && char_value <= 0x024f){ // latin b
sheet_x = (char_value - 0x0180) * font.char_w_sheet;
sheet_y = 4 * font.char_h_sheet + 3
} else if (char_value >= 0x2500 && char_value <= 0x25ff){ // geom
sheet_x = (char_value - 0x2500) * font.char_w_sheet;
sheet_y = 5 * font.char_h_sheet + 3
} else if (char_value >= 0x2600 && char_value <= 0x26ff){ // emoji
sheet_x = (char_value - 0x2600) * font.char_w_sheet;
sheet_y = 6 * font.char_h_sheet + 3
}
// var sheet_x = (char_value % font.sheet_w_in_chars) * font.char_w
// var sheet_y = ((char_value / font.sheet_w_in_chars) |0) * font.char_h + 3
ctx.drawImage(font.sheet,
sheet_x|0, (sheet_y|0) + font.y_adj, font.char_w, font.char_h,
ctx_x|0, ctx_y|0, font.char_w, font.char_h)
}
for (var i=0, wh; wh=fsexps[i]; i++){
var font = {
name: 'fixedsys_' + wh[0] + 'x' + wh[1],
sheet_url: './img/fsex-simple.png',
sheet_w_in_chars: 128,
char_w_sheet: 8,
char_h_sheet: 16,
char_w: wh[0],
char_h: wh[1],
y_adj: wh[2],
is_char_blank: require('../fontutil').is_char_blank,
render_char: render_char
}
fonts[font.name] = font
}
},{"../fontutil":7}],7:[function(require,module,exports){
var util = {};
module.exports = util;
util.is_char_blank = function(char_value){
if (char_value === 32) return true;
}
util.render_char = function(font, char_value, ctx, ctx_x, ctx_y){
var sheet_x = (char_value % font.sheet_w_in_chars) * font.char_w
var sheet_y = ((char_value / font.sheet_w_in_chars) |0) * font.char_h
ctx.drawImage(font.sheet,
sheet_x|0, sheet_y|0, font.char_w, font.char_h,
ctx_x|0, ctx_y|0, font.char_w, font.char_h)
}
},{}],8:[function(require,module,exports){
var char_color = '\x03';
var make_colorcode_fgbg = function(fg, bg){
// pad numbers: this prevents irc parsing confusion
// when the character after the colorcode is a number
if (fg < 10) fg = "0" + fg;
if (bg < 10) bg = "0" + bg;
return char_color + fg + "," + bg
}
var colorcode_from_json = function(json, opts){
var out = "";
for (var li=0, line; line=json.lines[li]; li++){
for (var ci=0, char; char=line[ci]; ci++){
out += make_colorcode_fgbg(char.fg, char.bg)
out += String.fromCharCode(char.value)
}
out += "\n";
}
return out;
}
module.exports = colorcode_from_json;
},{}],9:[function(require,module,exports){
// default settings for fonts, colors, etc
var style = {};
module.exports = style;
},{}],10:[function(require,module,exports){
var char_color = '\x03';
var regexp_color = /(^[\d]{1,2})?(?:,([\d]{1,2}))?/;
var style_chars = {
'\x02': 'bold',
'\x1d': 'italic',
'\x1f': 'underline',
'\x0f': 'reset',
'\x16': 'inverse'
};
var Style = function(style){
this.b = style.b;
this.i = style.i;
this.u = style.u;
this.fg = style.fg;
this.bg = style.bg;
};
var style_fns = {};
style_fns.bold = function(style){ style.b = !style.b };
style_fns.italic = function(style){ style.i = !style.i };
style_fns.underline = function(style){ style.u = !style.u };
style_fns.inverse = function(style){
var tmp = style.fg;
style.fg = style.bg;
style.bg = tmp;
};
style_fns.reset = function(style, base_style){
style.b = base_style.b;
style.i = base_style.i;
style.u = base_style.u;
style.fg = base_style.fg;
style.bg = base_style.bg;
};
var colorcode_to_json = function(string, opts){
// looks like its already converted
if (typeof string === 'object' &&
'lines' in string &&
'w' in string &&
'h' in string)
return string;
opts = opts || {};
var d = colorcode_to_json.defaults;
var base_style = {
b: "b" in opts ? opts.b : d.b,
i: "i" in opts ? opts.i : d.i,
u: "u" in opts ? opts.u : d.u,
fg: "fg" in opts ? opts.fg : d.fg,
bg: "bg" in opts ? opts.bg : d.bg
};
var lines_in = string.split(/\r?\n/);
var lines_out = [];
var w = 0, h = 0;
for (var i=0; i<lines_in.length; i++){
var line = lines_in[i];
if (line.length === 0) continue; // skip blank lines
var json_line = line_to_json(line, base_style);
if (w < json_line.length) w = json_line.length;
lines_out.push(json_line);
h++;
}
return {w:w, h:h, lines:lines_out};
};
colorcode_to_json.defaults = {
b: false
, i: false
, u: false
, fg: 1
, bg: 99
};
var line_to_json = function(line, base_style){
var out = [];
var pos = -1;
var len = line.length -1;
var char;
var style = new Style(base_style);
while (pos < len){ pos++;
char = line[pos];
// next char is a styling char
if (char in style_chars){
style_fns[style_chars[char]](style, base_style);
continue;
}
// next char is a color styling char, with possible color nums after
if (char === char_color){
var matches = line.substr(pos+1,5).match(regexp_color);
// \x03 without color code is a soft style reset
if (matches[1] === undefined && matches[2] === undefined) {
style.fg = base_style.fg;
style.bg = base_style.bg;
continue;
}
if (matches[1] !== undefined)
style.fg = Number(matches[1]);
if (matches[2] !== undefined)
style.bg = Number(matches[2]);
pos += matches[0].length;
continue;
}
// otherwise, next char is treated as normal content
var data = new Style(style);
//data.value = char;
data.value = char.charCodeAt(0);
out.push(data);
}
return out;
};
module.exports = colorcode_to_json;
},{}],11:[function(require,module,exports){
},{}]},{},[1])
(1)
});

58
js/vendor/dataUriToBlob.js vendored Normal file
View File

@ -0,0 +1,58 @@
var dataUriToUint8Array = function(uri){
var data = uri.split(',')[1];
var bytes = atob(data);
var buf = new ArrayBuffer(bytes.length);
var u8 = new Uint8Array(buf);
for (var i = 0; i < bytes.length; i++) {
u8[i] = bytes.charCodeAt(i);
}
return u8
}
window.dataUriToBlob = (function(){
/**
* Blob constructor.
*/
var Blob = window.Blob;
/**
* ArrayBufferView support.
*/
var hasArrayBufferView = new Blob([new Uint8Array(100)]).size == 100;
/**
* Return a `Blob` for the given data `uri`.
*
* @param {String} uri
* @return {Blob}
* @api public
*/
var dataUriToBlob = function(uri){
var data = uri.split(',')[1];
var bytes = atob(data);
var buf = new ArrayBuffer(bytes.length);
var arr = new Uint8Array(buf);
for (var i = 0; i < bytes.length; i++) {
arr[i] = bytes.charCodeAt(i);
}
if (!hasArrayBufferView) arr = buf;
var blob = new Blob([arr], { type: mime(uri) });
blob.slice = blob.slice || blob.webkitSlice;
return blob;
};
/**
* Return data uri mime type.
*/
function mime(uri) {
return uri.split(';')[0].slice(5);
}
return dataUriToBlob;
})()

167
js/vendor/oktween.js vendored Normal file
View File

@ -0,0 +1,167 @@
/*
oktween.add({
obj: el.style,
units: "px",
from: { left: 0 },
to: { left: 100 },
duration: 1000,
easing: oktween.easing.circ_out,
update: function(obj){
console.log(obj.left)
}
finished: function(){
console.log("done")
}
})
*/
var oktween = (function(){
var oktween = {}
var tweens = oktween.tweens = []
var last_t = 0
var id = 0
oktween.speed = 1
oktween.raf = requestAnimationFrame
oktween.add = function(tween){
tween.id = id++
tween.obj = tween.obj || {}
if (tween.easing) {
if (typeof tween.easing == "string") {
tween.easing = oktween.easing[tween.easing]
}
}
else {
tween.easing = oktween.easing.linear
}
if (! ('from' in tween) && ! ('to' in tween)) {
tween.keys = []
}
else if (! ('from' in tween) ) {
tween.from = {}
tween.keys = Object.keys(tween.to)
tween.keys.forEach(function(prop){
tween.from[prop] = parseFloat(tween.obj[prop])
})
}
else {
tween.keys = Object.keys(tween.from)
}
tween.delay = tween.delay || 0
tween.start = last_t + tween.delay
tween.done = false
tween.after = tween.after || []
tween.then = function(fn){ tween.after.push(fn); return tween }
tween.cancel = function(){
var index = tweens.indexOf(tween)
if (index != -1) tweens.splice(index, 1)
tween.obj = null
tween.after = null
tween.done = null
}
tween.tick = 0
tween.skip = tween.skip || 1
tween.dt = 0
tweens.push(tween)
return tween
}
oktween.update = function(t) {
oktween.raf(oktween.update)
last_t = t * oktween.speed
if (tweens.length == 0) return
var done = false
tweens.forEach(function(tween, i){
var dt = Math.min(1.0, (t - tween.start) / tween.duration)
tween.tick++
if (dt < 0 || (dt < 1 && (tween.tick % tween.skip != 0))) return
var ddt = tween.dt = tween.easing(dt)
tween.keys.forEach(function(prop){
val = lerp( ddt, tween.from[prop], tween.to[prop] )
if (tween.round) val = Math.round(val)
if (tween.units) val = (Math.round(val)) + tween.units
tween.obj[prop] = val
})
tween.update && tween.update(tween.obj, dt)
if (dt == 1) {
tween.finished && tween.finished(tween)
if (tween.after.length) {
var twn = tween.after.shift()
twn.obj = twn.obj || tween.obj
twn.after = tween.after
oktween.add(twn)
}
if (tween.loop) {
tween.start = t + tween.delay
}
else {
done = tween.done = true
}
}
})
if (done) {
tweens = tweens.filter(function(tween){ return ! tween.done })
}
}
function lerp(n,a,b){ return (b-a)*n+a }
// requestAnimationFrame(oktween.update)
oktween.easing = {
linear: function(t){
return t
},
circ_out: function(t) {
return Math.sqrt(1 - (t = t - 1) * t)
},
circ_in: function(t){
return -(Math.sqrt(1 - (t * t)) - 1)
},
circ_in_out: function(t) {
return ((t*=2) < 1) ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1)
},
quad_in: function(n){
return Math.pow(n, 2)
},
quad_out: function(n){
return n * (n - 2) * -1
},
quad_in_out: function(n){
n = n * 2
if(n < 1){ return Math.pow(n, 2) / 2 }
return -1 * ((--n) * (n - 2) - 1) / 2
},
cubic_bezier: function (mX1, mY1, mX2, mY2) {
function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C(aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function CalcBezier(aT, aA1, aA2) {
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function GetSlope(aT, aA1, aA2) {
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
}
function GetTForX(aX) {
// Newton raphson iteration
var aGuessT = aX;
for (var i = 0; i < 10; ++i) {
var currentSlope = GetSlope(aGuessT, mX1, mX2);
if (currentSlope == 0.0) return aGuessT;
var currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
return function(aX) {
if (mX1 == mY1 && mX2 == mY2) return aX; // linear
return CalcBezier(aX, mY1, mY2);
}
}
}
return oktween
})()

141
js/vendor/text-encoder-lite.js vendored Normal file
View File

@ -0,0 +1,141 @@
// taken from https://github.com/coolaj86/TextEncoderLite/blob/master/index.js
// added polyfill at bottom
function TextEncoderLite() {
}
function TextDecoderLite() {
}
(function () {
'use strict';
// Taken from https://github.com/feross/buffer/blob/master/index.js
// Thanks Feross et al! :-)
function utf8ToBytes (string, units) {
units = units || Infinity
var codePoint
var length = string.length
var leadSurrogate = null
var bytes = []
var i = 0
for (; i < length; i++) {
codePoint = string.charCodeAt(i)
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
} else {
// valid surrogate pair
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
leadSurrogate = null
}
} else {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else if (i + 1 === length) {
// unpaired lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else {
// valid lead
leadSurrogate = codePoint
continue
}
}
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = null
}
// encode utf8
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
bytes.push(codePoint)
} else if (codePoint < 0x800) {
if ((units -= 2) < 0) break
bytes.push(
codePoint >> 0x6 | 0xC0,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x10000) {
if ((units -= 3) < 0) break
bytes.push(
codePoint >> 0xC | 0xE0,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x200000) {
if ((units -= 4) < 0) break
bytes.push(
codePoint >> 0x12 | 0xF0,
codePoint >> 0xC & 0x3F | 0x80,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else {
throw new Error('Invalid code point')
}
}
return bytes
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end || Infinity)
start = start || 0;
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return res + decodeUtf8Char(tmp)
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
TextEncoderLite.prototype.encode = function (str) {
var result;
if ('undefined' === typeof Uint8Array) {
result = utf8ToBytes(str);
} else {
result = new Uint8Array(utf8ToBytes(str));
}
return result;
};
TextDecoderLite.prototype.decode = function (bytes) {
return utf8Slice(bytes, 0, bytes.length);
}
}());
if (typeof TextEncoder === 'undefined') TextEncoder = TextEncoderLite
if (typeof TextDecoder === 'undefined') TextDecoder = TextDecoderLite