UNPKG

5.98 kBJavaScriptView Raw
1/**
2 * ansi-viewer
3 * ANSI art viewer for node.
4 * Copyright (c) 2015, Christopher Jeffrey and contributors (MIT License).
5 * https://github.com/chjj/blessed
6 */
7
8var blessed = require('blessed')
9 , request = require('request')
10 , singlebyte = require('./singlebyte')
11 , fs = require('fs');
12
13// $ wget -r -o log --tries=10 'http://artscene.textfiles.com/ansi/'
14// $ grep 'http.*\.ans$' log | awk '{ print $3 }' > ansi-art.list
15
16var urls = fs.readFileSync(__dirname + '/ansi-art.list', 'utf8').trim().split('\n');
17
18var map = urls.reduce(function(map, url) {
19 map[/([^.\/]+\/[^.\/]+)\.ans$/.exec(url)[1]] = url;
20 return map;
21}, {});
22
23var max = Object.keys(map).reduce(function(out, text) {
24 return Math.max(out, text.length);
25}, 0) + 6;
26
27var screen = blessed.screen({
28 smartCSR: true,
29 dockBorders: true
30});
31
32var art = blessed.terminal({
33 parent: screen,
34 left: 0,
35 top: 0,
36 height: 60,
37 // some are 78/80, some are 80/82
38 width: 82,
39 border: 'line',
40 tags: true,
41 label: ' {bold}{cyan-fg}ANSI Art{/cyan-fg}{/bold} (Drag Me) ',
42 handler: function() {},
43 draggable: true
44});
45
46var list = blessed.list({
47 parent: screen,
48 label: ' {bold}{cyan-fg}Art List{/cyan-fg}{/bold} (Drag Me) ',
49 tags: true,
50 draggable: true,
51 top: 0,
52 right: 0,
53 width: max,
54 height: '50%',
55 keys: true,
56 vi: true,
57 mouse: true,
58 border: 'line',
59 scrollbar: {
60 ch: ' ',
61 track: {
62 bg: 'cyan'
63 },
64 style: {
65 inverse: true
66 }
67 },
68 style: {
69 item: {
70 hover: {
71 bg: 'blue'
72 }
73 },
74 selected: {
75 bg: 'blue',
76 bold: true
77 }
78 },
79 search: function(callback) {
80 prompt.input('Search:', '', function(err, value) {
81 if (err) return;
82 return callback(null, value);
83 });
84 }
85});
86
87var status = blessed.box({
88 parent: screen,
89 bottom: 0,
90 right: 0,
91 height: 1,
92 width: 'shrink',
93 style: {
94 bg: 'blue'
95 },
96 content: 'Select your piece of ANSI art (`/` to search).'
97});
98
99var loader = blessed.loading({
100 parent: screen,
101 top: 'center',
102 left: 'center',
103 height: 5,
104 align: 'center',
105 width: '50%',
106 tags: true,
107 hidden: true,
108 border: 'line'
109});
110
111var msg = blessed.message({
112 parent: screen,
113 top: 'center',
114 left: 'center',
115 height: 'shrink',
116 width: '50%',
117 align: 'center',
118 tags: true,
119 hidden: true,
120 border: 'line'
121});
122
123var prompt = blessed.prompt({
124 parent: screen,
125 top: 'center',
126 left: 'center',
127 height: 'shrink',
128 width: 'shrink',
129 keys: true,
130 vi: true,
131 mouse: true,
132 tags: true,
133 border: 'line',
134 hidden: true
135});
136
137list.setItems(Object.keys(map));
138
139list.on('select', function(el, selected) {
140 if (list._.rendering) return;
141
142 var name = el.getText();
143 var url = map[name];
144
145 status.setContent(url);
146
147 list._.rendering = true;
148 loader.load('Loading...');
149
150 request({
151 uri: url,
152 encoding: null
153 }, function(err, res, body) {
154 list._.rendering = false;
155 loader.stop();
156
157 if (err) {
158 return msg.error(err.message);
159 }
160
161 if (!body) {
162 return msg.error('No body.');
163 }
164
165 return cp437ToUtf8(body, function(err, body) {
166 if (err) {
167 return msg.error(err.message);
168 }
169
170 if (process.argv[2] === '--debug') {
171 var filename = name.replace(/\//g, '.') + '.ans';
172 fs.writeFileSync(__dirname + '/' + filename, body);
173 }
174
175 // Remove text:
176 body = body.replace('Downloaded From P-80 International Information Systems 304-744-2253', '');
177
178 // Remove MCI codes:
179 body = body.replace(/%[A-Z0-9]{2}/g, '');
180
181 // ^A (SOH) seems to need to produce CRLF in some cases??
182 // body = body.replace(/\x01/g, '\r\n');
183
184 // Reset and write the art:
185 art.term.reset();
186 art.term.write(body);
187 art.term.cursorHidden = true;
188
189 screen.render();
190
191 if (process.argv[2] === '--debug' || process.argv[2] === '--save') {
192 takeScreenshot(name);
193 }
194 });
195 });
196});
197
198list.items.forEach(function(item, i) {
199 var text = item.getText();
200 item.setHover(map[text]);
201});
202
203list.focus();
204list.enterSelected(0);
205
206screen.key('h', function() {
207 list.toggle();
208 if (list.visible) list.focus();
209});
210
211screen.key('r', function() {
212 shuffle();
213});
214
215screen.key('S-s', function() {
216 takeScreenshot(list.ritems[list.selected]);
217});
218
219screen.key('s', function() {
220 slideshow();
221});
222
223screen.key('q', function() {
224 return process.exit(0);
225});
226
227screen.render();
228
229/**
230 * Helpers
231 */
232
233// https://github.com/chjj/blessed/issues/127
234// https://github.com/Mithgol/node-singlebyte
235
236function cp437ToUtf8(buf, callback) {
237 try {
238 return callback(null, singlebyte.bufToStr(buf, 'cp437'));
239 } catch (e) {
240 return callback(e);
241 }
242}
243
244// Animating ANSI art doesn't work for screenshots.
245var ANIMATING = [
246 'bbs/void3',
247 'holiday/xmasfwks',
248 'unsorted/diver',
249 'unsorted/mash-chp',
250 'unsorted/ryans47',
251 'unsorted/xmasfwks'
252];
253
254function takeScreenshot(name) {
255 var filename = name.replace(/\//g, '.') + '.ans.sgr';
256 var image;
257 // Animating art hangs terminal during screenshot as of right now.
258 if (~ANIMATING.indexOf(name)) {
259 image = blessed.element.prototype.screenshot.call(art,
260 0 - art.ileft, art.width - art.iright,
261 0 - art.itop, art.height - art.ibottom);
262 } else {
263 image = art.screenshot();
264 }
265 fs.writeFileSync(__dirname + '/' + filename, image);
266 msg.display('Screenshot taken.');
267}
268
269function slideshow() {
270 if (!screen._.slideshow) {
271 screen._.slideshow = setInterval(function slide() {
272 if (screen.lockKeys) return;
273 var i = (list.items.length - 1) * Math.random() | 0;
274 list.enterSelected(i);
275 return slide;
276 }(), 3000);
277 msg.display('Slideshow started.');
278 } else {
279 clearInterval(screen._.slideshow);
280 delete screen._.slideshow;
281 msg.display('Slideshow stopped.');
282 }
283}
284
285function shuffle() {
286 var items = Object.keys(map).sort(function(key) {
287 return Math.random() > 0.5 ? 1 : -1;
288 });
289 list.setItems(items);
290 screen.render();
291 msg.display('Shuffled items.');
292}