macgyver 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;