123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /**
- * ansi-viewer
- * ANSI art viewer for node.
- * Copyright (c) 2015, Christopher Jeffrey and contributors (MIT License).
- * https://github.com/chjj/blessed
- */
- var blessed = require('blessed')
- , request = require('request')
- , singlebyte = require('./singlebyte')
- , fs = require('fs');
- // $ wget -r -o log --tries=10 'http://artscene.textfiles.com/ansi/'
- // $ grep 'http.*\.ans$' log | awk '{ print $3 }' > ansi-art.list
- var urls = fs.readFileSync(__dirname + '/ansi-art.list', 'utf8').trim().split('\n');
- var map = urls.reduce(function(map, url) {
- map[/([^.\/]+\/[^.\/]+)\.ans$/.exec(url)[1]] = url;
- return map;
- }, {});
- var max = Object.keys(map).reduce(function(out, text) {
- return Math.max(out, text.length);
- }, 0) + 6;
- var screen = blessed.screen({
- smartCSR: true,
- dockBorders: true
- });
- var art = blessed.terminal({
- parent: screen,
- left: 0,
- top: 0,
- height: 60,
- // some are 78/80, some are 80/82
- width: 82,
- border: 'line',
- tags: true,
- label: ' {bold}{cyan-fg}ANSI Art{/cyan-fg}{/bold} (Drag Me) ',
- handler: function() {},
- draggable: true
- });
- var list = blessed.list({
- parent: screen,
- label: ' {bold}{cyan-fg}Art List{/cyan-fg}{/bold} (Drag Me) ',
- tags: true,
- draggable: true,
- top: 0,
- right: 0,
- width: max,
- height: '50%',
- keys: true,
- vi: true,
- mouse: true,
- border: 'line',
- scrollbar: {
- ch: ' ',
- track: {
- bg: 'cyan'
- },
- style: {
- inverse: true
- }
- },
- style: {
- item: {
- hover: {
- bg: 'blue'
- }
- },
- selected: {
- bg: 'blue',
- bold: true
- }
- },
- search: function(callback) {
- prompt.input('Search:', '', function(err, value) {
- if (err) return;
- return callback(null, value);
- });
- }
- });
- var status = blessed.box({
- parent: screen,
- bottom: 0,
- right: 0,
- height: 1,
- width: 'shrink',
- style: {
- bg: 'blue'
- },
- content: 'Select your piece of ANSI art (`/` to search).'
- });
- var loader = blessed.loading({
- parent: screen,
- top: 'center',
- left: 'center',
- height: 5,
- align: 'center',
- width: '50%',
- tags: true,
- hidden: true,
- border: 'line'
- });
- var msg = blessed.message({
- parent: screen,
- top: 'center',
- left: 'center',
- height: 'shrink',
- width: '50%',
- align: 'center',
- tags: true,
- hidden: true,
- border: 'line'
- });
- var prompt = blessed.prompt({
- parent: screen,
- top: 'center',
- left: 'center',
- height: 'shrink',
- width: 'shrink',
- keys: true,
- vi: true,
- mouse: true,
- tags: true,
- border: 'line',
- hidden: true
- });
- list.setItems(Object.keys(map));
- list.on('select', function(el, selected) {
- if (list._.rendering) return;
- var name = el.getText();
- var url = map[name];
- status.setContent(url);
- list._.rendering = true;
- loader.load('Loading...');
- request({
- uri: url,
- encoding: null
- }, function(err, res, body) {
- list._.rendering = false;
- loader.stop();
- if (err) {
- return msg.error(err.message);
- }
- if (!body) {
- return msg.error('No body.');
- }
- return cp437ToUtf8(body, function(err, body) {
- if (err) {
- return msg.error(err.message);
- }
- if (process.argv[2] === '--debug') {
- var filename = name.replace(/\//g, '.') + '.ans';
- fs.writeFileSync(__dirname + '/' + filename, body);
- }
- // Remove text:
- body = body.replace('Downloaded From P-80 International Information Systems 304-744-2253', '');
- // Remove MCI codes:
- body = body.replace(/%[A-Z0-9]{2}/g, '');
- // ^A (SOH) seems to need to produce CRLF in some cases??
- // body = body.replace(/\x01/g, '\r\n');
- // Reset and write the art:
- art.term.reset();
- art.term.write(body);
- art.term.cursorHidden = true;
- screen.render();
- if (process.argv[2] === '--debug' || process.argv[2] === '--save') {
- takeScreenshot(name);
- }
- });
- });
- });
- list.items.forEach(function(item, i) {
- var text = item.getText();
- item.setHover(map[text]);
- });
- list.focus();
- list.enterSelected(0);
- screen.key('h', function() {
- list.toggle();
- if (list.visible) list.focus();
- });
- screen.key('r', function() {
- shuffle();
- });
- screen.key('S-s', function() {
- takeScreenshot(list.ritems[list.selected]);
- });
- screen.key('s', function() {
- slideshow();
- });
- screen.key('q', function() {
- return process.exit(0);
- });
- screen.render();
- /**
- * Helpers
- */
- // https://github.com/chjj/blessed/issues/127
- // https://github.com/Mithgol/node-singlebyte
- function cp437ToUtf8(buf, callback) {
- try {
- return callback(null, singlebyte.bufToStr(buf, 'cp437'));
- } catch (e) {
- return callback(e);
- }
- }
- // Animating ANSI art doesn't work for screenshots.
- var ANIMATING = [
- 'bbs/void3',
- 'holiday/xmasfwks',
- 'unsorted/diver',
- 'unsorted/mash-chp',
- 'unsorted/ryans47',
- 'unsorted/xmasfwks'
- ];
- function takeScreenshot(name) {
- var filename = name.replace(/\//g, '.') + '.ans.sgr';
- var image;
- // Animating art hangs terminal during screenshot as of right now.
- if (~ANIMATING.indexOf(name)) {
- image = blessed.element.prototype.screenshot.call(art,
- 0 - art.ileft, art.width - art.iright,
- 0 - art.itop, art.height - art.ibottom);
- } else {
- image = art.screenshot();
- }
- fs.writeFileSync(__dirname + '/' + filename, image);
- msg.display('Screenshot taken.');
- }
- function slideshow() {
- if (!screen._.slideshow) {
- screen._.slideshow = setInterval(function slide() {
- if (screen.lockKeys) return;
- var i = (list.items.length - 1) * Math.random() | 0;
- list.enterSelected(i);
- return slide;
- }(), 3000);
- msg.display('Slideshow started.');
- } else {
- clearInterval(screen._.slideshow);
- delete screen._.slideshow;
- msg.display('Slideshow stopped.');
- }
- }
- function shuffle() {
- var items = Object.keys(map).sort(function(key) {
- return Math.random() > 0.5 ? 1 : -1;
- });
- list.setItems(items);
- screen.render();
- msg.display('Shuffled items.');
- }
|