macgyver 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/README.md +45 -0
  6. data/Rakefile +1 -0
  7. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
  8. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
  9. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
  10. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
  11. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
  12. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
  13. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
  14. data/assets/MacGap.app/Contents/Info.plist +48 -0
  15. data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
  16. data/assets/MacGap.app/Contents/PkgInfo +1 -0
  17. data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
  18. data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
  19. data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
  20. data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
  21. data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
  22. data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
  23. data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
  24. data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
  25. data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
  26. data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
  27. data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
  28. data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
  29. data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
  30. data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
  31. data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
  32. data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
  33. data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
  34. data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
  35. data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
  36. data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
  37. data/assets/MacGap.app/Contents/Resources/console.js +108 -0
  38. data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
  39. data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
  40. data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
  41. data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
  42. data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
  43. data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
  44. data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
  45. data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
  46. data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
  47. data/assets/MacGap.app/Contents/Resources/events.js +312 -0
  48. data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
  49. data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
  50. data/assets/MacGap.app/Contents/Resources/http.js +119 -0
  51. data/assets/MacGap.app/Contents/Resources/https.js +134 -0
  52. data/assets/MacGap.app/Contents/Resources/module.js +529 -0
  53. data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
  54. data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
  55. data/assets/MacGap.app/Contents/Resources/os.js +64 -0
  56. data/assets/MacGap.app/Contents/Resources/path.js +517 -0
  57. data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
  58. data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
  59. data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
  60. data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
  61. data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
  62. data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
  63. data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
  64. data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
  65. data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
  66. data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
  67. data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
  68. data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
  69. data/assets/MacGap.app/Contents/Resources/url.js +693 -0
  70. data/assets/MacGap.app/Contents/Resources/util.js +688 -0
  71. data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
  72. data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
  73. data/assets/index.html +38 -0
  74. data/bin/macgyver +104 -0
  75. data/macgyver.gemspec +19 -0
  76. data/test/public/index.html +27 -0
  77. metadata +121 -0
