Tizen 2.0 Release
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / grunt / node_modules / prompt / lib / prompt.js
1 /*
2  * prompt.js: Simple prompt for prompting information from the command line
3  *
4  * (C) 2010, Nodejitsu Inc.
5  *
6  */
7
8 var events = require('events'),
9     async = require('async'),
10     colors = require('colors'),
11     winston = require('winston'),
12     tty = require('tty');
13
14 //
15 // ### @private function capitalize (str)
16 // #### str {string} String to capitalize
17 // Capitalizes the string supplied.
18 //
19 function capitalize(str) {
20   return str.charAt(0).toUpperCase() + str.slice(1);
21 }
22
23 //
24 // Expose version using `pkginfo`
25 //
26 require('pkginfo')(module, 'version');
27
28 var stdin, stdout, history = [];
29 var prompt = module.exports = Object.create(events.EventEmitter.prototype);
30 var logger = prompt.logger = new winston.Logger({
31   transports: [new (winston.transports.Console)()]
32 });
33
34 prompt.started    = false;
35 prompt.paused     = false;
36 prompt.allowEmpty = false;
37 prompt.message    = 'prompt';
38 prompt.delimiter  = ': ';
39
40 //
41 // Create an empty object for the properties
42 // known to `prompt`
43 //
44 prompt.properties = {};
45
46 //
47 // Setup the default winston logger to use
48 // the `cli` levels and colors.
49 //
50 logger.cli();
51
52 //
53 // ### function start (options)
54 // #### @options {Object} **Optional** Options to consume by prompt
55 // Starts the prompt by listening to the appropriate events on `options.stdin`
56 // and `options.stdout`. If no streams are supplied, then `process.stdin`
57 // and `process.stdout` are used, respectively.
58 //
59 prompt.start = function (options) {
60   if (prompt.started) {
61     return;
62   }
63
64   options = options        || {};
65   stdin   = options.stdin  || process.openStdin();
66   stdout  = options.stdout || process.stdout;
67
68   //
69   // By default: Remeber the last `10` prompt property /
70   // answer pairs and don't allow empty responses globally.
71   //
72   prompt.memory     = options.memory     || 10;
73   prompt.allowEmpty = options.allowEmpty || false;
74   prompt.message    = options.message    || prompt.message;
75   prompt.delimiter  = options.delimiter  || prompt.delimiter;
76
77   if (process.platform !== 'win32') {
78     // windows falls apart trying to deal with SIGINT
79     process.on('SIGINT', function () {
80       stdout.write('\n');
81       process.exit(1);
82     });   
83   }
84
85   prompt.emit('start');
86   prompt.started = true;
87   return prompt;
88 };
89
90 //
91 // ### function pause ()
92 // Pauses input coming in from stdin
93 //
94 prompt.pause = function () {
95   if (!prompt.started || prompt.paused) {
96     return;
97   }
98
99   stdin.pause();
100   prompt.emit('pause');
101   prompt.paused = true;
102   return prompt;
103 };
104
105 //
106 // ### function resume ()
107 // Resumes input coming in from stdin
108 //
109 prompt.resume = function () {
110   if (!prompt.started || !prompt.paused) {
111     return;
112   }
113
114   stdin.resume();
115   prompt.emit('resume');
116   prompt.paused = false;
117   return prompt;
118 };
119
120 //
121 // ### function history (search)
122 // #### @search {Number|string} Index or property name to find.
123 // Returns the `property:value` pair from within the prompts
124 // `history` array.
125 //
126 prompt.history = function (search) {
127   if (typeof search === 'number') {
128     return history[search] || {};
129   }
130
131   var names = history.map(function (pair) {
132     return typeof pair.property === 'string'
133       ? pair.property
134       : pair.property.name;
135   });
136
137   if (~names.indexOf(search)) {
138     return null;
139   }
140
141   return history.filter(function (name) {
142     return typeof pair.property === 'string'
143       ? pair.property === name
144       : pair.property.name === name;
145   })[0];
146 };
147
148 //
149 // ### function get (msg, [validator,] callback)
150 // #### @msg {Array|Object|string} Set of variables to get input for.
151 // #### @callback {function} Continuation to pass control to when complete.
152 // Gets input from the user via stdin for the specified message(s) `msg`.
153 //
154 prompt.get = function (msg, callback) {
155   var vars = !Array.isArray(msg) ? [msg] : msg,
156       result = {};
157
158   vars = vars.map(function (v) {
159     if (typeof v === 'string') {
160       v = v.toLowerCase();
161     }
162
163     return prompt.properties[v] || v;
164   });
165
166   function get(target, next) {
167     prompt.getInput(target, function (err, line) {
168       if (err) {
169         return next(err);
170       }
171
172       var name = target.name || target;
173       result[name] = line;
174       next();
175     });
176   }
177
178   async.forEachSeries(vars, get, function (err) {
179     return err ? callback(err) : callback(null, result);
180   });
181
182   return prompt;
183 };
184
185 //
186 // ### function getInput (msg, validator, callback)
187 // #### @msg {Object|string} Variable to get input for.
188 // #### @callback {function} Continuation to pass control to when complete.
189 // Gets input from the user via stdin for the specified message `msg`.
190 //
191 prompt.getInput = function (prop, callback) {
192   var name   = prop.message || prop.name || prop,
193       propName = prop.name || prop,
194       delim  = prompt.delimiter,
195       raw    = [prompt.message, delim + name.grey, delim.grey],
196       read   = prop.hidden ? prompt.readLineHidden : prompt.readLine,
197       length, msg;
198
199   if (prompt.override && prompt.override[propName]) {
200     return callback (null, prompt.override[propName])
201   }
202
203   if (prop.default) {
204     raw.splice(2, -1, ' (' + prop.default + ')');
205   }
206
207   // Calculate the raw length and colorize the prompt
208   length = raw.join('').length;
209   raw[0] = raw[0];
210   msg = raw.join('');
211
212   if (prop.help) {
213     prop.help.forEach(function (line) {
214       logger.help(line);
215     });
216   }
217
218   stdout.write(msg);
219   prompt.emit('prompt', prop);
220
221   read.call(null, function (err, line) {
222     if (err) {
223       return callback(err);
224     }
225
226     if (!line || line === '') {
227       line = prop.default || line;
228     }
229
230     if (!prop.validator && prop.empty !== false) {
231       logger.input(line.yellow);
232       return callback(null, line);
233     }
234
235     var valid = true,
236         validator = prop.validator;
237
238     function next(valid) {
239       if (arguments.length < 1) {
240         valid = true;
241       }
242
243       if (prop.empty === false && valid) {
244         valid = line.length > 0;
245         if (!valid) {
246           prop.warning = prop.warning || 'You must supply a value.';
247         }
248       }
249
250       if (!valid) {
251         logger.error('Invalid input for ' + name.grey);
252         if (prop.warning) {
253           logger.error(prop.warning);
254         }
255
256         prompt.emit('invalid', prop, line);
257         return prompt.getInput(prop, callback);
258       }
259
260       //
261       // Log the resulting line, append this `property:value`
262       // pair to the history for `prompt` and respond to
263       // the callback.
264       //
265       logger.input(line.yellow);
266       prompt._remember(prop, line);
267       callback(null, line);
268     }
269
270     if (validator) {
271       if (validator.test) {
272         valid = validator.test(line)
273       }
274       else if (typeof validator === 'function') {
275         return validator.length < 2
276           ? next(validator(line))
277           : validator(line, next);
278       }
279       else {
280         return callback(new Error('Invalid valiator: ' + typeof validator));
281       }
282     }
283
284     next(valid);
285   });
286
287   return prompt;
288 };
289
290 //
291 // ### function addProperties (obj, properties, callback)
292 // #### @obj {Object} Object to add properties to
293 // #### @properties {Array} List of properties to get values for
294 // #### @callback {function} Continuation to pass control to when complete.
295 // Prompts the user for values each of the `properties` if `obj` does not already
296 // have a value for the property. Responds with the modified object.
297 //
298 prompt.addProperties = function (obj, properties, callback) {
299   properties = properties.filter(function (prop) {
300     return typeof obj[prop] === 'undefined';
301   });
302
303   if (properties.length === 0) {
304     return callback(obj);
305   }
306
307   prompt.get(properties, function (err, results) {
308     if (err) {
309       return callback(err);
310     }
311     else if (!results) {
312       return callback(null, obj);
313     }
314
315     function putNested (obj, path, value) {
316       var last = obj, key;
317
318       while (path.length > 1) {
319         key = path.shift();
320         if (!last[key]) {
321           last[key] = {};
322         }
323
324         last = last[key];
325       }
326
327       last[path.shift()] = value;
328     }
329
330     Object.keys(results).forEach(function (key) {
331       putNested(obj, key.split('.'), results[key]);
332     });
333
334     callback(null, obj);
335   });
336
337   return prompt;
338 };
339
340 //
341 // ### function readLine (callback)
342 // #### @callback {function} Continuation to respond to when complete
343 // Gets a single line of input from the user.
344 //
345 prompt.readLine = function (callback) {
346   var value = '', buffer = '';
347   prompt.resume();
348   stdin.setEncoding('utf8');
349   stdin.on('error', callback);
350   stdin.on('data', function data (chunk) {
351     value += buffer + chunk;
352     buffer = '';
353     value = value.replace(/\r/g, '');
354     if (value.indexOf('\n') !== -1) {
355       if (value !== '\n') {
356         value = value.replace(/^\n+/, '');
357       }
358
359       buffer = value.substr(value.indexOf('\n'));
360       value = value.substr(0, value.indexOf('\n'));
361       prompt.pause();
362       stdin.removeListener('data', data);
363       stdin.removeListener('error', callback);
364       value = value.trim();
365       callback(null, value);
366     }
367   });
368
369   return prompt;
370 };
371
372 //
373 // ### function readLineHidden (callback)
374 // #### @callback {function} Continuation to respond to when complete
375 // Gets a single line of hidden input (i.e. `rawMode = true`) from the user.
376 //
377 prompt.readLineHidden = function (callback) {
378   var value = '';
379   
380   //
381   // Ignore errors from `.setRawMode()` so that `prompt` can
382   // be scripted in child processes.
383   //
384   try { tty.setRawMode(true) }
385   catch (ex) { }
386   
387   prompt.resume();
388   stdin.on('error', callback);
389   stdin.on('data', function data (line) {
390     line = line + '';
391     for(var i = 0; i < line.length; i++) {
392       c = line[i];
393       switch (c) {
394         case '\n': case '\r': case '\r\n': case '\u0004':
395           try { tty.setRawMode(false) }
396           catch (ex) { }
397           stdin.removeListener('data', data);
398           stdin.removeListener('error', callback);
399           value = value.trim();
400           stdout.write('\n');
401           stdout.flush && stdout.flush();
402           prompt.pause();
403           return callback(null, value);
404         case '\x7f': case'\x08':
405           value = value.slice(0,-1);
406           break;
407         case '\u0003': case '\0':
408           stdout.write('\n');
409           process.exit(1);
410           break;
411         default:
412           value = value + c;
413           break;
414       }
415     }
416   });
417
418   return prompt;
419 };
420
421 //
422 // ### @private function _remember (property, value)
423 // #### @property {Object|string} Property that the value is in response to.
424 // #### @value {string} User input captured by `prompt`.
425 // Prepends the `property:value` pair into the private `history` Array
426 // for `prompt` so that it can be accessed later.
427 //
428 prompt._remember = function (property, value) {
429   history.unshift({
430     property: property,
431     value: value
432   });
433
434   //
435   // If the length of the `history` Array
436   // has exceeded the specified length to remember,
437   // `prompt.memory`, truncate it.
438   //
439   if (history.length > prompt.memory) {
440     history.splice(prompt.memory, history.length - prompt.memory);
441   }
442 };