1. (function (console) {
    2. // vars
    3. var el, head, input,
    4. glob = this,
    5. rows = 1,
    6. ctrlon = false,
    7. shifton = false,
    8. history = new History(100),
    9. api = new API();
    10. // colors
    11. var GRAY = '#777',
    12. RED = 'red',
    13. BLUE = 'blue';
    14. // shortcuts
    15. var body = document.body,
    16. slice = Array.prototype.slice,
    17. toString = Object.prototype.toString;
    18. // hooks
    19. var evaluateHook = evaluate,
    20. stringifyHook = stringify;
    21. // dom
    22. document.write(''
    23. + '<pre id="miniconsole">'
    24. + '<div id="miniconsole-prompt">'
    25. + '<span>> </span>'
    26. + '<span><textarea '
    27. + 'id="miniconsole-input" rows="1"'
    28. + '></textarea><span>'
    29. + '</div>'
    30. + '<style>'
    31. + '#miniconsole {'
    32. + 'margin: 0;'
    33. + 'padding-bottom: 2em;'
    34. + '}'
    35. + '#miniconsole * {'
    36. + 'font-size: 1em;'
    37. + '}'
    38. + '#miniconsole-prompt span {'
    39. + 'display: table-cell;'
    40. + 'vertical-align: top;'
    41. + '}'
    42. + '#miniconsole-prompt span + span {'
    43. + 'width: 100%;'
    44. + '}'
    45. + '#miniconsole-input {'
    46. + 'font-family: inherit;'
    47. + 'overflow: hidden;'
    48. + 'outline: none;'
    49. + 'resize: none;'
    50. + 'display: block;'
    51. + 'width: 100%;'
    52. + 'border: 0;'
    53. + 'margin: 0;'
    54. + 'padding: 0;'
    55. + '}'
    56. + '#miniconsole div {'
    57. + 'padding: .5em;'
    58. + 'border-top: 1px solid #ddd;'
    59. + '}'
    60. + '</style>'
    61. + '</pre>'
    62. );
    63. el = document.getElementById('miniconsole');
    64. head = document.getElementsByTagName('head')[0];
    65. input = document.getElementById('miniconsole-input');
    66. head.appendChild(el.lastChild);
    67. onKeydown(input, {
    68. 13 /* ENTER */: function () {
    69. if (shifton) {
    70. rows += 1;
    71. } else {
    72. rows = 1;
    73. this.focus();
    74. send(this.value);
    75. setTimeout(function () {
    76. input.value = '';
    77. }, 0);
    78. }
    79. refreshRows();
    80. },
    81. 38 /* UP */: function () {
    82. if (ctrlon) {
    83. this.value = history.up();
    84. }
    85. },
    86. 40 /* DOWN */: function () {
    87. if (ctrlon) {
    88. this.value = history.down();
    89. }
    90. }
    91. });
    92. on(input, 'keyup', function (ev) {
    93. var i, s, c;
    94. if (which(ev) != 13) {
    95. rows = 1;
    96. c = '\n';
    97. s = this.value;
    98. i = s.length;
    99. while (i-- > 0) if (s[i] == c) rows++;
    100. refreshRows();
    101. }
    102. });
    103. onKeydown(document, {
    104. 16 /* SHIFT */: function () {
    105. shifton = true;
    106. },
    107. 17 /* CTRL */: function () {
    108. ctrlon = true;
    109. },
    110. 76 /* L */: function (ev) {
    111. if (ctrlon) {
    112. ev.preventDefault();
    113. clear();
    114. input.focus();
    115. }
    116. }
    117. });
    118. onKeyup(document, {
    119. 16 /* SHIFT */: function () {
    120. shifton = false;
    121. },
    122. 17 /* CTRL */: function () {
    123. ctrlon = false;
    124. }
    125. });
    126. on(document, 'click', function (ev) {
    127. var doFocus = ev.target == el;
    128. if (!doFocus) doFocus = ev.target == input.parentNode.parentNode;
    129. if (!doFocus) doFocus = ev.target == body;
    130. if (!doFocus) doFocus = ev.target == body.parentNode;
    131. if (!doFocus) doFocus = ev.target == document;
    132. if (doFocus) input.focus();
    133. });
    134. // init
    135. input.focus();
    136. glob.mini = api;
    137. // functions
    138. function noop () {}
    139. function clear () {
    140. while (last()) el.removeChild(last());
    141. }
    142. function last () {
    143. return el.lastChild.previousSibling;
    144. }
    145. function color (el, color) {
    146. el.style.color = color;
    147. }
    148. function which (ev) {
    149. return ev.which || ev.keyCode;
    150. }
    151. function onKeyup (o, handlers) {
    152. onKeystroke(o, 'keyup', handlers);
    153. }
    154. function onKeydown (o, handlers) {
    155. onKeystroke(o, 'keydown', handlers);
    156. }
    157. function refreshRows () {
    158. input.setAttribute('rows', rows);
    159. scrollToBottom();
    160. }
    161. function scrollToBottom () {
    162. var value = body.scrollHeight;
    163. if (body.scrollTop) body.scrollTop = value;
    164. else document.documentElement.scrollTop = value;
    165. }
    166. function map (l, f) {
    167. var i, n = l.length;
    168. var r = new Array(n);
    169. while (n--) r[n] = f(l[n]);
    170. return r;
    171. }
    172. function onKeystroke (o, ev, handlers) {
    173. var handler;
    174. on(o, ev, function (ev) {
    175. if (handler = handlers[which(ev)]) {
    176. return handler.apply(this, arguments);
    177. }
    178. });
    179. }
    180. function on (o, ev, fn) {
    181. if (o.addEventListener) {
    182. o.addEventListener(ev, fn, false);
    183. } else if (o.attachEvent) {
    184. o.attachEvent('on' + ev, fn);
    185. }
    186. }
    187. function un (o, ev, fn) {
    188. if (o.removeEventListener) {
    189. o.removeEventListener(ev, fn, false);
    190. } else if (o.detachEvent) {
    191. o.detachEvent('on' + ev, fn);
    192. }
    193. }
    194. function stringify (o) {
    195. switch (toString.call(o).slice(8, -1)) {
    196. case 'Object': return stringifyObject(o);
    197. case 'Array': return stringifyArray(o);
    198. case 'Function': return o + '';
    199. default: return stringifyItem(o);
    200. }
    201. }
    202. function stringifyArray (o) {
    203. var i, l = o.length, temp = [];
    204. for (i = 0; i < l; i++) {
    205. temp.push(stringifyItem(o[i]));
    206. }
    207. return '[' + temp.join(',') + ']';
    208. }
    209. function stringifyObject (o) {
    210. var k, temp = [];
    211. for (k in o) {
    212. if (o.hasOwnProperty(k)) {
    213. temp.push(k + ':' + stringifyItem(o[k]));
    214. }
    215. }
    216. return '{' + temp.join(',') + '}';
    217. }
    218. function stringifyItem (o) {
    219. var string = toString.call(o);
    220. switch (string.slice(8, -1)) {
    221. case 'Array': case 'Function': return string;
    222. case 'String': return '"' + o + '"';
    223. default: return o + '';
    224. }
    225. }
    226. function stringifyButString (o) {
    227. if (typeof o != 'string') {
    228. return stringifyHook(o);
    229. } else {
    230. return o;
    231. }
    232. }
    233. function entitify (str) {
    234. return String(str)
    235. .replace(/&/g, '&amp;')
    236. .replace(/</g, '&lt;')
    237. .replace(/>/g, '&gt;')
    238. .replace(/"/g, '&quot;')
    239. .replace(/ /g, '&nbsp;');
    240. }
    241. function evaluate (code) {
    242. var script = document.createElement('script');
    243. code = code.replace(/('|\\|\n)/g, '\\$1');
    244. script.text = ''
    245. + 'try { mini = [eval(\'' + code + '\'), true]; }'
    246. + 'catch (error) { mini = [error, false]; }';
    247. head.appendChild(script);
    248. head.removeChild(script);
    249. code = glob.mini;
    250. glob.mini = api;
    251. if (code.pop()) {
    252. return code.pop();
    253. } else {
    254. code = code.pop();
    255. consoleDotError(code);
    256. throw code;
    257. }
    258. }
    259. // io
    260. function info (msg) {
    261. print('<i>' + msg + '</i>');
    262. color(last(), GRAY);
    263. }
    264. function consoleDotError (error) {
    265. if (console && console.error)
    266. if (typeof console.error == 'function')
    267. console.error(error);
    268. }
    269. function print (msg) {
    270. var div = document.createElement('div');
    271. el.insertBefore(div, el.lastChild);
    272. div.innerHTML = msg === '' ? '&nbsp;' : msg + '';
    273. }
    274. function hook (dflt, hook) {
    275. return function () {
    276. var args = slice.call(arguments);
    277. return hook.apply(api, args.concat(dflt));
    278. };
    279. }
    280. function log (msg) {
    281. msg = slice.call(arguments);
    282. msg = map(msg, stringifyButString);
    283. print(entitify(msg.join(' ')));
    284. scrollToBottom();
    285. }
    286. function send (code) {
    287. history.save(code);
    288. print(entitify('> ' + code.replace(/\n/g, '\n ')));
    289. color(last(), BLUE);
    290. evalPrint(code);
    291. }
    292. function evalPrint (code) {
    293. try {
    294. var r = evaluateHook(code);
    295. print(entitify(stringifyHook(r)));
    296. if (r === undefined) color(last(), GRAY);
    297. } catch (error) {
    298. print(entitify(error));
    299. color(last(), RED);
    300. }
    301. }
    302. function set (opt, val) {
    303. switch (opt) {
    304. case 'evalHook':
    305. evaluateHook = hook(evaluate, val);
    306. info('"evalHook" set.');
    307. break;
    308. case 'printHook':
    309. stringifyHook = hook(stringify, val);
    310. info('"printHook" set.');
    311. break;
    312. default:
    313. info('Unknown option "' + opt + '".');
    314. break;
    315. }
    316. }
    317. // constructors
    318. function API () {
    319. this.set = set;
    320. this.log = log;
    321. this.send = send;
    322. this.clear = function () {
    323. clear(), info('Console was cleared.');
    324. };
    325. }
    326. function History (size) {
    327. var i = 0, items = [''];
    328. this.up = function () {
    329. return items[++i < items.length ? i : --i];
    330. };
    331. this.down = function () {
    332. return items[--i > -1 ? i : ++i];
    333. };
    334. this.save = function (input) {
    335. i = 0;
    336. if (input) items[0] = input;
    337. if (items[0] === items[1]) {
    338. items[0] = '';
    339. } else {
    340. items.unshift('');
    341. if (items.length > size) {
    342. items.pop();
    343. }
    344. }
    345. };
    346. }
    347. })(console);