7 Alexis Sellier (cloudhead)
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 var isHeadless = false;
31 if (typeof module !== 'undefined') {
39 exports.mode = "browser";
41 exports.mode = "console";
45 // Prototypes the string object to have additional method calls that add terminal colors
47 var addProperty = function (color, func) {
48 exports[color] = function (str) {
49 return func.apply(str);
51 String.prototype.__defineGetter__(color, func);
54 function stylize(str, style) {
58 if (exports.mode === 'console') {
61 'bold' : ['\x1B[1m', '\x1B[22m'],
62 'italic' : ['\x1B[3m', '\x1B[23m'],
63 'underline' : ['\x1B[4m', '\x1B[24m'],
64 'inverse' : ['\x1B[7m', '\x1B[27m'],
65 'strikethrough' : ['\x1B[9m', '\x1B[29m'],
68 'white' : ['\x1B[37m', '\x1B[39m'],
69 'grey' : ['\x1B[90m', '\x1B[39m'],
70 'black' : ['\x1B[30m', '\x1B[39m'],
72 'blue' : ['\x1B[34m', '\x1B[39m'],
73 'cyan' : ['\x1B[36m', '\x1B[39m'],
74 'green' : ['\x1B[32m', '\x1B[39m'],
75 'magenta' : ['\x1B[35m', '\x1B[39m'],
76 'red' : ['\x1B[31m', '\x1B[39m'],
77 'yellow' : ['\x1B[33m', '\x1B[39m'],
80 'whiteBG' : ['\x1B[47m', '\x1B[49m'],
81 'greyBG' : ['\x1B[49;5;8m', '\x1B[49m'],
82 'blackBG' : ['\x1B[40m', '\x1B[49m'],
84 'blueBG' : ['\x1B[44m', '\x1B[49m'],
85 'cyanBG' : ['\x1B[46m', '\x1B[49m'],
86 'greenBG' : ['\x1B[42m', '\x1B[49m'],
87 'magentaBG' : ['\x1B[45m', '\x1B[49m'],
88 'redBG' : ['\x1B[41m', '\x1B[49m'],
89 'yellowBG' : ['\x1B[43m', '\x1B[49m']
91 } else if (exports.mode === 'browser') {
94 'bold' : ['<b>', '</b>'],
95 'italic' : ['<i>', '</i>'],
96 'underline' : ['<u>', '</u>'],
97 'inverse' : ['<span style="background-color:black;color:white;">', '</span>'],
98 'strikethrough' : ['<del>', '</del>'],
101 'white' : ['<span style="color:white;">', '</span>'],
102 'grey' : ['<span style="color:gray;">', '</span>'],
103 'black' : ['<span style="color:black;">', '</span>'],
105 'blue' : ['<span style="color:blue;">', '</span>'],
106 'cyan' : ['<span style="color:cyan;">', '</span>'],
107 'green' : ['<span style="color:green;">', '</span>'],
108 'magenta' : ['<span style="color:magenta;">', '</span>'],
109 'red' : ['<span style="color:red;">', '</span>'],
110 'yellow' : ['<span style="color:yellow;">', '</span>'],
113 'whiteBG' : ['<span style="background-color:white;">', '</span>'],
114 'greyBG' : ['<span style="background-color:gray;">', '</span>'],
115 'blackBG' : ['<span style="background-color:black;">', '</span>'],
117 'blueBG' : ['<span style="background-color:blue;">', '</span>'],
118 'cyanBG' : ['<span style="background-color:cyan;">', '</span>'],
119 'greenBG' : ['<span style="background-color:green;">', '</span>'],
120 'magentaBG' : ['<span style="background-color:magenta;">', '</span>'],
121 'redBG' : ['<span style="background-color:red;">', '</span>'],
122 'yellowBG' : ['<span style="background-color:yellow;">', '</span>']
124 } else if (exports.mode === 'none') {
127 console.log('unsupported mode, try "browser", "console" or "none"');
129 return styles[style][0] + str + styles[style][1];
132 function applyTheme(theme) {
135 // Remark: This is a list of methods that exist
136 // on String that you should not overwrite.
138 var stringPrototypeBlacklist = [
139 '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'charAt', 'constructor',
140 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf', 'charCodeAt',
141 'indexOf', 'lastIndexof', 'length', 'localeCompare', 'match', 'replace', 'search', 'slice', 'split', 'substring',
142 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim', 'trimLeft', 'trimRight'
145 Object.keys(theme).forEach(function (prop) {
146 if (stringPrototypeBlacklist.indexOf(prop) !== -1) {
147 console.log('warn: '.red + ('String.prototype' + prop).magenta + ' is probably something you don\'t want to override. Ignoring style name');
150 if (typeof(theme[prop]) === 'string') {
151 addProperty(prop, function () {
152 return exports[theme[prop]](this);
156 addProperty(prop, function () {
158 for (var t = 0; t < theme[prop].length; t++) {
159 ret = exports[theme[prop][t]](ret);
170 // Iterate through all default styles and colors
172 var x = ['bold', 'underline', 'strikethrough', 'italic', 'inverse', 'grey', 'black', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta', 'greyBG', 'blackBG', 'yellowBG', 'redBG', 'greenBG', 'blueBG', 'whiteBG', 'cyanBG', 'magentaBG'];
173 x.forEach(function (style) {
175 // __defineGetter__ at the least works in more browsers
176 // http://robertnyman.com/javascript/javascript-getters-setters.html
177 // Object.defineProperty only works in Chrome
178 addProperty(style, function () {
179 return stylize(this, style);
183 function sequencer(map) {
186 return this.replace(/( )/, '$1');
188 var exploded = this.split(""), i = 0;
189 exploded = exploded.map(map);
190 return exploded.join("");
194 var rainbowMap = (function () {
195 var rainbowColors = ['red', 'yellow', 'green', 'blue', 'magenta']; //RoY G BiV
196 return function (letter, i, exploded) {
197 if (letter === " ") {
200 return stylize(letter, rainbowColors[i++ % rainbowColors.length]);
207 exports.addSequencer = function (name, map) {
208 addProperty(name, sequencer(map));
211 exports.addSequencer('rainbow', rainbowMap);
212 exports.addSequencer('zebra', function (letter, i, exploded) {
213 return i % 2 === 0 ? letter : letter.inverse;
216 exports.setTheme = function (theme) {
217 if (typeof theme === 'string') {
219 exports.themes[theme] = require(theme);
220 applyTheme(exports.themes[theme]);
221 return exports.themes[theme];
232 addProperty('stripColors', function () {
233 return ("" + this).replace(/\x1B\[\d+m/g, '');
237 function zalgo(text, options) {
275 all = [].concat(soul.up, soul.down, soul.mid),
278 function randomNumber(range) {
279 var r = Math.floor(Math.random() * range);
283 function is_char(character) {
285 all.filter(function (i) {
286 bool = (i === character);
291 function heComes(text, options) {
292 var result = '', counts, l;
293 options = options || {};
294 options["up"] = options["up"] || true;
295 options["mid"] = options["mid"] || true;
296 options["down"] = options["down"] || true;
297 options["size"] = options["size"] || "maxi";
298 text = text.split('');
303 result = result + text[l];
304 counts = {"up" : 0, "down" : 0, "mid" : 0};
305 switch (options.size) {
307 counts.up = randomNumber(8);
308 counts.min = randomNumber(2);
309 counts.down = randomNumber(8);
312 counts.up = randomNumber(16) + 3;
313 counts.min = randomNumber(4) + 1;
314 counts.down = randomNumber(64) + 3;
317 counts.up = randomNumber(8) + 1;
318 counts.mid = randomNumber(6) / 2;
319 counts.down = randomNumber(8) + 1;
323 var arr = ["up", "mid", "down"];
326 for (var i = 0 ; i <= counts[index]; i++) {
327 if (options[index]) {
328 result = result + soul[index][randomNumber(soul[index].length)];
335 return heComes(text);
339 // don't summon zalgo
340 addProperty('zalgo', function () {