@@ -0,0 +1,206 @@
1
+ // Copyright Joyent, Inc. and other Node contributors.
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a
4
+ // copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ // persons to whom the Software is furnished to do so, subject to the
9
+ // following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included
12
+ // in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ // Query String Utilities
23
+
24
+ var QueryString = exports;
25
+ var util = require('util');
26
+
27
+
28
+ // If obj.hasOwnProperty has been overridden, then calling
29
+ // obj.hasOwnProperty(prop) will break.
30
+ // See: https://github.com/joyent/node/issues/1707
31
+ function hasOwnProperty(obj, prop) {
32
+ return Object.prototype.hasOwnProperty.call(obj, prop);
33
+ }
34
+
35
+
36
+ function charCode(c) {
37
+ return c.charCodeAt(0);
38
+ }
39
+
40
+
41
+ // a safe fast alternative to decodeURIComponent
42
+ QueryString.unescapeBuffer = function(s, decodeSpaces) {
43
+ var out = new Buffer(s.length);
44
+ var state = 'CHAR'; // states: CHAR, HEX0, HEX1
45
+ var n, m, hexchar;
46
+
47
+ for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
48
+ var c = s.charCodeAt(inIndex);
49
+ switch (state) {
50
+ case 'CHAR':
51
+ switch (c) {
52
+ case charCode('%'):
53
+ n = 0;
54
+ m = 0;
55
+ state = 'HEX0';
56
+ break;
57
+ case charCode('+'):
58
+ if (decodeSpaces) c = charCode(' ');
59
+ // pass thru
60
+ default:
61
+ out[outIndex++] = c;
62
+ break;
63
+ }
64
+ break;
65
+
66
+ case 'HEX0':
67
+ state = 'HEX1';
68
+ hexchar = c;
69
+ if (charCode('0') <= c && c <= charCode('9')) {
70
+ n = c - charCode('0');
71
+ } else if (charCode('a') <= c && c <= charCode('f')) {
72
+ n = c - charCode('a') + 10;
73
+ } else if (charCode('A') <= c && c <= charCode('F')) {
74
+ n = c - charCode('A') + 10;
75
+ } else {
76
+ out[outIndex++] = charCode('%');
77
+ out[outIndex++] = c;
78
+ state = 'CHAR';
79
+ break;
80
+ }
81
+ break;
82
+
83
+ case 'HEX1':
84
+ state = 'CHAR';
85
+ if (charCode('0') <= c && c <= charCode('9')) {
86
+ m = c - charCode('0');
87
+ } else if (charCode('a') <= c && c <= charCode('f')) {
88
+ m = c - charCode('a') + 10;
89
+ } else if (charCode('A') <= c && c <= charCode('F')) {
90
+ m = c - charCode('A') + 10;
91
+ } else {
92
+ out[outIndex++] = charCode('%');
93
+ out[outIndex++] = hexchar;
94
+ out[outIndex++] = c;
95
+ break;
96
+ }
97
+ out[outIndex++] = 16 * n + m;
98
+ break;
99
+ }
100
+ }
101
+
102
+ // TODO support returning arbitrary buffers.
103
+
104
+ return out.slice(0, outIndex - 1);
105
+ };
106
+
107
+
108
+ QueryString.unescape = function(s, decodeSpaces) {
109
+ return QueryString.unescapeBuffer(s, decodeSpaces).toString();
110
+ };
111
+
112
+
113
+ QueryString.escape = function(str) {
114
+ return encodeURIComponent(str);
115
+ };
116
+
117
+ var stringifyPrimitive = function(v) {
118
+ if (util.isString(v))
119
+ return v;
120
+ if (util.isBoolean(v))
121
+ return v ? 'true' : 'false';
122
+ if (util.isNumber(v))
123
+ return isFinite(v) ? v : '';
124
+ return '';
125
+ };
126
+
127
+
128
+ QueryString.stringify = QueryString.encode = function(obj, sep, eq) {
129
+ sep = sep || '&';
130
+ eq = eq || '=';
131
+ if (util.isNull(obj)) {
132
+ obj = undefined;
133
+ }
134
+
135
+ if (util.isObject(obj)) {
136
+ return Object.keys(obj).map(function(k) {
137
+ var ks = QueryString.escape(stringifyPrimitive(k)) + eq;
138
+ if (util.isArray(obj[k])) {
139
+ return obj[k].map(function(v) {
140
+ return ks + QueryString.escape(stringifyPrimitive(v));
141
+ }).join(sep);
142
+ } else {
143
+ return ks + QueryString.escape(stringifyPrimitive(obj[k]));
144
+ }
145
+ }).join(sep);
146
+
147
+ }
148
+ return '';
149
+ };
150
+
151
+ // Parse a key=val string.
152
+ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
153
+ sep = sep || '&';
154
+ eq = eq || '=';
155
+ var obj = {};
156
+
157
+ if (!util.isString(qs) || qs.length === 0) {
158
+ return obj;
159
+ }
160
+
161
+ var regexp = /\+/g;
162
+ qs = qs.split(sep);
163
+
164
+ var maxKeys = 1000;
165
+ if (options && util.isNumber(options.maxKeys)) {
166
+ maxKeys = options.maxKeys;
167
+ }
168
+
169
+ var len = qs.length;
170
+ // maxKeys <= 0 means that we should not limit keys count
171
+ if (maxKeys > 0 && len > maxKeys) {
172
+ len = maxKeys;
173
+ }
174
+
175
+ for (var i = 0; i < len; ++i) {
176
+ var x = qs[i].replace(regexp, '%20'),
177
+ idx = x.indexOf(eq),
178
+ kstr, vstr, k, v;
179
+
180
+ if (idx >= 0) {
181
+ kstr = x.substr(0, idx);
182
+ vstr = x.substr(idx + 1);
183
+ } else {
184
+ kstr = x;
185
+ vstr = '';
186
+ }
187
+
188
+ try {
189
+ k = decodeURIComponent(kstr);
190
+ v = decodeURIComponent(vstr);
191
+ } catch (e) {
192
+ k = QueryString.unescape(kstr, true);
193
+ v = QueryString.unescape(vstr, true);
194
+ }
195
+
196
+ if (!hasOwnProperty(obj, k)) {
197
+ obj[k] = v;
198
+ } else if (util.isArray(obj[k])) {
199
+ obj[k].push(v);
200
+ } else {
201
+ obj[k] = [obj[k], v];
202
+ }
203
+ }
204
+
205
+ return obj;
206
+ };
@@ -0,0 +1,1311 @@
1
+ // Copyright Joyent, Inc. and other Node contributors.
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a
4
+ // copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ // persons to whom the Software is furnished to do so, subject to the
9
+ // following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included
12
+ // in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ // Inspiration for this code comes from Salvatore Sanfilippo's linenoise.
23
+ // https://github.com/antirez/linenoise
24
+ // Reference:
25
+ // * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
26
+ // * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
27
+
28
+ var kHistorySize = 30;
29
+
30
+ var util = require('util');
31
+ var inherits = require('util').inherits;
32
+ var EventEmitter = require('events').EventEmitter;
33
+
34
+
35
+ exports.createInterface = function(input, output, completer, terminal) {
36
+ var rl;
37
+ if (arguments.length === 1) {
38
+ rl = new Interface(input);
39
+ } else {
40
+ rl = new Interface(input, output, completer, terminal);
41
+ }
42
+ return rl;
43
+ };
44
+
45
+
46
+ function Interface(input, output, completer, terminal) {
47
+ if (!(this instanceof Interface)) {
48
+ return new Interface(input, output, completer, terminal);
49
+ }
50
+
51
+ this._sawReturn = false;
52
+
53
+ EventEmitter.call(this);
54
+
55
+ if (arguments.length === 1) {
56
+ // an options object was given
57
+ output = input.output;
58
+ completer = input.completer;
59
+ terminal = input.terminal;
60
+ input = input.input;
61
+ }
62
+
63
+ completer = completer || function() { return []; };
64
+
65
+ if (!util.isFunction(completer)) {
66
+ throw new TypeError('Argument \'completer\' must be a function');
67
+ }
68
+
69
+ // backwards compat; check the isTTY prop of the output stream
70
+ // when `terminal` was not specified
71
+ if (util.isUndefined(terminal)) {
72
+ terminal = !!output.isTTY;
73
+ }
74
+
75
+ var self = this;
76
+
77
+ this.output = output;
78
+ this.input = input;
79
+
80
+ // Check arity, 2 - for async, 1 for sync
81
+ this.completer = completer.length === 2 ? completer : function(v, callback) {
82
+ callback(null, completer(v));
83
+ };
84
+
85
+ this.setPrompt('> ');
86
+
87
+ this.terminal = !!terminal;
88
+
89
+ function ondata(data) {
90
+ self._normalWrite(data);
91
+ }
92
+
93
+ function onend() {
94
+ self.close();
95
+ }
96
+
97
+ function onkeypress(s, key) {
98
+ self._ttyWrite(s, key);
99
+ }
100
+
101
+ function onresize() {
102
+ self._refreshLine();
103
+ }
104
+
105
+ if (!this.terminal) {
106
+ input.on('data', ondata);
107
+ input.on('end', onend);
108
+ self.once('close', function() {
109
+ input.removeListener('data', ondata);
110
+ input.removeListener('end', onend);
111
+ });
112
+ var StringDecoder = require('string_decoder').StringDecoder; // lazy load
113
+ this._decoder = new StringDecoder('utf8');
114
+
115
+ } else {
116
+
117
+ exports.emitKeypressEvents(input);
118
+
119
+ // input usually refers to stdin
120
+ input.on('keypress', onkeypress);
121
+
122
+ // Current line
123
+ this.line = '';
124
+
125
+ this._setRawMode(true);
126
+ this.terminal = true;
127
+
128
+ // Cursor position on the line.
129
+ this.cursor = 0;
130
+
131
+ this.history = [];
132
+ this.historyIndex = -1;
133
+
134
+ output.on('resize', onresize);
135
+ self.once('close', function() {
136
+ input.removeListener('keypress', onkeypress);
137
+ output.removeListener('resize', onresize);
138
+ });
139
+ }
140
+
141
+ input.resume();
142
+ }
143
+
144
+ inherits(Interface, EventEmitter);
145
+
146
+ Interface.prototype.__defineGetter__('columns', function() {
147
+ return this.output.columns || Infinity;
148
+ });
149
+
150
+ Interface.prototype.setPrompt = function(prompt) {
151
+ this._prompt = prompt;
152
+ };
153
+
154
+
155
+ Interface.prototype._setRawMode = function(mode) {
156
+ if (util.isFunction(this.input.setRawMode)) {
157
+ return this.input.setRawMode(mode);
158
+ }
159
+ };
160
+
161
+
162
+ Interface.prototype.prompt = function(preserveCursor) {
163
+ if (this.paused) this.resume();
164
+ if (this.terminal) {
165
+ if (!preserveCursor) this.cursor = 0;
166
+ this._refreshLine();
167
+ } else {
168
+ this.output.write(this._prompt);
169
+ }
170
+ };
171
+
172
+
173
+ Interface.prototype.question = function(query, cb) {
174
+ if (util.isFunction(cb)) {
175
+ if (this._questionCallback) {
176
+ this.prompt();
177
+ } else {
178
+ this._oldPrompt = this._prompt;
179
+ this.setPrompt(query);
180
+ this._questionCallback = cb;
181
+ this.prompt();
182
+ }
183
+ }
184
+ };
185
+
186
+
187
+ Interface.prototype._onLine = function(line) {
188
+ if (this._questionCallback) {
189
+ var cb = this._questionCallback;
190
+ this._questionCallback = null;
191
+ this.setPrompt(this._oldPrompt);
192
+ cb(line);
193
+ } else {
194
+ this.emit('line', line);
195
+ }
196
+ };
197
+
198
+
199
+ Interface.prototype._addHistory = function() {
200
+ if (this.line.length === 0) return '';
201
+
202
+ if (this.history.length === 0 || this.history[0] !== this.line) {
203
+ this.history.unshift(this.line);
204
+
205
+ // Only store so many
206
+ if (this.history.length > kHistorySize) this.history.pop();
207
+ }
208
+
209
+ this.historyIndex = -1;
210
+ return this.history[0];
211
+ };
212
+
213
+
214
+ Interface.prototype._refreshLine = function() {
215
+ // line length
216
+ var line = this._prompt + this.line;
217
+ var dispPos = this._getDisplayPos(line);
218
+ var lineCols = dispPos.cols;
219
+ var lineRows = dispPos.rows;
220
+
221
+ // cursor position
222
+ var cursorPos = this._getCursorPos();
223
+
224
+ // first move to the bottom of the current line, based on cursor pos
225
+ var prevRows = this.prevRows || 0;
226
+ if (prevRows > 0) {
227
+ exports.moveCursor(this.output, 0, -prevRows);
228
+ }
229
+
230
+ // Cursor to left edge.
231
+ exports.cursorTo(this.output, 0);
232
+ // erase data
233
+ exports.clearScreenDown(this.output);
234
+
235
+ // Write the prompt and the current buffer content.
236
+ this.output.write(line);
237
+
238
+ // Force terminal to allocate a new line
239
+ if (lineCols === 0) {
240
+ this.output.write(' ');
241
+ }
242
+
243
+ // Move cursor to original position.
244
+ exports.cursorTo(this.output, cursorPos.cols);
245
+
246
+ var diff = lineRows - cursorPos.rows;
247
+ if (diff > 0) {
248
+ exports.moveCursor(this.output, 0, -diff);
249
+ }
250
+
251
+ this.prevRows = cursorPos.rows;
252
+ };
253
+
254
+
255
+ Interface.prototype.close = function() {
256
+ if (this.closed) return;
257
+ this.pause();
258
+ if (this.terminal) {
259
+ this._setRawMode(false);
260
+ }
261
+ this.closed = true;
262
+ this.emit('close');
263
+ };
264
+
265
+
266
+ Interface.prototype.pause = function() {
267
+ if (this.paused) return;
268
+ this.input.pause();
269
+ this.paused = true;
270
+ this.emit('pause');
271
+ return this;
272
+ };
273
+
274
+
275
+ Interface.prototype.resume = function() {
276
+ if (!this.paused) return;
277
+ this.input.resume();
278
+ this.paused = false;
279
+ this.emit('resume');
280
+ return this;
281
+ };
282
+
283
+
284
+ Interface.prototype.write = function(d, key) {
285
+ if (this.paused) this.resume();
286
+ this.terminal ? this._ttyWrite(d, key) : this._normalWrite(d);
287
+ };
288
+
289
+ // \r\n, \n, or \r followed by something other than \n
290
+ var lineEnding = /\r?\n|\r(?!\n)/;
291
+ Interface.prototype._normalWrite = function(b) {
292
+ if (util.isUndefined(b)) {
293
+ return;
294
+ }
295
+ var string = this._decoder.write(b);
296
+ if (this._sawReturn) {
297
+ string = string.replace(/^\n/, '');
298
+ this._sawReturn = false;
299
+ }
300
+
301
+ if (this._line_buffer) {
302
+ string = this._line_buffer + string;
303
+ this._line_buffer = null;
304
+ }
305
+ if (lineEnding.test(string)) {
306
+ this._sawReturn = /\r$/.test(string);
307
+
308
+ // got one or more newlines; process into "line" events
309
+ var lines = string.split(lineEnding);
310
+ // either '' or (concievably) the unfinished portion of the next line
311
+ string = lines.pop();
312
+ this._line_buffer = string;
313
+ lines.forEach(function(line) {
314
+ this._onLine(line);
315
+ }, this);
316
+ } else if (string) {
317
+ // no newlines this time, save what we have for next time
318
+ this._line_buffer = string;
319
+ }
320
+ };
321
+
322
+ Interface.prototype._insertString = function(c) {
323
+ //BUG: Problem when adding tabs with following content.
324
+ // Perhaps the bug is in _refreshLine(). Not sure.
325
+ // A hack would be to insert spaces instead of literal '\t'.
326
+ if (this.cursor < this.line.length) {
327
+ var beg = this.line.slice(0, this.cursor);
328
+ var end = this.line.slice(this.cursor, this.line.length);
329
+ this.line = beg + c + end;
330
+ this.cursor += c.length;
331
+ this._refreshLine();
332
+ } else {
333
+ this.line += c;
334
+ this.cursor += c.length;
335
+
336
+ if (this._getCursorPos().cols === 0) {
337
+ this._refreshLine();
338
+ } else {
339
+ this.output.write(c);
340
+ }
341
+
342
+ // a hack to get the line refreshed if it's needed
343
+ this._moveCursor(0);
344
+ }
345
+ };
346
+
347
+ Interface.prototype._tabComplete = function() {
348
+ var self = this;
349
+
350
+ self.pause();
351
+ self.completer(self.line.slice(0, self.cursor), function(err, rv) {
352
+ self.resume();
353
+
354
+ if (err) {
355
+ // XXX Log it somewhere?
356
+ return;
357
+ }
358
+
359
+ var completions = rv[0],
360
+ completeOn = rv[1]; // the text that was completed
361
+ if (completions && completions.length) {
362
+ // Apply/show completions.
363
+ if (completions.length === 1) {
364
+ self._insertString(completions[0].slice(completeOn.length));
365
+ } else {
366
+ self.output.write('\r\n');
367
+ var width = completions.reduce(function(a, b) {
368
+ return a.length > b.length ? a : b;
369
+ }).length + 2; // 2 space padding
370
+ var maxColumns = Math.floor(self.columns / width) || 1;
371
+ var group = [], c;
372
+ for (var i = 0, compLen = completions.length; i < compLen; i++) {
373
+ c = completions[i];
374
+ if (c === '') {
375
+ handleGroup(self, group, width, maxColumns);
376
+ group = [];
377
+ } else {
378
+ group.push(c);
379
+ }
380
+ }
381
+ handleGroup(self, group, width, maxColumns);
382
+
383
+ // If there is a common prefix to all matches, then apply that
384
+ // portion.
385
+ var f = completions.filter(function(e) { if (e) return e; });
386
+ var prefix = commonPrefix(f);
387
+ if (prefix.length > completeOn.length) {
388
+ self._insertString(prefix.slice(completeOn.length));
389
+ }
390
+
391
+ }
392
+ self._refreshLine();
393
+ }
394
+ });
395
+ };
396
+
397
+ // this = Interface instance
398
+ function handleGroup(self, group, width, maxColumns) {
399
+ if (group.length == 0) {
400
+ return;
401
+ }
402
+ var minRows = Math.ceil(group.length / maxColumns);
403
+ for (var row = 0; row < minRows; row++) {
404
+ for (var col = 0; col < maxColumns; col++) {
405
+ var idx = row * maxColumns + col;
406
+ if (idx >= group.length) {
407
+ break;
408
+ }
409
+ var item = group[idx];
410
+ self.output.write(item);
411
+ if (col < maxColumns - 1) {
412
+ for (var s = 0, itemLen = item.length; s < width - itemLen;
413
+ s++) {
414
+ self.output.write(' ');
415
+ }
416
+ }
417
+ }
418
+ self.output.write('\r\n');
419
+ }
420
+ self.output.write('\r\n');
421
+ }
422
+
423
+ function commonPrefix(strings) {
424
+ if (!strings || strings.length == 0) {
425
+ return '';
426
+ }
427
+ var sorted = strings.slice().sort();
428
+ var min = sorted[0];
429
+ var max = sorted[sorted.length - 1];
430
+ for (var i = 0, len = min.length; i < len; i++) {
431
+ if (min[i] != max[i]) {
432
+ return min.slice(0, i);
433
+ }
434
+ }
435
+ return min;
436
+ }
437
+
438
+
439
+ Interface.prototype._wordLeft = function() {
440
+ if (this.cursor > 0) {
441
+ var leading = this.line.slice(0, this.cursor);
442
+ var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
443
+ this._moveCursor(-match[0].length);
444
+ }
445
+ };
446
+
447
+
448
+ Interface.prototype._wordRight = function() {
449
+ if (this.cursor < this.line.length) {
450
+ var trailing = this.line.slice(this.cursor);
451
+ var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
452
+ this._moveCursor(match[0].length);
453
+ }
454
+ };
455
+
456
+
457
+ Interface.prototype._deleteLeft = function() {
458
+ if (this.cursor > 0 && this.line.length > 0) {
459
+ this.line = this.line.slice(0, this.cursor - 1) +
460
+ this.line.slice(this.cursor, this.line.length);
461
+
462
+ this.cursor--;
463
+ this._refreshLine();
464
+ }
465
+ };
466
+
467
+
468
+ Interface.prototype._deleteRight = function() {
469
+ this.line = this.line.slice(0, this.cursor) +
470
+ this.line.slice(this.cursor + 1, this.line.length);
471
+ this._refreshLine();
472
+ };
473
+
474
+
475
+ Interface.prototype._deleteWordLeft = function() {
476
+ if (this.cursor > 0) {
477
+ var leading = this.line.slice(0, this.cursor);
478
+ var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
479
+ leading = leading.slice(0, leading.length - match[0].length);
480
+ this.line = leading + this.line.slice(this.cursor, this.line.length);
481
+ this.cursor = leading.length;
482
+ this._refreshLine();
483
+ }
484
+ };
485
+
486
+
487
+ Interface.prototype._deleteWordRight = function() {
488
+ if (this.cursor < this.line.length) {
489
+ var trailing = this.line.slice(this.cursor);
490
+ var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
491
+ this.line = this.line.slice(0, this.cursor) +
492
+ trailing.slice(match[0].length);
493
+ this._refreshLine();
494
+ }
495
+ };
496
+
497
+
498
+ Interface.prototype._deleteLineLeft = function() {
499
+ this.line = this.line.slice(this.cursor);
500
+ this.cursor = 0;
501
+ this._refreshLine();
502
+ };
503
+
504
+
505
+ Interface.prototype._deleteLineRight = function() {
506
+ this.line = this.line.slice(0, this.cursor);
507
+ this._refreshLine();
508
+ };
509
+
510
+
511
+ Interface.prototype.clearLine = function() {
512
+ this._moveCursor(+Infinity);
513
+ this.output.write('\r\n');
514
+ this.line = '';
515
+ this.cursor = 0;
516
+ this.prevRows = 0;
517
+ };
518
+
519
+
520
+ Interface.prototype._line = function() {
521
+ var line = this._addHistory();
522
+ this.clearLine();
523
+ this._onLine(line);
524
+ };
525
+
526
+
527
+ Interface.prototype._historyNext = function() {
528
+ if (this.historyIndex > 0) {
529
+ this.historyIndex--;
530
+ this.line = this.history[this.historyIndex];
531
+ this.cursor = this.line.length; // set cursor to end of line.
532
+ this._refreshLine();
533
+
534
+ } else if (this.historyIndex === 0) {
535
+ this.historyIndex = -1;
536
+ this.cursor = 0;
537
+ this.line = '';
538
+ this._refreshLine();
539
+ }
540
+ };
541
+
542
+
543
+ Interface.prototype._historyPrev = function() {
544
+ if (this.historyIndex + 1 < this.history.length) {
545
+ this.historyIndex++;
546
+ this.line = this.history[this.historyIndex];
547
+ this.cursor = this.line.length; // set cursor to end of line.
548
+
549
+ this._refreshLine();
550
+ }
551
+ };
552
+
553
+
554
+ // Returns the last character's display position of the given string
555
+ Interface.prototype._getDisplayPos = function(str) {
556
+ var offset = 0;
557
+ var col = this.columns;
558
+ var code;
559
+ str = stripVTControlCharacters(str);
560
+ for (var i = 0, len = str.length; i < len; i++) {
561
+ code = codePointAt(str, i);
562
+ if (code >= 0x10000) { // surrogates
563
+ i++;
564
+ }
565
+ if (isFullWidthCodePoint(code)) {
566
+ if ((offset + 1) % col === 0) {
567
+ offset++;
568
+ }
569
+ offset += 2;
570
+ } else {
571
+ offset++;
572
+ }
573
+ }
574
+ var cols = offset % col;
575
+ var rows = (offset - cols) / col;
576
+ return {cols: cols, rows: rows};
577
+ };
578
+
579
+
580
+ // Returns current cursor's position and line
581
+ Interface.prototype._getCursorPos = function() {
582
+ var columns = this.columns;
583
+ var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
584
+ var dispPos = this._getDisplayPos(stripVTControlCharacters(strBeforeCursor));
585
+ var cols = dispPos.cols;
586
+ var rows = dispPos.rows;
587
+ // If the cursor is on a full-width character which steps over the line,
588
+ // move the cursor to the beginning of the next line.
589
+ if (cols + 1 === columns &&
590
+ this.cursor < this.line.length &&
591
+ isFullWidthCodePoint(codePointAt(this.line, this.cursor))) {
592
+ rows++;
593
+ cols = 0;
594
+ }
595
+ return {cols: cols, rows: rows};
596
+ };
597
+
598
+
599
+ // This function moves cursor dx places to the right
600
+ // (-dx for left) and refreshes the line if it is needed
601
+ Interface.prototype._moveCursor = function(dx) {
602
+ var oldcursor = this.cursor;
603
+ var oldPos = this._getCursorPos();
604
+ this.cursor += dx;
605
+
606
+ // bounds check
607
+ if (this.cursor < 0) this.cursor = 0;
608
+ else if (this.cursor > this.line.length) this.cursor = this.line.length;
609
+
610
+ var newPos = this._getCursorPos();
611
+
612
+ // check if cursors are in the same line
613
+ if (oldPos.rows === newPos.rows) {
614
+ var diffCursor = this.cursor - oldcursor;
615
+ var diffWidth;
616
+ if (diffCursor < 0) {
617
+ diffWidth = -getStringWidth(
618
+ this.line.substring(this.cursor, oldcursor)
619
+ );
620
+ } else if (diffCursor > 0) {
621
+ diffWidth = getStringWidth(
622
+ this.line.substring(this.cursor, oldcursor)
623
+ );
624
+ }
625
+ exports.moveCursor(this.output, diffWidth, 0);
626
+ this.prevRows = newPos.rows;
627
+ } else {
628
+ this._refreshLine();
629
+ }
630
+ };
631
+
632
+
633
+ // handle a write from the tty
634
+ Interface.prototype._ttyWrite = function(s, key) {
635
+ key = key || {};
636
+
637
+ // Ignore escape key - Fixes #2876
638
+ if (key.name == 'escape') return;
639
+
640
+ if (key.ctrl && key.shift) {
641
+ /* Control and shift pressed */
642
+ switch (key.name) {
643
+ case 'backspace':
644
+ this._deleteLineLeft();
645
+ break;
646
+
647
+ case 'delete':
648
+ this._deleteLineRight();
649
+ break;
650
+ }
651
+
652
+ } else if (key.ctrl) {
653
+ /* Control key pressed */
654
+
655
+ switch (key.name) {
656
+ case 'c':
657
+ if (EventEmitter.listenerCount(this, 'SIGINT') > 0) {
658
+ this.emit('SIGINT');
659
+ } else {
660
+ // This readline instance is finished
661
+ this.close();
662
+ }
663
+ break;
664
+
665
+ case 'h': // delete left
666
+ this._deleteLeft();
667
+ break;
668
+
669
+ case 'd': // delete right or EOF
670
+ if (this.cursor === 0 && this.line.length === 0) {
671
+ // This readline instance is finished
672
+ this.close();
673
+ } else if (this.cursor < this.line.length) {
674
+ this._deleteRight();
675
+ }
676
+ break;
677
+
678
+ case 'u': // delete the whole line
679
+ this.cursor = 0;
680
+ this.line = '';
681
+ this._refreshLine();
682
+ break;
683
+
684
+ case 'k': // delete from current to end of line
685
+ this._deleteLineRight();
686
+ break;
687
+
688
+ case 'a': // go to the start of the line
689
+ this._moveCursor(-Infinity);
690
+ break;
691
+
692
+ case 'e': // go to the end of the line
693
+ this._moveCursor(+Infinity);
694
+ break;
695
+
696
+ case 'b': // back one character
697
+ this._moveCursor(-1);
698
+ break;
699
+
700
+ case 'f': // forward one character
701
+ this._moveCursor(+1);
702
+ break;
703
+
704
+ case 'l': // clear the whole screen
705
+ exports.cursorTo(this.output, 0, 0);
706
+ exports.clearScreenDown(this.output);
707
+ this._refreshLine();
708
+ break;
709
+
710
+ case 'n': // next history item
711
+ this._historyNext();
712
+ break;
713
+
714
+ case 'p': // previous history item
715
+ this._historyPrev();
716
+ break;
717
+
718
+ case 'z':
719
+ if (process.platform == 'win32') break;
720
+ if (EventEmitter.listenerCount(this, 'SIGTSTP') > 0) {
721
+ this.emit('SIGTSTP');
722
+ } else {
723
+ process.once('SIGCONT', (function(self) {
724
+ return function() {
725
+ // Don't raise events if stream has already been abandoned.
726
+ if (!self.paused) {
727
+ // Stream must be paused and resumed after SIGCONT to catch
728
+ // SIGINT, SIGTSTP, and EOF.
729
+ self.pause();
730
+ self.emit('SIGCONT');
731
+ }
732
+ // explicitly re-enable "raw mode" and move the cursor to
733
+ // the correct position.
734
+ // See https://github.com/joyent/node/issues/3295.
735
+ self._setRawMode(true);
736
+ self._refreshLine();
737
+ };
738
+ })(this));
739
+ this._setRawMode(false);
740
+ process.kill(process.pid, 'SIGTSTP');
741
+ }
742
+ break;
743
+
744
+ case 'w': // delete backwards to a word boundary
745
+ case 'backspace':
746
+ this._deleteWordLeft();
747
+ break;
748
+
749
+ case 'delete': // delete forward to a word boundary
750
+ this._deleteWordRight();
751
+ break;
752
+
753
+ case 'backspace':
754
+ this._deleteWordLeft();
755
+ break;
756
+
757
+ case 'left':
758
+ this._wordLeft();
759
+ break;
760
+
761
+ case 'right':
762
+ this._wordRight();
763
+ break;
764
+ }
765
+
766
+ } else if (key.meta) {
767
+ /* Meta key pressed */
768
+
769
+ switch (key.name) {
770
+ case 'b': // backward word
771
+ this._wordLeft();
772
+ break;
773
+
774
+ case 'f': // forward word
775
+ this._wordRight();
776
+ break;
777
+
778
+ case 'd': // delete forward word
779
+ case 'delete':
780
+ this._deleteWordRight();
781
+ break;
782
+
783
+ case 'backspace': // delete backwards to a word boundary
784
+ this._deleteWordLeft();
785
+ break;
786
+ }
787
+
788
+ } else {
789
+ /* No modifier keys used */
790
+
791
+ // \r bookkeeping is only relevant if a \n comes right after.
792
+ if (this._sawReturn && key.name !== 'enter')
793
+ this._sawReturn = false;
794
+
795
+ switch (key.name) {
796
+ case 'return': // carriage return, i.e. \r
797
+ this._sawReturn = true;
798
+ this._line();
799
+ break;
800
+
801
+ case 'enter':
802
+ if (this._sawReturn)
803
+ this._sawReturn = false;
804
+ else
805
+ this._line();
806
+ break;
807
+
808
+ case 'backspace':
809
+ this._deleteLeft();
810
+ break;
811
+
812
+ case 'delete':
813
+ this._deleteRight();
814
+ break;
815
+
816
+ case 'tab': // tab completion
817
+ this._tabComplete();
818
+ break;
819
+
820
+ case 'left':
821
+ this._moveCursor(-1);
822
+ break;
823
+
824
+ case 'right':
825
+ this._moveCursor(+1);
826
+ break;
827
+
828
+ case 'home':
829
+ this._moveCursor(-Infinity);
830
+ break;
831
+
832
+ case 'end':
833
+ this._moveCursor(+Infinity);
834
+ break;
835
+
836
+ case 'up':
837
+ this._historyPrev();
838
+ break;
839
+
840
+ case 'down':
841
+ this._historyNext();
842
+ break;
843
+
844
+ default:
845
+ if (util.isBuffer(s))
846
+ s = s.toString('utf-8');
847
+
848
+ if (s) {
849
+ var lines = s.split(/\r\n|\n|\r/);
850
+ for (var i = 0, len = lines.length; i < len; i++) {
851
+ if (i > 0) {
852
+ this._line();
853
+ }
854
+ this._insertString(lines[i]);
855
+ }
856
+ }
857
+ }
858
+ }
859
+ };
860
+
861
+
862
+ exports.Interface = Interface;
863
+
864
+
865
+
866
+ /**
867
+ * accepts a readable Stream instance and makes it emit "keypress" events
868
+ */
869
+
870
+ function emitKeypressEvents(stream) {
871
+ if (stream._keypressDecoder) return;
872
+ var StringDecoder = require('string_decoder').StringDecoder; // lazy load
873
+ stream._keypressDecoder = new StringDecoder('utf8');
874
+
875
+ function onData(b) {
876
+ if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
877
+ var r = stream._keypressDecoder.write(b);
878
+ if (r) emitKey(stream, r);
879
+ } else {
880
+ // Nobody's watching anyway
881
+ stream.removeListener('data', onData);
882
+ stream.on('newListener', onNewListener);
883
+ }
884
+ }
885
+
886
+ function onNewListener(event) {
887
+ if (event == 'keypress') {
888
+ stream.on('data', onData);
889
+ stream.removeListener('newListener', onNewListener);
890
+ }
891
+ }
892
+
893
+ if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
894
+ stream.on('data', onData);
895
+ } else {
896
+ stream.on('newListener', onNewListener);
897
+ }
898
+ }
899
+ exports.emitKeypressEvents = emitKeypressEvents;
900
+
901
+ /*
902
+ Some patterns seen in terminal key escape codes, derived from combos seen
903
+ at http://www.midnight-commander.org/browser/lib/tty/key.c
904
+
905
+ ESC letter
906
+ ESC [ letter
907
+ ESC [ modifier letter
908
+ ESC [ 1 ; modifier letter
909
+ ESC [ num char
910
+ ESC [ num ; modifier char
911
+ ESC O letter
912
+ ESC O modifier letter
913
+ ESC O 1 ; modifier letter
914
+ ESC N letter
915
+ ESC [ [ num ; modifier char
916
+ ESC [ [ 1 ; modifier letter
917
+ ESC ESC [ num char
918
+ ESC ESC O letter
919
+
920
+ - char is usually ~ but $ and ^ also happen with rxvt
921
+ - modifier is 1 +
922
+ (shift * 1) +
923
+ (left_alt * 2) +
924
+ (ctrl * 4) +
925
+ (right_alt * 8)
926
+ - two leading ESCs apparently mean the same as one leading ESC
927
+ */
928
+
929
+ // Regexes used for ansi escape code splitting
930
+ var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
931
+ var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
932
+ var functionKeyCodeReAnywhere =
933
+ /(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
934
+ var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
935
+
936
+ function emitKey(stream, s) {
937
+ var ch,
938
+ key = {
939
+ name: undefined,
940
+ ctrl: false,
941
+ meta: false,
942
+ shift: false
943
+ },
944
+ parts;
945
+
946
+ if (util.isBuffer(s)) {
947
+ if (s[0] > 127 && util.isUndefined(s[1])) {
948
+ s[0] -= 128;
949
+ s = '\x1b' + s.toString(stream.encoding || 'utf-8');
950
+ } else {
951
+ s = s.toString(stream.encoding || 'utf-8');
952
+ }
953
+ }
954
+
955
+ key.sequence = s;
956
+
957
+ if (s === '\r') {
958
+ // carriage return
959
+ key.name = 'return';
960
+
961
+ } else if (s === '\n') {
962
+ // enter, should have been called linefeed
963
+ key.name = 'enter';
964
+
965
+ } else if (s === '\t') {
966
+ // tab
967
+ key.name = 'tab';
968
+
969
+ } else if (s === '\b' || s === '\x7f' ||
970
+ s === '\x1b\x7f' || s === '\x1b\b') {
971
+ // backspace or ctrl+h
972
+ key.name = 'backspace';
973
+ key.meta = (s.charAt(0) === '\x1b');
974
+
975
+ } else if (s === '\x1b' || s === '\x1b\x1b') {
976
+ // escape key
977
+ key.name = 'escape';
978
+ key.meta = (s.length === 2);
979
+
980
+ } else if (s === ' ' || s === '\x1b ') {
981
+ key.name = 'space';
982
+ key.meta = (s.length === 2);
983
+
984
+ } else if (s.length === 1 && s <= '\x1a') {
985
+ // ctrl+letter
986
+ key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
987
+ key.ctrl = true;
988
+
989
+ } else if (s.length === 1 && s >= 'a' && s <= 'z') {
990
+ // lowercase letter
991
+ key.name = s;
992
+
993
+ } else if (s.length === 1 && s >= 'A' && s <= 'Z') {
994
+ // shift+letter
995
+ key.name = s.toLowerCase();
996
+ key.shift = true;
997
+
998
+ } else if (parts = metaKeyCodeRe.exec(s)) {
999
+ // meta+character key
1000
+ key.name = parts[1].toLowerCase();
1001
+ key.meta = true;
1002
+ key.shift = /^[A-Z]$/.test(parts[1]);
1003
+
1004
+ } else if (parts = functionKeyCodeRe.exec(s)) {
1005
+ // ansi escape sequence
1006
+
1007
+ // reassemble the key code leaving out leading \x1b's,
1008
+ // the modifier key bitflag and any meaningless "1;" sequence
1009
+ var code = (parts[1] || '') + (parts[2] || '') +
1010
+ (parts[4] || '') + (parts[6] || ''),
1011
+ modifier = (parts[3] || parts[5] || 1) - 1;
1012
+
1013
+ // Parse the key modifier
1014
+ key.ctrl = !!(modifier & 4);
1015
+ key.meta = !!(modifier & 10);
1016
+ key.shift = !!(modifier & 1);
1017
+ key.code = code;
1018
+
1019
+ // Parse the key itself
1020
+ switch (code) {
1021
+ /* xterm/gnome ESC O letter */
1022
+ case 'OP': key.name = 'f1'; break;
1023
+ case 'OQ': key.name = 'f2'; break;
1024
+ case 'OR': key.name = 'f3'; break;
1025
+ case 'OS': key.name = 'f4'; break;
1026
+
1027
+ /* xterm/rxvt ESC [ number ~ */
1028
+ case '[11~': key.name = 'f1'; break;
1029
+ case '[12~': key.name = 'f2'; break;
1030
+ case '[13~': key.name = 'f3'; break;
1031
+ case '[14~': key.name = 'f4'; break;
1032
+
1033
+ /* from Cygwin and used in libuv */
1034
+ case '[[A': key.name = 'f1'; break;
1035
+ case '[[B': key.name = 'f2'; break;
1036
+ case '[[C': key.name = 'f3'; break;
1037
+ case '[[D': key.name = 'f4'; break;
1038
+ case '[[E': key.name = 'f5'; break;
1039
+
1040
+ /* common */
1041
+ case '[15~': key.name = 'f5'; break;
1042
+ case '[17~': key.name = 'f6'; break;
1043
+ case '[18~': key.name = 'f7'; break;
1044
+ case '[19~': key.name = 'f8'; break;
1045
+ case '[20~': key.name = 'f9'; break;
1046
+ case '[21~': key.name = 'f10'; break;
1047
+ case '[23~': key.name = 'f11'; break;
1048
+ case '[24~': key.name = 'f12'; break;
1049
+
1050
+ /* xterm ESC [ letter */
1051
+ case '[A': key.name = 'up'; break;
1052
+ case '[B': key.name = 'down'; break;
1053
+ case '[C': key.name = 'right'; break;
1054
+ case '[D': key.name = 'left'; break;
1055
+ case '[E': key.name = 'clear'; break;
1056
+ case '[F': key.name = 'end'; break;
1057
+ case '[H': key.name = 'home'; break;
1058
+
1059
+ /* xterm/gnome ESC O letter */
1060
+ case 'OA': key.name = 'up'; break;
1061
+ case 'OB': key.name = 'down'; break;
1062
+ case 'OC': key.name = 'right'; break;
1063
+ case 'OD': key.name = 'left'; break;
1064
+ case 'OE': key.name = 'clear'; break;
1065
+ case 'OF': key.name = 'end'; break;
1066
+ case 'OH': key.name = 'home'; break;
1067
+
1068
+ /* xterm/rxvt ESC [ number ~ */
1069
+ case '[1~': key.name = 'home'; break;
1070
+ case '[2~': key.name = 'insert'; break;
1071
+ case '[3~': key.name = 'delete'; break;
1072
+ case '[4~': key.name = 'end'; break;
1073
+ case '[5~': key.name = 'pageup'; break;
1074
+ case '[6~': key.name = 'pagedown'; break;
1075
+
1076
+ /* putty */
1077
+ case '[[5~': key.name = 'pageup'; break;
1078
+ case '[[6~': key.name = 'pagedown'; break;
1079
+
1080
+ /* rxvt */
1081
+ case '[7~': key.name = 'home'; break;
1082
+ case '[8~': key.name = 'end'; break;
1083
+
1084
+ /* rxvt keys with modifiers */
1085
+ case '[a': key.name = 'up'; key.shift = true; break;
1086
+ case '[b': key.name = 'down'; key.shift = true; break;
1087
+ case '[c': key.name = 'right'; key.shift = true; break;
1088
+ case '[d': key.name = 'left'; key.shift = true; break;
1089
+ case '[e': key.name = 'clear'; key.shift = true; break;
1090
+
1091
+ case '[2$': key.name = 'insert'; key.shift = true; break;
1092
+ case '[3$': key.name = 'delete'; key.shift = true; break;
1093
+ case '[5$': key.name = 'pageup'; key.shift = true; break;
1094
+ case '[6$': key.name = 'pagedown'; key.shift = true; break;
1095
+ case '[7$': key.name = 'home'; key.shift = true; break;
1096
+ case '[8$': key.name = 'end'; key.shift = true; break;
1097
+
1098
+ case 'Oa': key.name = 'up'; key.ctrl = true; break;
1099
+ case 'Ob': key.name = 'down'; key.ctrl = true; break;
1100
+ case 'Oc': key.name = 'right'; key.ctrl = true; break;
1101
+ case 'Od': key.name = 'left'; key.ctrl = true; break;
1102
+ case 'Oe': key.name = 'clear'; key.ctrl = true; break;
1103
+
1104
+ case '[2^': key.name = 'insert'; key.ctrl = true; break;
1105
+ case '[3^': key.name = 'delete'; key.ctrl = true; break;
1106
+ case '[5^': key.name = 'pageup'; key.ctrl = true; break;
1107
+ case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
1108
+ case '[7^': key.name = 'home'; key.ctrl = true; break;
1109
+ case '[8^': key.name = 'end'; key.ctrl = true; break;
1110
+
1111
+ /* misc. */
1112
+ case '[Z': key.name = 'tab'; key.shift = true; break;
1113
+ default: key.name = 'undefined'; break;
1114
+
1115
+ }
1116
+ } else if (s.length > 1 && s[0] !== '\x1b') {
1117
+ // Got a longer-than-one string of characters.
1118
+ // Probably a paste, since it wasn't a control sequence.
1119
+ Array.prototype.forEach.call(s, function(c) {
1120
+ emitKey(stream, c);
1121
+ });
1122
+ return;
1123
+ }
1124
+
1125
+ // Don't emit a key if no name was found
1126
+ if (util.isUndefined(key.name)) {
1127
+ key = undefined;
1128
+ }
1129
+
1130
+ if (s.length === 1) {
1131
+ ch = s;
1132
+ }
1133
+
1134
+ if (key || ch) {
1135
+ stream.emit('keypress', ch, key);
1136
+ }
1137
+ }
1138
+
1139
+
1140
+ /**
1141
+ * moves the cursor to the x and y coordinate on the given stream
1142
+ */
1143
+
1144
+ function cursorTo(stream, x, y) {
1145
+ if (!util.isNumber(x) && !util.isNumber(y))
1146
+ return;
1147
+
1148
+ if (!util.isNumber(x))
1149
+ throw new Error("Can't set cursor row without also setting it's column");
1150
+
1151
+ if (!util.isNumber(y)) {
1152
+ stream.write('\x1b[' + (x + 1) + 'G');
1153
+ } else {
1154
+ stream.write('\x1b[' + (y + 1) + ';' + (x + 1) + 'H');
1155
+ }
1156
+ }
1157
+ exports.cursorTo = cursorTo;
1158
+
1159
+
1160
+ /**
1161
+ * moves the cursor relative to its current location
1162
+ */
1163
+
1164
+ function moveCursor(stream, dx, dy) {
1165
+ if (dx < 0) {
1166
+ stream.write('\x1b[' + (-dx) + 'D');
1167
+ } else if (dx > 0) {
1168
+ stream.write('\x1b[' + dx + 'C');
1169
+ }
1170
+
1171
+ if (dy < 0) {
1172
+ stream.write('\x1b[' + (-dy) + 'A');
1173
+ } else if (dy > 0) {
1174
+ stream.write('\x1b[' + dy + 'B');
1175
+ }
1176
+ }
1177
+ exports.moveCursor = moveCursor;
1178
+
1179
+
1180
+ /**
1181
+ * clears the current line the cursor is on:
1182
+ * -1 for left of the cursor
1183
+ * +1 for right of the cursor
1184
+ * 0 for the entire line
1185
+ */
1186
+
1187
+ function clearLine(stream, dir) {
1188
+ if (dir < 0) {
1189
+ // to the beginning
1190
+ stream.write('\x1b[1K');
1191
+ } else if (dir > 0) {
1192
+ // to the end
1193
+ stream.write('\x1b[0K');
1194
+ } else {
1195
+ // entire line
1196
+ stream.write('\x1b[2K');
1197
+ }
1198
+ }
1199
+ exports.clearLine = clearLine;
1200
+
1201
+
1202
+ /**
1203
+ * clears the screen from the current position of the cursor down
1204
+ */
1205
+
1206
+ function clearScreenDown(stream) {
1207
+ stream.write('\x1b[0J');
1208
+ }
1209
+ exports.clearScreenDown = clearScreenDown;
1210
+
1211
+
1212
+ /**
1213
+ * Returns the number of columns required to display the given string.
1214
+ */
1215
+
1216
+ function getStringWidth(str) {
1217
+ var width = 0;
1218
+ str = stripVTControlCharacters(str);
1219
+ for (var i = 0, len = str.length; i < len; i++) {
1220
+ var code = codePointAt(str, i);
1221
+ if (code >= 0x10000) { // surrogates
1222
+ i++;
1223
+ }
1224
+ if (isFullWidthCodePoint(code)) {
1225
+ width += 2;
1226
+ } else {
1227
+ width++;
1228
+ }
1229
+ }
1230
+ return width;
1231
+ }
1232
+ exports.getStringWidth = getStringWidth;
1233
+
1234
+
1235
+ /**
1236
+ * Returns true if the character represented by a given
1237
+ * Unicode code point is full-width. Otherwise returns false.
1238
+ */
1239
+
1240
+ function isFullWidthCodePoint(code) {
1241
+ if (isNaN(code)) {
1242
+ return false;
1243
+ }
1244
+
1245
+ // Code points are derived from:
1246
+ // http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
1247
+ if (code >= 0x1100 && (
1248
+ code <= 0x115f || // Hangul Jamo
1249
+ 0x2329 === code || // LEFT-POINTING ANGLE BRACKET
1250
+ 0x232a === code || // RIGHT-POINTING ANGLE BRACKET
1251
+ // CJK Radicals Supplement .. Enclosed CJK Letters and Months
1252
+ (0x2e80 <= code && code <= 0x3247 && code !== 0x303f) ||
1253
+ // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
1254
+ 0x3250 <= code && code <= 0x4dbf ||
1255
+ // CJK Unified Ideographs .. Yi Radicals
1256
+ 0x4e00 <= code && code <= 0xa4c6 ||
1257
+ // Hangul Jamo Extended-A
1258
+ 0xa960 <= code && code <= 0xa97c ||
1259
+ // Hangul Syllables
1260
+ 0xac00 <= code && code <= 0xd7a3 ||
1261
+ // CJK Compatibility Ideographs
1262
+ 0xf900 <= code && code <= 0xfaff ||
1263
+ // Vertical Forms
1264
+ 0xfe10 <= code && code <= 0xfe19 ||
1265
+ // CJK Compatibility Forms .. Small Form Variants
1266
+ 0xfe30 <= code && code <= 0xfe6b ||
1267
+ // Halfwidth and Fullwidth Forms
1268
+ 0xff01 <= code && code <= 0xff60 ||
1269
+ 0xffe0 <= code && code <= 0xffe6 ||
1270
+ // Kana Supplement
1271
+ 0x1b000 <= code && code <= 0x1b001 ||
1272
+ // Enclosed Ideographic Supplement
1273
+ 0x1f200 <= code && code <= 0x1f251 ||
1274
+ // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
1275
+ 0x20000 <= code && code <= 0x3fffd)) {
1276
+ return true;
1277
+ }
1278
+ return false;
1279
+ }
1280
+ exports.isFullWidthCodePoint = isFullWidthCodePoint;
1281
+
1282
+
1283
+ /**
1284
+ * Returns the Unicode code point for the character at the
1285
+ * given index in the given string. Similar to String.charCodeAt(),
1286
+ * but this function handles surrogates (code point >= 0x10000).
1287
+ */
1288
+
1289
+ function codePointAt(str, index) {
1290
+ var code = str.charCodeAt(index);
1291
+ var low;
1292
+ if (0xd800 <= code && code <= 0xdbff) { // High surrogate
1293
+ low = str.charCodeAt(index + 1);
1294
+ if (!isNaN(low)) {
1295
+ code = 0x10000 + (code - 0xd800) * 0x400 + (low - 0xdc00);
1296
+ }
1297
+ }
1298
+ return code;
1299
+ }
1300
+ exports.codePointAt = codePointAt;
1301
+
1302
+
1303
+ /**
1304
+ * Tries to remove all VT control characters. Use to estimate displayed
1305
+ * string width. May be buggy due to not running a real state machine
1306
+ */
1307
+ function stripVTControlCharacters(str) {
1308
+ str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
1309
+ return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
1310
+ }
1311
+ exports.stripVTControlCharacters = stripVTControlCharacters;